// Copyright (c) 2016 Google Inc. // // Licensed 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. #ifndef TOOLS_IO_H_ #define TOOLS_IO_H_ #include #include #include #include #if defined(SPIRV_WINDOWS) #include #include #define SET_STDIN_TO_BINARY_MODE() _setmode(_fileno(stdin), O_BINARY); #define SET_STDIN_TO_TEXT_MODE() _setmode(_fileno(stdin), O_TEXT); #define SET_STDOUT_TO_BINARY_MODE() _setmode(_fileno(stdout), O_BINARY); #define SET_STDOUT_TO_TEXT_MODE() _setmode(_fileno(stdout), O_TEXT); #define SET_STDOUT_MODE(mode) _setmode(_fileno(stdout), mode); #else #define SET_STDIN_TO_BINARY_MODE() #define SET_STDIN_TO_TEXT_MODE() #define SET_STDOUT_TO_BINARY_MODE() 0 #define SET_STDOUT_TO_TEXT_MODE() 0 #define SET_STDOUT_MODE(mode) #endif // Appends the contents of the |file| to |data|, assuming each element in the // file is of type |T|. template void ReadFile(FILE* file, std::vector* data) { if (file == nullptr) return; const int buf_size = 1024; T buf[buf_size]; while (size_t len = fread(buf, sizeof(T), buf_size, file)) { data->insert(data->end(), buf, buf + len); } } // Returns true if |file| has encountered an error opening the file or reading // the file as a series of element of type |T|. If there was an error, writes an // error message to standard error. template bool WasFileCorrectlyRead(FILE* file, const char* filename) { if (file == nullptr) { fprintf(stderr, "error: file does not exist '%s'\n", filename); return false; } if (ftell(file) == -1L) { if (ferror(file)) { fprintf(stderr, "error: error reading file '%s'\n", filename); return false; } } else { if (sizeof(T) != 1 && (ftell(file) % sizeof(T))) { fprintf( stderr, "error: file size should be a multiple of %zd; file '%s' corrupt\n", sizeof(T), filename); return false; } } return true; } // Appends the contents of the file named |filename| to |data|, assuming // each element in the file is of type |T|. The file is opened as a binary file // If |filename| is nullptr or "-", reads from the standard input, but // reopened as a binary file. If any error occurs, writes error messages to // standard error and returns false. template bool ReadBinaryFile(const char* filename, std::vector* data) { const bool use_file = filename && strcmp("-", filename); FILE* fp = nullptr; if (use_file) { fp = fopen(filename, "rb"); } else { SET_STDIN_TO_BINARY_MODE(); fp = stdin; } ReadFile(fp, data); bool succeeded = WasFileCorrectlyRead(fp, filename); if (use_file && fp) fclose(fp); return succeeded; } // Appends the contents of the file named |filename| to |data|, assuming // each element in the file is of type |T|. The file is opened as a text file // If |filename| is nullptr or "-", reads from the standard input, but // reopened as a text file. If any error occurs, writes error messages to // standard error and returns false. template bool ReadTextFile(const char* filename, std::vector* data) { const bool use_file = filename && strcmp("-", filename); FILE* fp = nullptr; if (use_file) { fp = fopen(filename, "r"); } else { SET_STDIN_TO_TEXT_MODE(); fp = stdin; } ReadFile(fp, data); bool succeeded = WasFileCorrectlyRead(fp, filename); if (use_file && fp) fclose(fp); return succeeded; } namespace { // A class to create and manage a file for outputting data. class OutputFile { public: // Opens |filename| in the given mode. If |filename| is nullptr, the empty // string or "-", stdout will be set to the given mode. OutputFile(const char* filename, const char* mode) { const bool use_stdout = !filename || (filename[0] == '-' && filename[1] == '\0'); if (use_stdout) { if (strchr(mode, 'b')) { old_mode_ = SET_STDOUT_TO_BINARY_MODE(); } else { old_mode_ = SET_STDOUT_TO_TEXT_MODE(); } fp_ = stdout; } else { fp_ = fopen(filename, mode); } } ~OutputFile() { if (fp_ == stdout) { SET_STDOUT_MODE(old_mode_); } else if (fp_ != nullptr) { fclose(fp_); } } // Returns a file handle to the file. FILE* GetFileHandle() const { return fp_; } private: FILE* fp_; int old_mode_; }; } // namespace // Writes the given |data| into the file named as |filename| using the given // |mode|, assuming |data| is an array of |count| elements of type |T|. If // |filename| is nullptr or "-", writes to standard output. If any error occurs, // returns false and outputs error message to standard error. template bool WriteFile(const char* filename, const char* mode, const T* data, size_t count) { OutputFile file(filename, mode); FILE* fp = file.GetFileHandle(); if (fp == nullptr) { fprintf(stderr, "error: could not open file '%s'\n", filename); return false; } size_t written = fwrite(data, sizeof(T), count, fp); if (count != written) { fprintf(stderr, "error: could not write to file '%s'\n", filename); return false; } return true; } #endif // TOOLS_IO_H_