]> de.git.xonotic.org Git - xonotic/gmqcc.git/blob - opts.c
Wrapper around FILE to take advantage of MSVC "secure" CRT. We don't actually defend...
[xonotic/gmqcc.git] / opts.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 unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS];
26 opts_cmd_t   opts; /* command lien options */
27
28 static void opts_setdefault() {
29     memset(&opts, 0, sizeof(opts_cmd_t));
30     
31     /* warnings */
32     opts_set(opts.warn,  WARN_UNUSED_VARIABLE,           true);
33     opts_set(opts.warn,  WARN_USED_UNINITIALIZED,        true);
34     opts_set(opts.warn,  WARN_UNKNOWN_CONTROL_SEQUENCE,  true);
35     opts_set(opts.warn,  WARN_EXTENSIONS,                true);
36     opts_set(opts.warn,  WARN_FIELD_REDECLARED,          true);
37     opts_set(opts.warn,  WARN_MISSING_RETURN_VALUES,     true);
38     opts_set(opts.warn,  WARN_TOO_FEW_PARAMETERS,        true);
39     opts_set(opts.warn,  WARN_LOCAL_SHADOWS,             false);
40     opts_set(opts.warn,  WARN_LOCAL_CONSTANTS,           true);
41     opts_set(opts.warn,  WARN_VOID_VARIABLES,            true);
42     opts_set(opts.warn,  WARN_IMPLICIT_FUNCTION_POINTER, true);
43     opts_set(opts.warn,  WARN_VARIADIC_FUNCTION,         true);
44     opts_set(opts.warn,  WARN_FRAME_MACROS,              true);
45     opts_set(opts.warn,  WARN_EFFECTLESS_STATEMENT,      true);
46     opts_set(opts.warn,  WARN_END_SYS_FIELDS,            true);
47     opts_set(opts.warn,  WARN_ASSIGN_FUNCTION_TYPES,     true);
48     opts_set(opts.warn,  WARN_PREPROCESSOR,              true);
49     opts_set(opts.warn,  WARN_MULTIFILE_IF,              true);
50     opts_set(opts.warn,  WARN_DOUBLE_DECLARATION,        true);
51     opts_set(opts.warn,  WARN_CONST_VAR,                 true);
52     opts_set(opts.warn,  WARN_MULTIBYTE_CHARACTER,       true);
53     opts_set(opts.warn,  WARN_UNKNOWN_PRAGMAS,           true);
54     opts_set(opts.warn,  WARN_UNREACHABLE_CODE,          true);
55     opts_set(opts.warn,  WARN_CPP,                       true);
56     /* flags */
57     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,           true);
58     opts_set(opts.flags, FTEPP,                          false);
59     opts_set(opts.flags, CORRECT_TERNARY,                true);
60 }
61
62 void opts_init(const char *output, int standard, size_t arraysize) {
63     opts_setdefault();
64     
65     opts.output         = output;
66     opts.standard       = standard;
67     opts.max_array_size = arraysize;
68 }
69
70 static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) {
71     size_t i;
72
73     for (i = 0; i < listsize; ++i) {
74         if (!strcmp(name, list[i].name)) {
75             longbit lb = list[i].bit;
76 #if 0
77             if (on)
78                 flags[lb.idx] |= (1<<(lb.bit));
79             else
80                 flags[lb.idx] &= ~(1<<(lb.bit));
81 #else
82             if (on)
83                 flags[0] |= (1<<lb);
84             else
85                 flags[0] &= ~(1<<(lb));
86 #endif
87             return true;
88         }
89     }
90     return false;
91 }
92 bool opts_setflag  (const char *name, bool on) {
93     return opts_setflag_all(name, on, opts.flags,        opts_flag_list, COUNT_FLAGS);
94 }
95 bool opts_setwarn  (const char *name, bool on) {
96     return opts_setflag_all(name, on, opts.warn,         opts_warn_list, COUNT_WARNINGS);
97 }
98 bool opts_setwerror(const char *name, bool on) {
99     return opts_setflag_all(name, on, opts.werror,       opts_warn_list, COUNT_WARNINGS);
100 }
101 bool opts_setoptim (const char *name, bool on) {
102     return opts_setflag_all(name, on, opts.optimization, opts_opt_list,  COUNT_OPTIMIZATIONS);
103 }
104
105 void opts_set(uint32_t *flags, size_t idx, bool on) {
106     longbit lb = LONGBIT(idx);
107 #if 0
108     if (on)
109         flags[lb.idx] |= (1<<(lb.bit));
110     else
111         flags[lb.idx] &= ~(1<<(lb.bit));
112 #else
113     if (on)
114         flags[0] |= (1<<(lb));
115     else
116         flags[0] &= ~(1<<(lb));
117 #endif
118 }
119
120 void opts_setoptimlevel(unsigned int level) {
121     size_t i;
122     for (i = 0; i < COUNT_OPTIMIZATIONS; ++i)
123         opts_set(opts.optimization, i, level >= opts_opt_oflag[i]);
124 }
125
126 /*
127  * Standard configuration parser and subsystem.  Yes, optionally you may
128  * create ini files or cfg (the driver accepts both) for a project opposed
129  * to supplying just a progs.src (since you also may need to supply command
130  * line arguments or set the options of the compiler) [which cannot be done
131  * from a progs.src.
132  */
133 static char *opts_ini_rstrip(char *s) {
134     char *p = s + strlen(s);
135     while(p > s && isspace(*--p))
136         *p = '\0';
137     return s;
138 }
139
140 static char *opts_ini_lskip(const char *s) {
141     while (*s && isspace(*s))
142         s++;
143     return (char*)s;
144 }
145
146 static char *opts_ini_next(const char *s, char c) {
147     bool last = false;
148     while (*s && *s != c && !(last && *s == ';'))
149         last = !!isspace(*s), s++;
150
151     return (char*)s;
152 }
153
154 static size_t opts_ini_parse (
155     FILE   *filehandle,
156     char *(*loadhandle)(const char *, const char *, const char *),
157     char **errorhandle
158 ) {
159     size_t linesize;
160     size_t lineno             = 1;
161     size_t error              = 0;
162     char  *line               = NULL;
163     char   section_data[2048] = "";
164     char   oldname_data[2048] = "";
165
166     /* parsing and reading variables */
167     char *parse_beg;
168     char *parse_end;
169     char *read_name;
170     char *read_value;
171
172     while (file_getline(&line, &linesize, filehandle) != EOF) {
173         parse_beg = line;
174
175         /* handle BOM */
176         if (lineno == 1 && (
177                 (unsigned char)parse_beg[0] == 0xEF &&
178                 (unsigned char)parse_beg[1] == 0xBB &&
179                 (unsigned char)parse_beg[2] == 0xBF
180             )
181         ) {
182             parse_beg ++; /* 0xEF */
183             parse_beg ++; /* 0xBB */
184             parse_beg ++; /* 0xBF */
185         }
186
187         if (*(parse_beg = opts_ini_lskip(opts_ini_rstrip(parse_beg))) == ';' || *parse_beg == '#') {
188             /* ignore '#' is a perl extension */
189         } else if (*parse_beg == '[') {
190             /* section found */
191             if (*(parse_end = opts_ini_next(parse_beg + 1, ']')) == ']') {
192                 * parse_end = '\0'; /* terminate bro */
193                 strncpy(section_data, parse_beg + 1, sizeof(section_data));
194                 section_data[sizeof(section_data) - 1] = '\0';
195                 *oldname_data                          = '\0';
196             } else if (!error) {
197                 /* otherwise set error to the current line number */
198                 error = lineno;
199             }
200         } else if (*parse_beg && *parse_beg != ';') {
201             /* not a comment, must be a name value pair :) */
202             if (*(parse_end = opts_ini_next(parse_beg, '=')) != '=')
203                   parse_end = opts_ini_next(parse_beg, ':');
204
205             if (*parse_end == '=' || *parse_end == ':') {
206                 *parse_end = '\0'; /* terminate bro */
207                 read_name  = opts_ini_rstrip(parse_beg);
208                 read_value = opts_ini_lskip(parse_end + 1);
209                 if (*(parse_end = opts_ini_next(read_value, '\0')) == ';')
210                     * parse_end = '\0';
211                 opts_ini_rstrip(read_value);
212
213                 /* valid name value pair, lets call down to handler */
214                 strncpy(oldname_data, read_name, sizeof(oldname_data));
215                 oldname_data[sizeof(oldname_data) - 1] ='\0';
216
217                 if ((*errorhandle = loadhandle(section_data, read_name, read_value)) && !error)
218                     error = lineno;
219             } else if (!error) {
220                 /* otherwise set error to the current line number */
221                 error = lineno;
222             }
223         }
224         lineno++;
225     }
226     mem_d(line);
227     return error;
228 }
229
230 /*
231  * returns true/false for a char that contains ("true" or "false" or numeric 0/1)
232  */
233 static bool opts_ini_bool(const char *value) {
234     if (!strcmp(value, "true"))  return true;
235     if (!strcmp(value, "false")) return false;
236     return !!atoi(value);
237 }
238
239 static char *opts_ini_load(const char *section, const char *name, const char *value) {
240     char *error = NULL;
241     bool  found = false;
242
243     /*
244      * undef all of these because they may still be defined like in my
245      * case they where.
246      */  
247     #undef GMQCC_TYPE_FLAGS
248     #undef GMQCC_TYPE_OPTIMIZATIONS
249     #undef GMQCC_TYPE_WARNS
250
251     /* flags */
252     #define GMQCC_TYPE_FLAGS
253     #define GMQCC_DEFINE_FLAG(X)                                       \
254     if (!strcmp(section, "flags") && !strcmp(name, #X)) {              \
255         opts_set(opts.flags, X, opts_ini_bool(value));                 \
256         found = true;                                                  \
257     }
258     #include "opts.def"
259
260     /* warnings */
261     #define GMQCC_TYPE_WARNS
262     #define GMQCC_DEFINE_FLAG(X)                                       \
263     if (!strcmp(section, "warnings") && !strcmp(name, #X)) {           \
264         opts_set(opts.warn, WARN_##X, opts_ini_bool(value));           \
265         found = true;                                                  \
266     }
267     #include "opts.def"
268
269     /* optimizations */
270     #define GMQCC_TYPE_OPTIMIZATIONS
271     #define GMQCC_DEFINE_FLAG(X,Y)                                     \
272     if (!strcmp(section, "optimizations") && !strcmp(name, #X)) {      \
273         opts_set(opts.optimization, OPTIM_##X, opts_ini_bool(value));  \
274         found = true;                                                  \
275     }
276     #include "opts.def"
277
278     /* nothing was found ever! */
279     if (!found) {
280         if (strcmp(section, "flags")         &&
281             strcmp(section, "warnings")      &&
282             strcmp(section, "optimizations"))
283         {
284             vec_upload(error, "invalid section `", 17);
285             vec_upload(error, section, strlen(section));
286             vec_push  (error, '`');
287             vec_push  (error, '\0');
288         } else {
289             vec_upload(error, "invalid variable `", 18);
290             vec_upload(error, name, strlen(name));
291             vec_push  (error, '`');
292             vec_upload(error, " in section: `", 14);
293             vec_upload(error, section, strlen(section));
294             vec_push  (error, '`');
295             vec_push  (error, '\0');
296         }
297     }
298     return error;
299 }
300
301 /*
302  * Actual loading subsystem, this finds the ini or cfg file, and properly
303  * loads it and executes it to set compiler options.
304  */
305 void opts_ini_init(const char *file) {
306     /*
307      * Possible matches are:
308      *  gmqcc.ini
309      *  gmqcc.cfg
310      */
311     char       *error;
312     size_t     line;
313     FILE       *ini;
314
315     
316     if (!file) {
317         /* try ini */
318         if (!(ini = file_open((file = "gmqcc.ini"), "r")))
319             /* try cfg */
320             if (!(ini = file_open((file = "gmqcc.cfg"), "r")))
321                 return;
322     } else if (!(ini = file_open(file, "r")))
323         return;
324
325     con_out("found ini file `%s`\n", file);
326
327     if ((line = opts_ini_parse(ini, &opts_ini_load, &error)) != 0) {
328         /* there was a parse error with the ini file */
329         con_printmsg(LVL_ERROR, file, line, "error", error);
330         vec_free(error);
331     }
332
333     file_close(ini);
334 }