1 // Copyright (c) 2016 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef TOOLS_IO_H_
16 #define TOOLS_IO_H_
17
18 #include <cstdint>
19 #include <cstdio>
20 #include <cstring>
21 #include <vector>
22
23 #if defined(SPIRV_WINDOWS)
24 #include <fcntl.h>
25 #include <io.h>
26
27 #define SET_STDIN_TO_BINARY_MODE() _setmode(_fileno(stdin), O_BINARY);
28 #define SET_STDIN_TO_TEXT_MODE() _setmode(_fileno(stdin), O_TEXT);
29 #define SET_STDOUT_TO_BINARY_MODE() _setmode(_fileno(stdout), O_BINARY);
30 #define SET_STDOUT_TO_TEXT_MODE() _setmode(_fileno(stdout), O_TEXT);
31 #define SET_STDOUT_MODE(mode) _setmode(_fileno(stdout), mode);
32 #else
33 #define SET_STDIN_TO_BINARY_MODE()
34 #define SET_STDIN_TO_TEXT_MODE()
35 #define SET_STDOUT_TO_BINARY_MODE() 0
36 #define SET_STDOUT_TO_TEXT_MODE() 0
37 #define SET_STDOUT_MODE(mode)
38 #endif
39
40 // Appends the contents of the |file| to |data|, assuming each element in the
41 // file is of type |T|.
42 template <typename T>
ReadFile(FILE * file,std::vector<T> * data)43 void ReadFile(FILE* file, std::vector<T>* data) {
44 if (file == nullptr) return;
45
46 const int buf_size = 1024;
47 T buf[buf_size];
48 while (size_t len = fread(buf, sizeof(T), buf_size, file)) {
49 data->insert(data->end(), buf, buf + len);
50 }
51 }
52
53 // Returns true if |file| has encountered an error opening the file or reading
54 // the file as a series of element of type |T|. If there was an error, writes an
55 // error message to standard error.
56 template <class T>
WasFileCorrectlyRead(FILE * file,const char * filename)57 bool WasFileCorrectlyRead(FILE* file, const char* filename) {
58 if (file == nullptr) {
59 fprintf(stderr, "error: file does not exist '%s'\n", filename);
60 return false;
61 }
62
63 if (ftell(file) == -1L) {
64 if (ferror(file)) {
65 fprintf(stderr, "error: error reading file '%s'\n", filename);
66 return false;
67 }
68 } else {
69 if (sizeof(T) != 1 && (ftell(file) % sizeof(T))) {
70 fprintf(
71 stderr,
72 "error: file size should be a multiple of %zd; file '%s' corrupt\n",
73 sizeof(T), filename);
74 return false;
75 }
76 }
77 return true;
78 }
79
80 // Appends the contents of the file named |filename| to |data|, assuming
81 // each element in the file is of type |T|. The file is opened as a binary file
82 // If |filename| is nullptr or "-", reads from the standard input, but
83 // reopened as a binary file. If any error occurs, writes error messages to
84 // standard error and returns false.
85 template <typename T>
ReadBinaryFile(const char * filename,std::vector<T> * data)86 bool ReadBinaryFile(const char* filename, std::vector<T>* data) {
87 const bool use_file = filename && strcmp("-", filename);
88 FILE* fp = nullptr;
89 if (use_file) {
90 fp = fopen(filename, "rb");
91 } else {
92 SET_STDIN_TO_BINARY_MODE();
93 fp = stdin;
94 }
95
96 ReadFile(fp, data);
97 bool succeeded = WasFileCorrectlyRead<T>(fp, filename);
98 if (use_file && fp) fclose(fp);
99 return succeeded;
100 }
101
102 // Appends the contents of the file named |filename| to |data|, assuming
103 // each element in the file is of type |T|. The file is opened as a text file
104 // If |filename| is nullptr or "-", reads from the standard input, but
105 // reopened as a text file. If any error occurs, writes error messages to
106 // standard error and returns false.
107 template <typename T>
ReadTextFile(const char * filename,std::vector<T> * data)108 bool ReadTextFile(const char* filename, std::vector<T>* data) {
109 const bool use_file = filename && strcmp("-", filename);
110 FILE* fp = nullptr;
111 if (use_file) {
112 fp = fopen(filename, "r");
113 } else {
114 SET_STDIN_TO_TEXT_MODE();
115 fp = stdin;
116 }
117
118 ReadFile(fp, data);
119 bool succeeded = WasFileCorrectlyRead<T>(fp, filename);
120 if (use_file && fp) fclose(fp);
121 return succeeded;
122 }
123
124 namespace {
125 // A class to create and manage a file for outputting data.
126 class OutputFile {
127 public:
128 // Opens |filename| in the given mode. If |filename| is nullptr, the empty
129 // string or "-", stdout will be set to the given mode.
OutputFile(const char * filename,const char * mode)130 OutputFile(const char* filename, const char* mode) {
131 const bool use_stdout =
132 !filename || (filename[0] == '-' && filename[1] == '\0');
133 if (use_stdout) {
134 if (strchr(mode, 'b')) {
135 old_mode_ = SET_STDOUT_TO_BINARY_MODE();
136 } else {
137 old_mode_ = SET_STDOUT_TO_TEXT_MODE();
138 }
139 fp_ = stdout;
140 } else {
141 fp_ = fopen(filename, mode);
142 }
143 }
144
~OutputFile()145 ~OutputFile() {
146 if (fp_ == stdout) {
147 SET_STDOUT_MODE(old_mode_);
148 } else if (fp_ != nullptr) {
149 fclose(fp_);
150 }
151 }
152
153 // Returns a file handle to the file.
GetFileHandle()154 FILE* GetFileHandle() const { return fp_; }
155
156 private:
157 FILE* fp_;
158 int old_mode_;
159 };
160 } // namespace
161
162 // Writes the given |data| into the file named as |filename| using the given
163 // |mode|, assuming |data| is an array of |count| elements of type |T|. If
164 // |filename| is nullptr or "-", writes to standard output. If any error occurs,
165 // returns false and outputs error message to standard error.
166 template <typename T>
WriteFile(const char * filename,const char * mode,const T * data,size_t count)167 bool WriteFile(const char* filename, const char* mode, const T* data,
168 size_t count) {
169 OutputFile file(filename, mode);
170 FILE* fp = file.GetFileHandle();
171 if (fp == nullptr) {
172 fprintf(stderr, "error: could not open file '%s'\n", filename);
173 return false;
174 }
175
176 size_t written = fwrite(data, sizeof(T), count, fp);
177 if (count != written) {
178 fprintf(stderr, "error: could not write to file '%s'\n", filename);
179 return false;
180 }
181
182 return true;
183 }
184
185 #endif // TOOLS_IO_H_
186