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