gitignore: add gmqcc, gmqpak, qmcvm, testsuite, pak.
[xonotic/gmqcc.git] / fs.c
1 /*
2  * Copyright (C) 2012, 2013
3  *     Dale Weiler
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is furnished to do
10  * so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 #include "gmqcc.h"
24
25 /*
26  * This is essentially a "wrapper" interface around standard C's IO
27  * library.  There is two reason we implement this, 1) visual studio
28  * hearts for "secure" varations, as part of it's "Security Enhancements
29  * in the CRT" (http://msdn.microsoft.com/en-us/library/8ef0s5kh.aspx).
30  * 2) But one of the greater reasons is for the possibility of large file
31  * support in the future.  I don't expect to reach the 2GB limit any
32  * time soon (mainly because that would be insane).  But when it comes
33  * to adding support for some other larger IO tasks (in the test-suite,
34  * or even the QCVM we'll need it). There is also a third possibility of
35  * building .dat files directly from zip files (which would be very cool
36  * at least I think so).
37  */
38 #ifdef _MSC_VER
39 #include <crtdbg.h> /* _CrtSetReportMode, _CRT_ASSERT */
40 /* {{{ */
41     /*
42      * Visual Studio has security CRT features which I actually want to support
43      * if we ever port to Windows 8, and want GMQCC to be API safe.
44      *
45      * We handle them here, for all file-operations.
46      */
47
48     static void file_exception (
49         const wchar_t *expression,
50         const wchar_t *function,
51         const wchar_t *file,
52         unsigned int   line,
53         uintptr_t      reserved
54     ) {
55         wprintf(L"Invalid parameter dectected %s:%d %s [%s]\n", file, line, function, expression);
56         wprintf(L"Aborting ...\n");
57         exit(EXIT_FAILURE);
58     }
59
60     static void file_init() {
61         static bool init = false;
62
63         if (init)
64             return;
65
66         _set_invalid_parameter_handler(&file_exception);
67
68         /*
69          * Turnoff the message box for CRT asserations otherwise
70          * we don't get the error reported to the console as we should
71          * otherwise get.
72          */
73         _CrtSetReportMode(_CRT_ASSERT, 0);
74         init = !init;
75     }
76
77
78     FILE *fs_file_open(const char *filename, const char *mode) {
79         FILE *handle = NULL;
80         file_init();
81
82         return (fopen_s(&handle, filename, mode) != 0) ? NULL : handle;
83     }
84
85     size_t fs_file_read(void *buffer, size_t size, size_t count, FILE *fp) {
86         file_init();
87         return fread_s(buffer, size*count, size, count, fp);
88     }
89
90     int fs_file_printf(FILE *fp, const char *format, ...) {
91         int      rt;
92         va_list  va;
93         va_start(va, format);
94
95         file_init();
96         rt = vfprintf_s(fp, format, va);
97         va_end  (va);
98
99         return rt;
100     }
101
102 /* }}} */
103 #else
104 /* {{{ */
105     /*
106      * All other compilers/platforms that don't restrict insane policies on
107      * IO for no aparent reason.
108      */
109     FILE *fs_file_open(const char *filename, const char *mode) {
110         return fopen(filename, mode);
111     }
112
113     size_t fs_file_read(void *buffer, size_t size, size_t count, FILE *fp) {
114         return fread(buffer, size, count, fp);
115     }
116
117     int fs_file_printf(FILE *fp, const char *format, ...) {
118         int      rt;
119         va_list  va;
120         va_start(va, format);
121         rt = vfprintf(fp, format, va);
122         va_end  (va);
123
124         return rt;
125     }
126
127 /* }}} */
128 #endif
129
130 /*
131  * These are implemented as just generic wrappers to keep consistency in
132  * the API.  Not as macros though
133  */
134 void fs_file_close(FILE *fp) {
135     /* Invokes file_exception on windows if fp is null */
136     fclose (fp);
137 }
138
139 size_t  fs_file_write (
140     const void    *buffer,
141     size_t         size,
142     size_t         count,
143     FILE          *fp
144 ) {
145     /* Invokes file_exception on windows if fp is null */
146     return fwrite(buffer, size, count, fp);
147 }
148
149 int fs_file_error(FILE *fp) {
150     /* Invokes file_exception on windows if fp is null */
151     return ferror(fp);
152 }
153
154 int fs_file_getc(FILE *fp) {
155     /* Invokes file_exception on windows if fp is null */
156     return fgetc(fp);
157 }
158
159 int fs_file_puts(FILE *fp, const char *str) {
160     /* Invokes file_exception on windows if fp is null */
161     return fputs(str, fp);
162 }
163
164 int fs_file_seek(FILE *fp, long int off, int whence) {
165     /* Invokes file_exception on windows if fp is null */
166     return fseek(fp, off, whence);
167 }
168
169 long int fs_file_tell(FILE *fp) {
170     /* Invokes file_exception on windows if fp is null */
171     return ftell(fp);
172 }
173
174 /*
175  * Implements libc getline for systems that don't have it, which is
176  * assmed all.  This works the same as getline().
177  */
178 int fs_file_getline(char **lineptr, size_t *n, FILE *stream) {
179     int   chr;
180     int   ret;
181     char *pos;
182
183     if (!lineptr || !n || !stream)
184         return -1;
185     if (!*lineptr) {
186         if (!(*lineptr = (char*)mem_a((*n=64))))
187             return -1;
188     }
189
190     chr = *n;
191     pos = *lineptr;
192
193     for (;;) {
194         int c = fs_file_getc(stream);
195
196         if (chr < 2) {
197             *n += (*n > 16) ? *n : 64;
198             chr = *n + *lineptr - pos;
199             if (!(*lineptr = (char*)mem_r(*lineptr,*n)))
200                 return -1;
201             pos = *n - chr + *lineptr;
202         }
203
204         if (ferror(stream))
205             return -1;
206         if (c == EOF) {
207             if (pos == *lineptr)
208                 return -1;
209             else
210                 break;
211         }
212
213         *pos++ = c;
214         chr--;
215         if (c == '\n')
216             break;
217     }
218     *pos = '\0';
219     return (ret = pos - *lineptr);
220 }
221
222 /*
223  * Now we implement some directory functionality.  Windows lacks dirent.h
224  * this is such a pisss off, we implement it here.
225  */
226 #if defined(_WIN32) && !defined(__MINGW32__)
227     DIR *fs_dir_open(const char *name) {
228         DIR *dir = (DIR*)mem_a(sizeof(DIR) + strlen(name));
229         if (!dir)
230             return NULL;
231
232         util_strncpy(dir->dd_name, name, strlen(name));
233         return dir;
234     }
235
236     int fs_dir_close(DIR *dir) {
237         FindClose((HANDLE)dir->dd_handle);
238         mem_d ((void*)dir);
239         return 0;
240     }
241
242     struct dirent *fs_dir_read(DIR *dir) {
243         WIN32_FIND_DATA info;
244         struct dirent  *data;
245         int             rets;
246
247         if (!dir->dd_handle) {
248             char *dirname;
249             if (*dir->dd_name) {
250                 size_t n = strlen(dir->dd_name);
251                 if ((dirname  = (char*)mem_a(n + 5) /* 4 + 1 */)) {
252                     util_strncpy(dirname, dir->dd_name, n);
253                     util_strncpy(dirname + n, "\\*.*", 4);   /* 4 + 1 */
254                 }
255             } else {
256                 if (!(dirname = util_strdup("\\*.*")))
257                     return NULL;
258             }
259
260             dir->dd_handle = (long)FindFirstFile(dirname, &info);
261             mem_d(dirname);
262             rets = !(!dir->dd_handle);
263         } else if (dir->dd_handle != -11) {
264             rets = FindNextFile ((HANDLE)dir->dd_handle, &info);
265         } else {
266             rets = 0;
267         }
268
269         if (!rets)
270             return NULL;
271
272         if ((data = (struct dirent*)mem_a(sizeof(struct dirent)))) {
273             util_strncpy(data->d_name, info.cFileName, FILENAME_MAX - 1);
274             data->d_name[FILENAME_MAX - 1] = '\0'; /* terminate */
275             data->d_namlen                 = strlen(data->d_name);
276         }
277         return data;
278     }
279
280     int fs_dir_change(const char *path) {
281         return !SetCurrentDirectory(path);
282     }
283
284     int fs_dir_make(const char *path) {
285         return !CreateDirectory(path, NULL);
286     }
287 #else
288 #   if !defined(__MINGW32__)
289 #       include <sys/stat.h> /* mkdir */
290
291         int fs_dir_make(const char *path) {
292             return mkdir(path, 0700);
293         }
294 #   else
295         int fs_dir_make(const char *path) {
296             return mkdir(path);
297         }
298 #   endif /*! !defined(__MINGW32__) */
299
300 DIR *fs_dir_open(const char *name) {
301     return opendir(name);
302 }
303
304 int fs_dir_close(DIR *dir) {
305     return closedir(dir);
306 }
307
308 struct dirent *fs_dir_read(DIR *dir) {
309     return readdir(dir);
310 }
311
312 #endif /*! defined(_WIN32) && !defined(__MINGW32__) */