More cleanup
[xonotic/gmqcc.git] / main.c
1 #include <stdlib.h>
2 #include <string.h>
3
4 #include "gmqcc.h"
5 #include "lexer.h"
6
7 /* TODO: cleanup this whole file .. it's a fuckign mess */
8
9 /* set by the standard */
10 const oper_info *operators      = NULL;
11 size_t           operator_count = 0;
12 static bool      opts_output_wasset = false;
13
14 typedef struct { char *filename; int   type;  } argitem;
15 typedef struct { char *name;     char *value; } ppitem;
16 static argitem *items = NULL;
17 static ppitem  *ppems = NULL;
18
19 #define TYPE_QC  0
20 #define TYPE_ASM 1
21 #define TYPE_SRC 2
22
23 static const char *app_name;
24
25 static void version(void) {
26     con_out("GMQCC %d.%d.%d Built %s %s\n" GMQCC_DEV_VERSION_STRING,
27         GMQCC_VERSION_MAJOR,
28         GMQCC_VERSION_MINOR,
29         GMQCC_VERSION_PATCH,
30         __DATE__,
31         __TIME__
32     );
33 }
34
35 static int usage(void) {
36     con_out("usage: %s [options] [files...]", app_name);
37     con_out("options:\n"
38             "  -h, --help             show this help message\n"
39             "  -debug                 turns on compiler debug messages\n");
40     con_out("  -o, --output=file      output file, defaults to progs.dat\n"
41             "  -s filename            add a progs.src file to be used\n");
42     con_out("  -E                     stop after preprocessing\n");
43     con_out("  -q, --quiet            be less verbose\n");
44     con_out("  -config file           use the specified ini file\n");
45     con_out("  -std=standard          select one of the following standards\n"
46             "       -std=qcc          original QuakeC\n"
47             "       -std=fteqcc       fteqcc QuakeC\n"
48             "       -std=gmqcc        this compiler (default)\n");
49     con_out("  -f<flag>               enable a flag\n"
50             "  -fno-<flag>            disable a flag\n"
51             "  -fhelp                 list possible flags\n");
52     con_out("  -W<warning>            enable a warning\n"
53             "  -Wno-<warning>         disable a warning\n"
54             "  -Wall                  enable all warnings\n");
55     con_out("  -Werror                treat warnings as errors\n"
56             "  -Werror-<warning>      treat a warning as error\n"
57             "  -Wno-error-<warning>   opposite of the above\n");
58     con_out("  -Whelp                 list possible warnings\n");
59     con_out("  -O<number>             optimization level\n"
60             "  -O<name>               enable specific optimization\n"
61             "  -Ono-<name>            disable specific optimization\n"
62             "  -Ohelp                 list optimizations\n");
63     con_out("  -force-crc=num         force a specific checksum into the header\n");
64     con_out("  -state-fps=num         emulate OP_STATE with the specified FPS\n");
65     con_out("  -coverage              add coverage support\n");
66     return -1;
67 }
68
69 /* command line parsing */
70 static bool options_witharg(int *argc_, char ***argv_, char **out) {
71     int  argc   = *argc_;
72     char **argv = *argv_;
73
74     if (argv[0][2]) {
75         *out = argv[0]+2;
76         return true;
77     }
78     /* eat up the next */
79     if (argc < 2) /* no parameter was provided */
80         return false;
81
82     *out = argv[1];
83     --*argc_;
84     ++*argv_;
85     return true;
86 }
87
88 static bool options_long_witharg_all(const char *optname, int *argc_, char ***argv_, char **out, int ds, bool split) {
89     int  argc   = *argc_;
90     char **argv = *argv_;
91
92     size_t len = strlen(optname);
93
94     if (strncmp(argv[0]+ds, optname, len))
95         return false;
96
97     /* it's --optname, check how the parameter is supplied */
98     if (argv[0][ds+len] == '=') {
99         /* using --opt=param */
100         *out = argv[0]+ds+len+1;
101         return true;
102     }
103
104     if (!split || argc < ds) /* no parameter was provided, or only single-arg form accepted */
105         return false;
106
107     /* using --opt param */
108     *out = argv[1];
109     --*argc_;
110     ++*argv_;
111     return true;
112 }
113 static bool options_long_witharg(const char *optname, int *argc_, char ***argv_, char **out) {
114     return options_long_witharg_all(optname, argc_, argv_, out, 2, true);
115 }
116 static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, char **out) {
117     return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
118 }
119
120 static bool options_parse(int argc, char **argv) {
121     bool argend = false;
122     size_t itr;
123     char buffer[1024];
124     char *config = NULL;
125
126     while (!argend && argc > 1) {
127         char *argarg;
128         argitem item;
129         ppitem macro;
130
131         ++argv;
132         --argc;
133
134         if (argv[0][0] == '-') {
135             /* All gcc-type long options */
136             if (options_long_gcc("std", &argc, &argv, &argarg)) {
137                 if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) {
138
139                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,          true);
140                     opts_set(opts.flags, CORRECT_LOGIC,                 true);
141                     opts_set(opts.flags, SHORT_LOGIC,                   true);
142                     opts_set(opts.flags, UNTYPED_NIL,                   true);
143                     opts_set(opts.flags, VARIADIC_ARGS,                 true);
144                     opts_set(opts.flags, FALSE_EMPTY_STRINGS,           false);
145                     opts_set(opts.flags, TRUE_EMPTY_STRINGS,            true);
146                     opts_set(opts.flags, LOOP_LABELS,                   true);
147                     opts_set(opts.flags, TRANSLATABLE_STRINGS,          true);
148                     opts_set(opts.flags, INITIALIZED_NONCONSTANTS,      true);
149                     opts_set(opts.werror, WARN_INVALID_PARAMETER_COUNT, true);
150                     opts_set(opts.werror, WARN_MISSING_RETURN_VALUES,   true);
151                     opts_set(opts.flags,  EXPRESSIONS_FOR_BUILTINS,     true);
152                     opts_set(opts.warn,   WARN_BREAKDEF,                true);
153
154
155
156                     OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_GMQCC;
157
158                 } else if (!strcmp(argarg, "qcc")) {
159
160                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,  false);
161                     opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
162
163                     OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCC;
164
165                 } else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
166
167                     opts_set(opts.flags, FTEPP,                    true);
168                     opts_set(opts.flags, TRANSLATABLE_STRINGS,     true);
169                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,     false);
170                     opts_set(opts.flags, ASSIGN_FUNCTION_TYPES,    true);
171                     opts_set(opts.flags, CORRECT_TERNARY,          false);
172                     opts_set(opts.warn, WARN_TERNARY_PRECEDENCE,   true);
173                     opts_set(opts.warn, WARN_BREAKDEF,             true);
174
175                     OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_FTEQCC;
176
177                 } else if (!strcmp(argarg, "qccx")) {
178
179                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,  false);
180                     OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCCX;
181
182                 } else {
183                     con_out("Unknown standard: %s\n", argarg);
184                     return false;
185                 }
186                 continue;
187             }
188             if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
189
190                 OPTS_OPTION_BOOL(OPTION_FORCECRC)   = true;
191                 OPTS_OPTION_U16 (OPTION_FORCED_CRC) = strtol(argarg, NULL, 0);
192                 continue;
193             }
194             if (options_long_gcc("state-fps", &argc, &argv, &argarg)) {
195                 OPTS_OPTION_U32(OPTION_STATE_FPS) = strtol(argarg, NULL, 0);
196                 opts_set(opts.flags, EMULATE_STATE, true);
197                 continue;
198             }
199             if (options_long_gcc("config", &argc, &argv, &argarg)) {
200                 config = argarg;
201                 continue;
202             }
203             if (options_long_gcc("progsrc", &argc, &argv, &argarg)) {
204                 OPTS_OPTION_STR(OPTION_PROGSRC) = argarg;
205                 continue;
206             }
207
208             /* show defaults (like pathscale) */
209             if (!strcmp(argv[0]+1, "show-defaults")) {
210                 for (itr = 0; itr < COUNT_FLAGS; ++itr) {
211                     if (!OPTS_FLAG(itr))
212                         continue;
213
214                     memset(buffer, 0, sizeof(buffer));
215                     util_strtononcmd(opts_flag_list[itr].name, buffer, strlen(opts_flag_list[itr].name) + 1);
216
217                     con_out("-f%s ", buffer);
218                 }
219                 for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
220                     if (!OPTS_WARN(itr))
221                         continue;
222
223                     memset(buffer, 0, sizeof(buffer));
224                     util_strtononcmd(opts_warn_list[itr].name, buffer, strlen(opts_warn_list[itr].name) + 1);
225                     con_out("-W%s ", buffer);
226                 }
227                 con_out("\n");
228                 exit(0);
229             }
230
231             if (!strcmp(argv[0]+1, "debug")) {
232                 OPTS_OPTION_BOOL(OPTION_DEBUG) = true;
233                 continue;
234             }
235             if (!strcmp(argv[0]+1, "dump")) {
236                 OPTS_OPTION_BOOL(OPTION_DUMP)  = true;
237                 continue;
238             }
239             if (!strcmp(argv[0]+1, "dumpfin")) {
240                 OPTS_OPTION_BOOL(OPTION_DUMPFIN) = true;
241                 continue;
242             }
243             if (!strcmp(argv[0]+1, "nocolor")) {
244                 con_color(0);
245                 continue;
246             }
247             if (!strcmp(argv[0]+1, "coverage")) {
248                 OPTS_OPTION_BOOL(OPTION_COVERAGE) = true;
249                 continue;
250             }
251
252             switch (argv[0][1]) {
253                 /* -h, show usage but exit with 0 */
254                 case 'h':
255                     usage();
256                     exit(0);
257                     /* break; never reached because of exit(0) */
258
259                 case 'v':
260                     version();
261                     exit(0);
262
263                 case 'E':
264                     OPTS_OPTION_BOOL(OPTION_PP_ONLY) = true;
265                     opts_set(opts.flags, FTEPP_PREDEFS, true); /* predefs on for -E */
266                     break;
267
268                 /* debug turns on -flno */
269                 case 'g':
270                     opts_setflag("LNO", true);
271                     OPTS_OPTION_BOOL(OPTION_G) = true;
272                     break;
273
274                 case 'q':
275                     OPTS_OPTION_BOOL(OPTION_QUIET) = true;
276                     break;
277
278                 case 'D':
279                     if (!strlen(argv[0]+2)) {
280                         con_err("expected name after -D\n");
281                         exit(0);
282                     }
283
284                     if (!(argarg = strchr(argv[0] + 2, '='))) {
285                         macro.name  = util_strdup(argv[0]+2);
286                         macro.value = NULL;
287                     } else {
288                         *argarg='\0'; /* terminate for name */
289                         macro.name  = util_strdup(argv[0]+2);
290                         macro.value = util_strdup(argarg+1);
291                     }
292                     vec_push(ppems, macro);
293                     break;
294
295                 /* handle all -fflags */
296                 case 'f':
297                     util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
298                     if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
299                         con_out("Possible flags:\n\n");
300                         for (itr = 0; itr < COUNT_FLAGS; ++itr) {
301                             util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
302                             con_out(" -f%s\n", buffer);
303                         }
304                         exit(0);
305                     }
306                     else if (!strncmp(argv[0]+2, "NO_", 3)) {
307                         if (!opts_setflag(argv[0]+5, false)) {
308                             con_out("unknown flag: %s\n", argv[0]+2);
309                             return false;
310                         }
311                     }
312                     else if (!opts_setflag(argv[0]+2, true)) {
313                         con_out("unknown flag: %s\n", argv[0]+2);
314                         return false;
315                     }
316                     break;
317                 case 'W':
318                     util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
319                     if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
320                         con_out("Possible warnings:\n");
321                         for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
322                             util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
323                             con_out(" -W%s\n", buffer);
324                             if (itr == WARN_DEBUG)
325                                 con_out("   Warnings included by -Wall:\n");
326                         }
327                         exit(0);
328                     }
329                     else if (!strcmp(argv[0]+2, "NO_ERROR") ||
330                              !strcmp(argv[0]+2, "NO_ERROR_ALL"))
331                     {
332                         for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.werror); ++itr)
333                             opts.werror[itr] = 0;
334                         break;
335                     }
336                     else if (!strcmp(argv[0]+2, "ERROR") ||
337                              !strcmp(argv[0]+2, "ERROR_ALL"))
338                     {
339                         opts_backup_non_Werror_all();
340                         for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.werror); ++itr)
341                             opts.werror[itr] = 0xFFFFFFFFL;
342                         opts_restore_non_Werror_all();
343                         break;
344                     }
345                     else if (!strcmp(argv[0]+2, "NONE")) {
346                         for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.warn); ++itr)
347                             opts.warn[itr] = 0;
348                         break;
349                     }
350                     else if (!strcmp(argv[0]+2, "ALL")) {
351                         opts_backup_non_Wall();
352                         for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.warn); ++itr)
353                             opts.warn[itr] = 0xFFFFFFFFL;
354                         opts_restore_non_Wall();
355                         break;
356                     }
357                     else if (!strncmp(argv[0]+2, "ERROR_", 6)) {
358                         if (!opts_setwerror(argv[0]+8, true)) {
359                             con_out("unknown warning: %s\n", argv[0]+2);
360                             return false;
361                         }
362                     }
363                     else if (!strncmp(argv[0]+2, "NO_ERROR_", 9)) {
364                         if (!opts_setwerror(argv[0]+11, false)) {
365                             con_out("unknown warning: %s\n", argv[0]+2);
366                             return false;
367                         }
368                     }
369                     else if (!strncmp(argv[0]+2, "NO_", 3)) {
370                         if (!opts_setwarn(argv[0]+5, false)) {
371                             con_out("unknown warning: %s\n", argv[0]+2);
372                             return false;
373                         }
374                     }
375                     else if (!opts_setwarn(argv[0]+2, true)) {
376                         con_out("unknown warning: %s\n", argv[0]+2);
377                         return false;
378                     }
379                     break;
380
381                 case 'O':
382                     if (!options_witharg(&argc, &argv, &argarg)) {
383                         con_out("option -O requires a numerical argument, or optimization name with an optional 'no-' prefix\n");
384                         return false;
385                     }
386                     if (util_isdigit(argarg[0])) {
387                         uint32_t val = (uint32_t)strtol(argarg, NULL, 10);
388                         OPTS_OPTION_U32(OPTION_O) = val;
389                         opts_setoptimlevel(val);
390                     } else {
391                         util_strtocmd(argarg, argarg, strlen(argarg)+1);
392                         if (!strcmp(argarg, "HELP")) {
393                             con_out("Possible optimizations:\n");
394                             for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
395                                 util_strtononcmd(opts_opt_list[itr].name, buffer, sizeof(buffer));
396                                 con_out(" -O%-20s (-O%u)\n", buffer, opts_opt_oflag[itr]);
397                             }
398                             exit(0);
399                         }
400                         else if (!strcmp(argarg, "ALL"))
401                             opts_setoptimlevel(OPTS_OPTION_U32(OPTION_O) = 9999);
402                         else if (!strncmp(argarg, "NO_", 3)) {
403                             /* constant folding cannot be turned off for obvious reasons */
404                             if (!strcmp(argarg, "NO_CONST_FOLD") || !opts_setoptim(argarg+3, false)) {
405                                 con_out("unknown optimization: %s\n", argarg+3);
406                                 return false;
407                             }
408                         }
409                         else {
410                             if (!opts_setoptim(argarg, true)) {
411                                 con_out("unknown optimization: %s\n", argarg);
412                                 return false;
413                             }
414                         }
415                     }
416                     break;
417
418                 case 'o':
419                     if (!options_witharg(&argc, &argv, &argarg)) {
420                         con_out("option -o requires an argument: the output file name\n");
421                         return false;
422                     }
423                     OPTS_OPTION_STR(OPTION_OUTPUT) = argarg;
424                     opts_output_wasset = true;
425                     break;
426
427                 case 'a':
428                 case 's':
429                     item.type = argv[0][1] == 'a' ? TYPE_ASM : TYPE_SRC;
430                     if (!options_witharg(&argc, &argv, &argarg)) {
431                         con_out("option -a requires a filename %s\n",
432                                 (argv[0][1] == 'a' ? "containing QC-asm" : "containing a progs.src formatted list"));
433                         return false;
434                     }
435                     item.filename = argarg;
436                     vec_push(items, item);
437                     break;
438
439                 case '-':
440                     if (!argv[0][2]) {
441                         /* anything following -- is considered a non-option argument */
442                         argend = true;
443                         break;
444                     }
445             /* All long options without arguments */
446                     else if (!strcmp(argv[0]+2, "help")) {
447                         usage();
448                         exit(0);
449                     }
450                     else if (!strcmp(argv[0]+2, "version")) {
451                         version();
452                         exit(0);
453                     }
454                     else if (!strcmp(argv[0]+2, "quiet")) {
455                         OPTS_OPTION_BOOL(OPTION_QUIET) = true;
456                         break;
457                     }
458                     else if (!strcmp(argv[0]+2, "add-info")) {
459                         OPTS_OPTION_BOOL(OPTION_ADD_INFO) = true;
460                         break;
461                     }
462                     else {
463             /* All long options with arguments */
464                         if (options_long_witharg("output", &argc, &argv, &argarg)) {
465                             OPTS_OPTION_STR(OPTION_OUTPUT) = argarg;
466                             opts_output_wasset = true;
467                         } else {
468                             con_out("Unknown parameter: %s\n", argv[0]);
469                             return false;
470                         }
471                     }
472                     break;
473
474                 default:
475                     con_out("Unknown parameter: %s\n", argv[0]);
476                     return false;
477             }
478         }
479         else
480         {
481             /* it's a QC filename */
482             item.filename = argv[0];
483             item.type     = TYPE_QC;
484             vec_push(items, item);
485         }
486     }
487     opts_ini_init(config);
488     return true;
489 }
490
491 /* returns the line number, or -1 on error */
492 static bool progs_nextline(char **out, size_t *alen, FILE *src) {
493     int    len;
494     char  *line;
495     char  *start;
496     char  *end;
497
498     line = *out;
499     len  = util_getline(&line, alen, src);
500     if (len == -1)
501         return false;
502
503     /* start at first non-blank */
504     for (start = line; util_isspace(*start); ++start) {}
505     /* end at the first non-blank */
506     for (end = start; *end && !util_isspace(*end);  ++end)   {}
507
508     *out = line;
509     /* move the actual filename to the beginning */
510     while (start != end) {
511         *line++ = *start++;
512     }
513     *line = 0;
514     return true;
515 }
516
517 int main(int argc, char **argv) {
518     size_t          itr;
519     int             retval           = 0;
520     bool            operators_free   = false;
521     bool            progs_src        = false;
522     FILE       *outfile         = NULL;
523     struct parser_s *parser          = NULL;
524     struct ftepp_s  *ftepp           = NULL;
525
526     app_name = argv[0];
527     con_init ();
528     opts_init("progs.dat", COMPILER_QCC, (1024 << 3));
529
530     util_seed(time(0));
531
532     if (!options_parse(argc, argv)) {
533         return usage();
534     }
535
536     if (OPTS_FLAG(TRUE_EMPTY_STRINGS) && OPTS_FLAG(FALSE_EMPTY_STRINGS)) {
537         con_err("-ftrue-empty-strings and -ffalse-empty-strings are mutually exclusive");
538         exit(EXIT_FAILURE);
539     }
540
541     /* the standard decides which set of operators to use */
542     if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
543         operators      = c_operators;
544         operator_count = GMQCC_ARRAY_COUNT(c_operators);
545     } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
546         operators      = fte_operators;
547         operator_count = GMQCC_ARRAY_COUNT(fte_operators);
548     } else {
549         operators      = qcc_operators;
550         operator_count = GMQCC_ARRAY_COUNT(qcc_operators);
551     }
552
553     if (operators == fte_operators) {
554         /* fix ternary? */
555         if (OPTS_FLAG(CORRECT_TERNARY)) {
556             oper_info *newops;
557             if (operators[operator_count-2].id != opid1(',') ||
558                 operators[operator_count-1].id != opid2(':','?'))
559             {
560                 con_err("internal error: operator precedence table wasn't updated correctly!\n");
561                 exit(EXIT_FAILURE);
562             }
563             operators_free = true;
564             newops = (oper_info*)mem_a(sizeof(operators[0]) * operator_count);
565             memcpy(newops, operators, sizeof(operators[0]) * operator_count);
566             memcpy(&newops[operator_count-2], &operators[operator_count-1], sizeof(newops[0]));
567             memcpy(&newops[operator_count-1], &operators[operator_count-2], sizeof(newops[0]));
568             newops[operator_count-2].prec = newops[operator_count-1].prec+1;
569             operators = newops;
570         }
571     }
572
573     if (OPTS_OPTION_BOOL(OPTION_DUMP)) {
574         for (itr = 0; itr < COUNT_FLAGS; ++itr)
575             con_out("Flag %s = %i\n",    opts_flag_list[itr].name, OPTS_FLAG(itr));
576         for (itr = 0; itr < COUNT_WARNINGS; ++itr)
577             con_out("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
578
579         con_out("output             = %s\n", OPTS_OPTION_STR(OPTION_OUTPUT));
580         con_out("optimization level = %u\n", OPTS_OPTION_U32(OPTION_O));
581         con_out("standard           = %u\n", OPTS_OPTION_U32(OPTION_STANDARD));
582     }
583
584     if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
585         if (opts_output_wasset) {
586             outfile = fopen(OPTS_OPTION_STR(OPTION_OUTPUT), "wb");
587             if (!outfile) {
588                 con_err("failed to open `%s` for writing\n", OPTS_OPTION_STR(OPTION_OUTPUT));
589                 retval = 1;
590                 goto cleanup;
591             }
592         }
593         else {
594             outfile = con_default_out();
595         }
596     }
597
598     if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
599         if (!(parser = parser_create())) {
600             con_err("failed to initialize parser\n");
601             retval = 1;
602             goto cleanup;
603         }
604     }
605
606     if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
607         if (!(ftepp = ftepp_create())) {
608             con_err("failed to initialize parser\n");
609             retval = 1;
610             goto cleanup;
611         }
612     }
613
614     /* add macros */
615     if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
616         for (itr = 0; itr < vec_size(ppems); itr++) {
617             ftepp_add_macro(ftepp, ppems[itr].name, ppems[itr].value);
618             mem_d(ppems[itr].name);
619
620             /* can be null */
621             if (ppems[itr].value)
622                 mem_d(ppems[itr].value);
623         }
624     }
625
626     if (!vec_size(items)) {
627         FILE *src;
628         char      *line    = NULL;
629         size_t     linelen = 0;
630         bool       hasline = false;
631
632         progs_src = true;
633
634         src = fopen(OPTS_OPTION_STR(OPTION_PROGSRC), "rb");
635         if (!src) {
636             con_err("failed to open `%s` for reading\n", OPTS_OPTION_STR(OPTION_PROGSRC));
637             retval = 1;
638             goto cleanup;
639         }
640
641         while (progs_nextline(&line, &linelen, src)) {
642             argitem item;
643
644             if (!line[0] || (line[0] == '/' && line[1] == '/'))
645                 continue;
646
647             if (hasline) {
648                 item.filename = util_strdup(line);
649                 item.type     = TYPE_QC;
650                 vec_push(items, item);
651             } else if (!opts_output_wasset) {
652                 OPTS_OPTION_DUP(OPTION_OUTPUT) = util_strdup(line);
653                 hasline                        = true;
654             }
655         }
656
657         fclose(src);
658         mem_d(line);
659     }
660
661     if (vec_size(items)) {
662         if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
663             !OPTS_OPTION_BOOL(OPTION_PP_ONLY))
664         {
665             con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual"));
666             con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
667         }
668
669         for (itr = 0; itr < vec_size(items); ++itr) {
670             if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
671                 !OPTS_OPTION_BOOL(OPTION_PP_ONLY))
672             {
673                 con_out("  item: %s (%s)\n",
674                        items[itr].filename,
675                        ( (items[itr].type == TYPE_QC ? "qc" :
676                          (items[itr].type == TYPE_ASM ? "asm" :
677                          (items[itr].type == TYPE_SRC ? "progs.src" :
678                          ("unknown"))))));
679             }
680
681             if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
682                 const char *out;
683                 if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
684                     retval = 1;
685                     goto cleanup;
686                 }
687                 out = ftepp_get(ftepp);
688                 if (out)
689                     fprintf(outfile, "%s", out);
690                 ftepp_flush(ftepp);
691             }
692             else {
693                 if (OPTS_FLAG(FTEPP)) {
694                     const char *data;
695                     if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
696                         retval = 1;
697                         goto cleanup;
698                     }
699                     data = ftepp_get(ftepp);
700                     if (vec_size(data)) {
701                         if (!parser_compile_string(parser, items[itr].filename, data, vec_size(data))) {
702                             retval = 1;
703                             goto cleanup;
704                         }
705                     }
706                     ftepp_flush(ftepp);
707                 }
708                 else {
709                     if (!parser_compile_file(parser, items[itr].filename)) {
710                         retval = 1;
711                         goto cleanup;
712                     }
713                 }
714             }
715
716             if (progs_src) {
717                 mem_d(items[itr].filename);
718                 items[itr].filename = NULL;
719             }
720         }
721
722         ftepp_finish(ftepp);
723         ftepp = NULL;
724         if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
725             if (!parser_finish(parser, OPTS_OPTION_STR(OPTION_OUTPUT))) {
726                 retval = 1;
727                 goto cleanup;
728             }
729         }
730     }
731
732 cleanup:
733     if (ftepp)
734         ftepp_finish(ftepp);
735     con_close();
736     vec_free(items);
737     vec_free(ppems);
738
739     if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
740         if(parser) parser_cleanup(parser);
741
742     /* free allocated option strings */
743     for (itr = 0; itr < OPTION_COUNT; itr++)
744         if (OPTS_OPTION_DUPED(itr))
745             mem_d(OPTS_OPTION_STR(itr));
746
747     if (operators_free)
748         mem_d((void*)operators);
749
750     lex_cleanup();
751
752     if (!retval && compile_errors)
753         retval = 1;
754     return retval;
755 }