1 // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code.
2 // Copyright (C) 1998-2020 Marco Baye
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 #include "acme.h"
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include "alu.h"
22 #include "cliargs.h"
23 #include "config.h"
24 #include "cpu.h"
25 #include "dynabuf.h"
26 #include "encoding.h"
27 #include "flow.h"
28 #include "global.h"
29 #include "input.h"
30 #include "macro.h"
31 #include "mnemo.h"
32 #include "output.h"
33 #include "platform.h"
34 #include "pseudoopcodes.h"
35 #include "section.h"
36 #include "symbol.h"
37 #include "version.h"
38
39
40 // constants
41 static const char FILE_WRITETEXT[] = "w";
42 static const char FILE_WRITEBINARY[] = "wb";
43 // names for error messages
44 static const char name_outfile[] = "output filename";
45 static const char arg_symbollist[] = "symbol list filename";
46 static const char arg_reportfile[] = "report filename";
47 static const char arg_vicelabels[] = "VICE labels filename";
48 // long options
49 #define OPTION_HELP "help"
50 #define OPTION_FORMAT "format"
51 #define OPTION_OUTFILE "outfile"
52 #define OPTION_LABELDUMP "labeldump" // old
53 #define OPTION_SYMBOLLIST "symbollist" // new
54 #define OPTION_VICELABELS "vicelabels"
55 #define OPTION_REPORT "report"
56 #define OPTION_SETPC "setpc"
57 #define OPTION_CPU "cpu"
58 #define OPTION_INITMEM "initmem"
59 #define OPTION_MAXERRORS "maxerrors"
60 #define OPTION_MAXDEPTH "maxdepth"
61 #define OPTION_USE_STDOUT "use-stdout"
62 #define OPTION_VERSION "version"
63 #define OPTION_MSVC "msvc"
64 #define OPTION_COLOR "color"
65 #define OPTION_FULLSTOP "fullstop"
66 #define OPTION_IGNORE_ZEROES "ignore-zeroes"
67 #define OPTION_STRICT_SEGMENTS "strict-segments"
68 #define OPTION_DIALECT "dialect"
69 #define OPTION_TEST "test"
70 // options for "-W"
71 #define OPTIONWNO_LABEL_INDENT "no-label-indent"
72 #define OPTIONWNO_OLD_FOR "no-old-for"
73 #define OPTIONWNO_BIN_LEN "no-bin-len"
74 #define OPTIONWTYPE_MISMATCH "type-mismatch"
75
76
77 // variables
78 static const char **toplevel_sources;
79 static int toplevel_src_count = 0;
80 #define ILLEGAL_START_ADDRESS (-1)
81 static signed long start_address = ILLEGAL_START_ADDRESS;
82 static signed long fill_value = MEMINIT_USE_DEFAULT;
83 static const struct cpu_type *default_cpu = NULL;
84 const char *symbollist_filename = NULL;
85 const char *vicelabels_filename = NULL;
86 const char *output_filename = NULL;
87 const char *report_filename = NULL;
88 // maximum recursion depth for macro calls and "!source"
89 signed long macro_recursions_left = MAX_NESTING;
90 signed long source_recursions_left = MAX_NESTING;
91
92
93 // show release and platform info (and exit, if wanted)
show_version(int exit_after)94 static void show_version(int exit_after)
95 {
96 puts(
97 "This is ACME, release " RELEASE " (\"" CODENAME "\"), " CHANGE_DATE " " CHANGE_YEAR "\n"
98 " " PLATFORM_VERSION);
99 if (exit_after)
100 exit(EXIT_SUCCESS);
101 }
102
103
104 // show full help (headline, release/platform/version, copyright, dedication,
105 // warranty disclaimer, usage) and exit program (SUCCESS)
show_help_and_exit(void)106 static void show_help_and_exit(void)
107 {
108 puts(
109 "ACME - the ACME Crossassembler for Multiple Environments\n"
110 " Copyright (C) 1998-" CHANGE_YEAR " Marco Baye");
111 show_version(FALSE);
112 puts(
113 "ACME comes with ABSOLUTELY NO WARRANTY; for details read the help file.\n"
114 " This is free software, and you are welcome to redistribute it under\n"
115 " certain conditions; as outlined in the GNU General Public License.\n"
116 "Dedicated to the wisest being I ever had the pleasure of reading\n"
117 " books of (currently spending some time dead for tax reasons).\n"
118 "The newest version can be found at the ACME homepage:\n"
119 " " HOME_PAGE "\n"
120 "\n"
121 "Usage:\n"
122 "acme [OPTION...] [FILE]...\n"
123 "\n"
124 "Options:\n"
125 " -h, --" OPTION_HELP " show this help and exit\n"
126 " -f, --" OPTION_FORMAT " FORMAT set output file format\n"
127 " -o, --" OPTION_OUTFILE " FILE set output file name\n"
128 " -r, --" OPTION_REPORT " FILE set report file name\n"
129 " -l, --" OPTION_SYMBOLLIST " FILE set symbol list file name\n"
130 " --" OPTION_LABELDUMP " (old name for --" OPTION_SYMBOLLIST ")\n"
131 " --" OPTION_VICELABELS " FILE set file name for label dump in VICE format\n"
132 " --" OPTION_SETPC " VALUE set program counter\n"
133 " --" OPTION_CPU " CPU set target processor\n"
134 " --" OPTION_INITMEM " VALUE define 'empty' memory\n"
135 " --" OPTION_MAXERRORS " NUMBER set number of errors before exiting\n"
136 " --" OPTION_MAXDEPTH " NUMBER set recursion depth for macro calls and !src\n"
137 " --" OPTION_IGNORE_ZEROES " do not determine number size by leading zeroes\n"
138 " --" OPTION_STRICT_SEGMENTS " turn segment overlap warnings into errors\n"
139 " -vDIGIT set verbosity level\n"
140 " -DSYMBOL=VALUE define global symbol\n"
141 " -I PATH/TO/DIR add search path for input files\n"
142 // TODO: replace these:
143 " -W" OPTIONWNO_LABEL_INDENT " suppress warnings about indented labels\n"
144 " -W" OPTIONWNO_OLD_FOR " (old, use \"--dialect 0.94.8\" instead)\n"
145 " -W" OPTIONWNO_BIN_LEN " suppress warnings about lengths of binary literals\n"
146 " -W" OPTIONWTYPE_MISMATCH " enable type checking (warn about type mismatch)\n"
147 // with this line and add a separate function:
148 //" -W show warning level options\n"
149 " --" OPTION_USE_STDOUT " fix for 'Relaunch64' IDE (see docs)\n"
150 " --" OPTION_MSVC " output errors in MS VS format\n"
151 " --" OPTION_COLOR " uses ANSI color codes for error output\n"
152 " --" OPTION_FULLSTOP " use '.' as pseudo opcode prefix\n"
153 " --" OPTION_DIALECT " VERSION behave like different version\n"
154 " --" OPTION_TEST " enable experimental features\n"
155 PLATFORM_OPTION_HELP
156 " -V, --" OPTION_VERSION " show version and exit\n");
157 exit(EXIT_SUCCESS);
158 }
159
160
161 // initialise report struct
report_init(struct report * report)162 static void report_init(struct report *report)
163 {
164 report->fd = NULL;
165 report->asc_used = 0;
166 report->bin_used = 0;
167 report->last_input = NULL;
168 }
169 // open report file
report_open(struct report * report,const char * filename)170 static int report_open(struct report *report, const char *filename)
171 {
172 report->fd = fopen(filename, FILE_WRITETEXT);
173 if (report->fd == NULL) {
174 fprintf(stderr, "Error: Cannot open report file \"%s\".\n", filename);
175 return 1;
176 }
177 return 0; // success
178 }
179 // close report file
report_close(struct report * report)180 static void report_close(struct report *report)
181 {
182 if (report && report->fd) {
183 fclose(report->fd);
184 report->fd = NULL;
185 }
186 }
187
188
189 // error handling
190
191 // tidy up before exiting by saving symbol list and close other output files
ACME_finalize(int exit_code)192 int ACME_finalize(int exit_code)
193 {
194 FILE *fd;
195
196 report_close(report);
197 if (symbollist_filename) {
198 fd = fopen(symbollist_filename, FILE_WRITETEXT); // FIXME - what if filename is given via !sl in sub-dir? fix path!
199 if (fd) {
200 symbols_list(fd);
201 fclose(fd);
202 PLATFORM_SETFILETYPE_TEXT(symbollist_filename);
203 } else {
204 fprintf(stderr, "Error: Cannot open symbol list file \"%s\".\n", symbollist_filename);
205 exit_code = EXIT_FAILURE;
206 }
207 }
208 if (vicelabels_filename) {
209 fd = fopen(vicelabels_filename, FILE_WRITETEXT);
210 if (fd) {
211 symbols_vicelabels(fd);
212 fclose(fd);
213 PLATFORM_SETFILETYPE_TEXT(vicelabels_filename);
214 } else {
215 fprintf(stderr, "Error: Cannot open VICE label dump file \"%s\".\n", vicelabels_filename);
216 exit_code = EXIT_FAILURE;
217 }
218 }
219 return exit_code;
220 }
221
222
223 // save output file
save_output_file(void)224 static void save_output_file(void)
225 {
226 FILE *fd;
227
228 // if no output file chosen, tell user and do nothing
229 if (output_filename == NULL) {
230 fputs("No output file specified (use the \"-o\" option or the \"!to\" pseudo opcode).\n", stderr);
231 return;
232 }
233 fd = fopen(output_filename, FILE_WRITEBINARY); // FIXME - what if filename is given via !to in sub-dir? fix path!
234 if (fd == NULL) {
235 fprintf(stderr, "Error: Cannot open output file \"%s\".\n",
236 output_filename);
237 return;
238 }
239 Output_save_file(fd);
240 fclose(fd);
241 }
242
243
244 // increment pass number and perform a single pass
perform_pass(void)245 static void perform_pass(void)
246 {
247 FILE *fd;
248 int ii;
249
250 ++pass.number;
251 // call modules' "pass init" functions
252 Output_passinit(); // disable output, PC undefined
253 cputype_passinit(default_cpu); // set default cpu type
254 // if start address was given on command line, use it:
255 if (start_address != ILLEGAL_START_ADDRESS)
256 vcpu_set_pc(start_address, 0);
257 encoding_passinit(); // set default encoding
258 section_passinit(); // set initial zone (untitled)
259 // init variables
260 pass.undefined_count = 0;
261 //pass.needvalue_count = 0; FIXME - use
262 pass.error_count = 0;
263 // Process toplevel files
264 for (ii = 0; ii < toplevel_src_count; ++ii) {
265 if ((fd = fopen(toplevel_sources[ii], FILE_READBINARY))) {
266 flow_parse_and_close_file(fd, toplevel_sources[ii]);
267 } else {
268 fprintf(stderr, "Error: Cannot open toplevel file \"%s\".\n", toplevel_sources[ii]);
269 if (toplevel_sources[ii][0] == '-')
270 fprintf(stderr, "Options (starting with '-') must be given _before_ source files!\n");
271 ++pass.error_count;
272 }
273 }
274 Output_end_segment();
275 /* TODO:
276 if --save-start is given, parse arg string
277 if --save-limit is given, parse arg string
278 */
279 if (pass.error_count)
280 exit(ACME_finalize(EXIT_FAILURE));
281 }
282
283
284 static struct report global_report;
285 // do passes until done (or errors occurred). Return whether output is ready.
do_actual_work(void)286 static boolean do_actual_work(void)
287 {
288 int undefs_before; // number of undefined results in previous pass
289
290 report = &global_report; // let global pointer point to something
291 report_init(report); // we must init struct before doing passes
292 if (config.process_verbosity > 1)
293 puts("First pass.");
294 pass.complain_about_undefined = FALSE; // disable until error pass needed
295 pass.number = -1; // pre-init, will be incremented by perform_pass()
296 perform_pass(); // first pass
297 // pretend there has been a previous pass, with one more undefined result
298 undefs_before = pass.undefined_count + 1;
299 // keep doing passes as long as the number of undefined results keeps decreasing.
300 // stop on zero (FIXME - zero-check pass.needvalue_count instead!)
301 while (pass.undefined_count && (pass.undefined_count < undefs_before)) {
302 undefs_before = pass.undefined_count;
303 if (config.process_verbosity > 1)
304 puts("Further pass.");
305 perform_pass();
306 }
307 // any errors left?
308 if (pass.undefined_count == 0) { // FIXME - use pass.needvalue_count instead!
309 // if listing report is wanted and there were no errors,
310 // do another pass to generate listing report
311 if (report_filename) {
312 if (config.process_verbosity > 1)
313 puts("Extra pass to generate listing report.");
314 if (report_open(report, report_filename) == 0) {
315 perform_pass();
316 report_close(report);
317 }
318 }
319 return TRUE;
320 }
321 // There are still errors (unsolvable by doing further passes),
322 // so perform additional pass to find and show them.
323 if (config.process_verbosity > 1)
324 puts("Extra pass needed to find error.");
325 pass.complain_about_undefined = TRUE; // activate error output
326 perform_pass(); // perform pass, but now show "value undefined"
327 return FALSE;
328 }
329
330
331 // copy string to DynaBuf
keyword_to_dynabuf(const char keyword[])332 static void keyword_to_dynabuf(const char keyword[])
333 {
334 DYNABUF_CLEAR(GlobalDynaBuf);
335 DynaBuf_add_string(GlobalDynaBuf, keyword);
336 DynaBuf_append(GlobalDynaBuf, '\0');
337 DynaBuf_to_lower(GlobalDynaBuf, GlobalDynaBuf); // convert to lower case
338 }
339
340
341 // set output format (the output format tree must be set up at this point!)
set_output_format(const char format_name[])342 static void set_output_format(const char format_name[])
343 {
344 // caution, name may be NULL!
345 if (format_name) {
346 keyword_to_dynabuf(format_name);
347 if (!outputfile_set_format())
348 return; // ok
349
350 fputs("Error: Unknown output format.\n", stderr);
351 } else {
352 fputs("Error: No output format specified.\n", stderr);
353 }
354 fprintf(stderr, "Supported formats are:\n\n\t%s\n\n", outputfile_formats);
355 exit(EXIT_FAILURE);
356 }
357
358
359 // set CPU type (the cpu type tree must be set up at this point!)
set_starting_cpu(const char cpu_name[])360 static void set_starting_cpu(const char cpu_name[])
361 {
362 const struct cpu_type *new_cpu_type;
363
364 // caution, name may be NULL!
365 if (cpu_name) {
366 keyword_to_dynabuf(cpu_name);
367 new_cpu_type = cputype_find();
368 if (new_cpu_type) {
369 default_cpu = new_cpu_type;
370 return; // ok
371 }
372 fputs("Error: Unknown CPU type.\n", stderr);
373 } else {
374 fputs("Error: No CPU type specified.\n", stderr);
375 }
376 fprintf(stderr, "Supported types are:\n\n\t%s\n\n", cputype_names);
377 exit(EXIT_FAILURE);
378 }
379
380
could_not_parse(const char strange[])381 static void could_not_parse(const char strange[])
382 {
383 fprintf(stderr, "%sCould not parse '%s'.\n", cliargs_error, strange);
384 exit(EXIT_FAILURE);
385 }
386
387
388 // return signed long representation of string.
389 // copes with hexadecimal if prefixed with "$", "0x" or "0X".
390 // copes with octal if prefixed with "&". FIXME - add "0o" prefix?
391 // copes with binary if prefixed with "%". FIXME - add "0b" prefix!
392 // assumes decimal otherwise.
string_to_number(const char * string)393 static signed long string_to_number(const char *string)
394 {
395 signed long result;
396 char *end;
397 int base = 10;
398
399 if (*string == '%') {
400 base = 2;
401 ++string;
402 } else if (*string == '&') {
403 base = 8;
404 ++string;
405 } else if (*string == '$') {
406 base = 16;
407 ++string;
408 } else if ((*string == '0') && ((string[1] == 'x') || (string[1] == 'X'))) {
409 base = 16;
410 string += 2;
411 }
412 result = strtol(string, &end, base);
413 if (*end)
414 could_not_parse(end);
415 return result;
416 }
417
418
419 // set program counter
set_starting_pc(const char expression[])420 static void set_starting_pc(const char expression[])
421 {
422 start_address = string_to_number(expression);
423 if ((start_address > -1) && (start_address < 65536))
424 return;
425
426 fprintf(stderr, "%sProgram counter out of range (0-0xffff).\n", cliargs_error);
427 exit(EXIT_FAILURE);
428 }
429
430
431 // set initial memory contents
set_mem_contents(const char expression[])432 static void set_mem_contents(const char expression[])
433 {
434 fill_value = string_to_number(expression);
435 if ((fill_value >= -128) && (fill_value <= 255))
436 return;
437
438 fprintf(stderr, "%sInitmem value out of range (0-0xff).\n", cliargs_error);
439 exit(EXIT_FAILURE);
440 }
441
442
443 // define symbol
define_symbol(const char definition[])444 static void define_symbol(const char definition[])
445 {
446 const char *walk = definition;
447 signed long value;
448
449 // copy definition to GlobalDynaBuf until '=' reached
450 DYNABUF_CLEAR(GlobalDynaBuf);
451 while ((*walk != '=') && (*walk != '\0'))
452 DynaBuf_append(GlobalDynaBuf, *walk++);
453 if ((*walk == '\0') || (walk[1] == '\0'))
454 could_not_parse(definition);
455 value = string_to_number(walk + 1);
456 DynaBuf_append(GlobalDynaBuf, '\0');
457 symbol_define(value);
458 }
459
460
461 struct dialect {
462 enum version dialect;
463 const char *version;
464 const char *description;
465 };
466 struct dialect dialects[] = {
467 {VER_OLDEST_SUPPORTED, "0.85", "(the oldest version supported)"},
468 {VER_DEPRECATE_REALPC, "0.86", "\"!realpc\" gives a warning, \"!to\" wants a file format"},
469 // {VER_SHORTER_SETPC_WARNING, "0.93", "\"*=\" in offset assembly gives shorter warning but still switches off"},
470 {VER_RIGHTASSOCIATIVEPOWEROF, "0.94.6", "\"power of\" is now right-associative"},
471 // {VER_, "0.94.7", "empty code segments are no longer included in output file"},
472 {VER_DISABLED_OBSOLETE_STUFF, "0.94.8", "\"*=\" works inside \"!pseudopc\", disabled \"!cbm/!realpc/!subzone\""},
473 {VER_NEWFORSYNTAX, "0.94.12", "new \"!for\" syntax"},
474 // {VER_, "0.95.2", "changed ANC#8 from 0x2b to 0x0b"},
475 {VER_BACKSLASHESCAPING, "0.97", "backslash escaping and strings"},
476 // {VER_CURRENT, "default", "default"},
477 {VER_FUTURE, "future", "enable all experimental features"},
478 {0, NULL, NULL} // NULLs terminate
479 };
480
481 // choose dialect (mimic behaviour of different version)
set_dialect(const char version[])482 static void set_dialect(const char version[])
483 {
484 struct dialect *dia;
485
486 // caution, version may be NULL!
487 if (version) {
488 // scan array
489 for (dia = dialects; dia->version; ++dia) {
490 if (strcmp(version, dia->version) == 0) {
491 config.wanted_version = dia->dialect;
492 return; // found
493 }
494 }
495 fputs("Error: Unknown dialect specifier.\n", stderr);
496 } else {
497 fputs("Error: No dialect specified.\n", stderr);
498 }
499 // output table of possible versions and die
500 fputs(
501 "Supported dialects are:\n"
502 "\n"
503 "\tdialect\t\tdescription\n"
504 "\t-------\t\t-----------\n", stderr);
505 for (dia = dialects; dia->version; ++dia)
506 fprintf(stderr, "\t%s\t\t%s\n", dia->version, dia->description);
507 fputc('\n', stderr);
508 exit(EXIT_FAILURE);
509 }
510
511
512 // handle long options (like "--example"). Return unknown string.
long_option(const char * string)513 static const char *long_option(const char *string)
514 {
515 if (strcmp(string, OPTION_HELP) == 0)
516 show_help_and_exit();
517 else if (strcmp(string, OPTION_FORMAT) == 0)
518 set_output_format(cliargs_get_next()); // NULL is ok (handled like unknown)
519 else if (strcmp(string, OPTION_OUTFILE) == 0)
520 output_filename = cliargs_safe_get_next(name_outfile);
521 else if (strcmp(string, OPTION_LABELDUMP) == 0) // old
522 symbollist_filename = cliargs_safe_get_next(arg_symbollist);
523 else if (strcmp(string, OPTION_SYMBOLLIST) == 0) // new
524 symbollist_filename = cliargs_safe_get_next(arg_symbollist);
525 else if (strcmp(string, OPTION_VICELABELS) == 0)
526 vicelabels_filename = cliargs_safe_get_next(arg_vicelabels);
527 else if (strcmp(string, OPTION_REPORT) == 0)
528 report_filename = cliargs_safe_get_next(arg_reportfile);
529 else if (strcmp(string, OPTION_SETPC) == 0)
530 set_starting_pc(cliargs_safe_get_next("program counter"));
531 else if (strcmp(string, OPTION_CPU) == 0)
532 set_starting_cpu(cliargs_get_next()); // NULL is ok (handled like unknown)
533 else if (strcmp(string, OPTION_INITMEM) == 0)
534 set_mem_contents(cliargs_safe_get_next("initmem value"));
535 else if (strcmp(string, OPTION_MAXERRORS) == 0)
536 config.max_errors = string_to_number(cliargs_safe_get_next("maximum error count"));
537 else if (strcmp(string, OPTION_MAXDEPTH) == 0)
538 macro_recursions_left = (source_recursions_left = string_to_number(cliargs_safe_get_next("recursion depth")));
539 // else if (strcmp(string, "strictsyntax") == 0)
540 // strict_syntax = TRUE;
541 else if (strcmp(string, OPTION_USE_STDOUT) == 0)
542 config.msg_stream = stdout;
543 else if (strcmp(string, OPTION_MSVC) == 0)
544 config.format_msvc = TRUE;
545 else if (strcmp(string, OPTION_FULLSTOP) == 0)
546 config.pseudoop_prefix = '.';
547 else if (strcmp(string, OPTION_IGNORE_ZEROES) == 0)
548 config.honor_leading_zeroes = FALSE;
549 else if (strcmp(string, OPTION_STRICT_SEGMENTS) == 0)
550 config.segment_warning_is_error = TRUE;
551 else if (strcmp(string, OPTION_DIALECT) == 0)
552 set_dialect(cliargs_get_next()); // NULL is ok (handled like unknown)
553 else if (strcmp(string, OPTION_TEST) == 0) {
554 config.wanted_version = VER_FUTURE;
555 config.test_new_features = TRUE;
556 } PLATFORM_LONGOPTION_CODE
557 else if (strcmp(string, OPTION_COLOR) == 0)
558 config.format_color = TRUE;
559 else if (strcmp(string, OPTION_VERSION) == 0)
560 show_version(TRUE);
561 else
562 return string;
563 return NULL;
564 }
565
566
567 // handle short options (like "-e"). Return unknown character.
short_option(const char * argument)568 static char short_option(const char *argument)
569 {
570 while (*argument) {
571 switch (*argument) {
572 case 'D': // "-D" define constants
573 define_symbol(argument + 1);
574 goto done;
575 case 'f': // "-f" selects output format
576 set_output_format(cliargs_get_next()); // NULL is ok (handled like unknown)
577 break;
578 case 'h': // "-h" shows help
579 show_help_and_exit();
580 break;
581 case 'I': // "-I" adds an include directory
582 if (argument[1])
583 includepaths_add(argument + 1);
584 else
585 includepaths_add(cliargs_safe_get_next("include path"));
586 goto done;
587 case 'l': // "-l" selects symbol list filename
588 symbollist_filename = cliargs_safe_get_next(arg_symbollist);
589 break;
590 case 'o': // "-o" selects output filename
591 output_filename = cliargs_safe_get_next(name_outfile);
592 break;
593 case 'r': // "-r" selects report filename
594 report_filename = cliargs_safe_get_next(arg_reportfile);
595 break;
596 case 'v': // "-v" changes verbosity
597 ++config.process_verbosity;
598 if ((argument[1] >= '0') && (argument[1] <= '9'))
599 config.process_verbosity = *(++argument) - '0';
600 break;
601 // platform specific switches are inserted here
602 PLATFORM_SHORTOPTION_CODE
603 case 'V': // "-V" shows version
604 show_version(TRUE);
605 break;
606 case 'W': // "-W" tunes warning level
607 if (strcmp(argument + 1, OPTIONWNO_LABEL_INDENT) == 0) {
608 config.warn_on_indented_labels = FALSE;
609 goto done;
610 } else if (strcmp(argument + 1, OPTIONWNO_OLD_FOR) == 0) {
611 config.wanted_version = VER_NEWFORSYNTAX - 1;
612 goto done;
613 } else if (strcmp(argument + 1, OPTIONWNO_BIN_LEN) == 0) {
614 config.warn_bin_mask = 0;
615 goto done;
616 } else if (strcmp(argument + 1, OPTIONWTYPE_MISMATCH) == 0) {
617 config.warn_on_type_mismatch = TRUE;
618 goto done;
619 } else {
620 fprintf(stderr, "%sUnknown warning level.\n", cliargs_error);
621 exit(EXIT_FAILURE);
622 }
623 break;
624 default: // unknown ones: program termination
625 return *argument;
626 }
627 ++argument;
628 }
629 done:
630 return '\0';
631 }
632
633
634 // guess what
main(int argc,const char * argv[])635 int main(int argc, const char *argv[])
636 {
637 config_default(&config);
638 // if called without any arguments, show usage info (not full help)
639 if (argc == 1)
640 show_help_and_exit();
641 cliargs_init(argc, argv);
642 // init platform-specific stuff.
643 // this may read the library path from an environment variable.
644 PLATFORM_INIT;
645 // handle command line arguments
646 cliargs_handle_options(short_option, long_option);
647 // generate list of files to process
648 cliargs_get_rest(&toplevel_src_count, &toplevel_sources, "No top level sources given");
649 // init output buffer
650 Output_init(fill_value, config.test_new_features);
651 if (do_actual_work())
652 save_output_file();
653 return ACME_finalize(EXIT_SUCCESS); // dump labels, if wanted
654 }
655