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