| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| // Date: Thu Oct 28 15:27:05 2010 |
| |
| #include <fcntl.h> // open |
| #include <unistd.h> // close |
| #include <stdio.h> // snprintf, vdprintf |
| #include <stdlib.h> // mkstemp |
| #include <string.h> // strlen |
| #include <stdarg.h> // va_list |
| #include <errno.h> // errno |
| #include <new> // placement new |
| #include "temp_file.h" // TempFile |
| |
| // Initializing array. Needs to be macro. |
| #define BASE_FILES_TEMP_FILE_PATTERN "temp_file_XXXXXX"; |
| |
| namespace butil { |
| |
| TempFile::TempFile() : _ever_opened(0) { |
| char temp_name[] = BASE_FILES_TEMP_FILE_PATTERN; |
| _fd = mkstemp(temp_name); |
| if (_fd >= 0) { |
| _ever_opened = 1; |
| snprintf(_fname, sizeof(_fname), "%s", temp_name); |
| } else { |
| *_fname = '\0'; |
| } |
| |
| } |
| |
| TempFile::TempFile(const char* ext) { |
| if (NULL == ext || '\0' == *ext) { |
| new (this) TempFile(); |
| return; |
| } |
| |
| *_fname = '\0'; |
| _fd = -1; |
| _ever_opened = 0; |
| |
| // Make a temp file to occupy the filename without ext. |
| char temp_name[] = BASE_FILES_TEMP_FILE_PATTERN; |
| const int tmp_fd = mkstemp(temp_name); |
| if (tmp_fd < 0) { |
| return; |
| } |
| |
| // Open the temp_file_XXXXXX.ext |
| snprintf(_fname, sizeof(_fname), "%s.%s", temp_name, ext); |
| |
| _fd = open(_fname, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0600); |
| if (_fd < 0) { |
| *_fname = '\0'; |
| } else { |
| _ever_opened = 1; |
| } |
| |
| // Close and remove temp_file_XXXXXX anyway. |
| close(tmp_fd); |
| unlink(temp_name); |
| } |
| |
| int TempFile::_reopen_if_necessary() { |
| if (_fd < 0) { |
| _fd = open(_fname, O_CREAT | O_WRONLY | O_TRUNC, 0600); |
| } |
| return _fd; |
| } |
| |
| TempFile::~TempFile() { |
| if (_fd >= 0) { |
| close(_fd); |
| _fd = -1; |
| } |
| if (_ever_opened) { |
| // Only remove temp file when _fd >= 0, otherwise we may |
| // remove another temporary file (occupied the same name) |
| unlink(_fname); |
| } |
| } |
| |
| int TempFile::save(const char *content) { |
| return save_bin(content, strlen(content)); |
| } |
| |
| int TempFile::save_format(const char *fmt, ...) { |
| if (_reopen_if_necessary() < 0) { |
| return -1; |
| } |
| va_list ap; |
| va_start(ap, fmt); |
| const int rc = vdprintf(_fd, fmt, ap); |
| va_end(ap); |
| |
| close(_fd); |
| _fd = -1; |
| // TODO: is this right? |
| return (rc < 0 ? -1 : 0); |
| } |
| |
| // Write until all buffer was written or an error except EINTR. |
| // Returns: |
| // -1 error happened, errno is set |
| // count all written |
| static ssize_t temp_file_write_all(int fd, const void *buf, size_t count) { |
| size_t off = 0; |
| for (;;) { |
| ssize_t nw = write(fd, (char*)buf + off, count - off); |
| if (nw == (ssize_t)(count - off)) { // including count==0 |
| return count; |
| } |
| if (nw >= 0) { |
| off += nw; |
| } else if (errno != EINTR) { |
| return -1; |
| } |
| } |
| } |
| |
| int TempFile::save_bin(const void *buf, size_t count) { |
| if (_reopen_if_necessary() < 0) { |
| return -1; |
| } |
| |
| const ssize_t len = temp_file_write_all(_fd, buf, count); |
| |
| close(_fd); |
| _fd = -1; |
| if (len < 0) { |
| return -1; |
| } else if ((size_t)len != count) { |
| errno = ENOSPC; |
| return -1; |
| } |
| return 0; |
| } |
| |
| } // namespace butil |