Merge branch 'master' of github.com:graphitemaster/gmqcc
[xonotic/gmqcc.git] / file.c
1 /*
2  * Copyright (C) 2012
3  *     Wolfgang Bumiller
4  *     Dale Weiler
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy of
7  * this software and associated documentation files (the "Software"), to deal in
8  * the Software without restriction, including without limitation the rights to
9  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10  * of the Software, and to permit persons to whom the Software is furnished to do
11  * so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #include "gmqcc.h"
25
26 /*
27  * This is essentially a "wrapper" interface around standard C's IO
28  * library.  There is two reason we implement this, 1) visual studio
29  * hearts for "secure" varations, as part of it's "Security Enhancements
30  * in the CRT" (http://msdn.microsoft.com/en-us/library/8ef0s5kh.aspx).
31  * 2) But one of the greater reasons is for the possibility of large file
32  * support in the future.  I don't expect to reach the 2GB limit any
33  * time soon (mainly because that would be insane).  But when it comes
34  * to adding support for some other larger IO tasks (in the test-suite,
35  * or even the QCVM we'll need it). There is also a third possibility of
36  * building .dat files directly from zip files (which would be very cool
37  * at least I think so).  
38  */
39 #ifdef _MSC_VER
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         abort();
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 *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 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 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 *file_open(const char *filename, const char *mode) {
110         return fopen(filename, mode);
111     }
112
113     size_t file_read(void *buffer, size_t size, size_t count, FILE *fp) {
114         return fread(buffer, size, count, fp);
115     }
116
117     int 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 GMQCC_INLINE file_close(FILE *fp) {
135     /* Invokes file_exception on windows if fp is null */
136     fclose (fp);
137 }
138
139 size_t GMQCC_INLINE 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 GMQCC_INLINE file_error(FILE *fp) {
150     /* Invokes file_exception on windows if fp is null */
151     return ferror(fp);
152 }
153
154 int GMQCC_INLINE file_getc(FILE *fp) {
155     /* Invokes file_exception on windows if fp is null */
156     return fgetc(fp);
157 }
158
159 int GMQCC_INLINE 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 GMQCC_INLINE 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 /*
170  * Implements libc getline for systems that don't have it, which is
171  * assmed all.  This works the same as getline().
172  */
173 int file_getline(char **lineptr, size_t *n, FILE *stream) {
174     int   chr;
175     int   ret;
176     char *pos;
177
178     if (!lineptr || !n || !stream)
179         return -1;
180     if (!*lineptr) {
181         if (!(*lineptr = (char*)mem_a((*n=64))))
182             return -1;
183     }
184
185     chr = *n;
186     pos = *lineptr;
187
188     for (;;) {
189         int c = file_getc(stream);
190
191         if (chr < 2) {
192             *n += (*n > 16) ? *n : 64;
193             chr = *n + *lineptr - pos;
194             if (!(*lineptr = (char*)mem_r(*lineptr,*n)))
195                 return -1;
196             pos = *n - chr + *lineptr;
197         }
198
199         if (ferror(stream))
200             return -1;
201         if (c == EOF) {
202             if (pos == *lineptr)
203                 return -1;
204             else
205                 break;
206         }
207
208         *pos++ = c;
209         chr--;
210         if (c == '\n')
211             break;
212     }
213     *pos = '\0';
214     return (ret = pos - *lineptr);
215 }