BR 1091926: Bounds checking for command line parsing
[platform/upstream/nasm.git] / nasm.c
1 /* The Netwide Assembler main program module
2  *
3  * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
4  * Julian Hall. All rights reserved. The software is
5  * redistributable under the licence given in the file "Licence"
6  * distributed in the NASM archive.
7  */
8
9 #include "compiler.h"
10
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <inttypes.h>
17 #include <limits.h>
18
19 #include "nasm.h"
20 #include "nasmlib.h"
21 #include "float.h"
22 #include "stdscan.h"
23 #include "insns.h"
24 #include "preproc.h"
25 #include "parser.h"
26 #include "eval.h"
27 #include "assemble.h"
28 #include "labels.h"
29 #include "outform.h"
30 #include "listing.h"
31
32 struct forwrefinfo {            /* info held on forward refs. */
33     int lineno;
34     int operand;
35 };
36
37 static int get_bits(char *value);
38 static uint32_t get_cpu(char *cpu_str);
39 static void parse_cmdline(int, char **);
40 static void assemble_file(char *);
41 static void register_output_formats(void);
42 static void report_error_gnu(int severity, const char *fmt, ...);
43 static void report_error_vc(int severity, const char *fmt, ...);
44 static void report_error_common(int severity, const char *fmt,
45                                 va_list args);
46 static int is_suppressed_warning(int severity);
47 static void usage(void);
48 static efunc report_error;
49
50 static int using_debug_info, opt_verbose_info;
51 bool tasm_compatible_mode = false;
52 int pass0;
53 int maxbits = 0;
54 int globalrel = 0;
55
56 static char inname[FILENAME_MAX];
57 static char outname[FILENAME_MAX];
58 static char listname[FILENAME_MAX];
59 static char errname[FILENAME_MAX];
60 static int globallineno;        /* for forward-reference tracking */
61 /* static int pass = 0; */
62 static struct ofmt *ofmt = NULL;
63
64 static FILE *error_file;        /* Where to write error messages */
65
66 static FILE *ofile = NULL;
67 int optimizing = -1;            /* number of optimization passes to take */
68 static int sb, cmd_sb = 16;     /* by default */
69 static uint32_t cmd_cpu = IF_PLEVEL;       /* highest level by default */
70 static uint32_t cpu = IF_PLEVEL;   /* passed to insn_size & assemble.c */
71 bool global_offset_changed;      /* referenced in labels.c */
72
73 static struct location location;
74 int in_abs_seg;                 /* Flag we are in ABSOLUTE seg */
75 int32_t abs_seg;                   /* ABSOLUTE segment basis */
76 int32_t abs_offset;                /* ABSOLUTE offset */
77
78 static struct RAA *offsets;
79
80 static struct SAA *forwrefs;    /* keep track of forward references */
81 static const struct forwrefinfo *forwref;
82
83 static Preproc *preproc;
84 enum op_type {
85     op_normal,                  /* Preprocess and assemble */
86     op_preprocess,              /* Preprocess only */
87     op_depend,                  /* Generate dependencies */
88     op_depend_missing_ok,       /* Generate dependencies, missing OK */
89 };
90 static enum op_type operating_mode;
91
92 /*
93  * Which of the suppressible warnings are suppressed. Entry zero
94  * isn't an actual warning, but it used for -w+error/-Werror.
95  */
96 static bool suppressed[ERR_WARN_MAX+1] = {
97     true, false, true, false, false, true, false, true, true, false
98 };
99
100 /*
101  * The option names for the suppressible warnings. As before, entry
102  * zero does nothing.
103  */
104 static const char *suppressed_names[ERR_WARN_MAX+1] = {
105     "error", "macro-params", "macro-selfref", "orphan-labels",
106     "number-overflow", "gnu-elf-extensions", "float-overflow",
107     "float-denorm", "float-underflow", "float-toolong"
108 };
109
110 /*
111  * The explanations for the suppressible warnings. As before, entry
112  * zero does nothing.
113  */
114 static const char *suppressed_what[ERR_WARN_MAX+1] = {
115     "treat warnings as errors",
116     "macro calls with wrong parameter count",
117     "cyclic macro references",
118     "labels alone on lines without trailing `:'",
119     "numeric constants does not fit in 64 bits",
120     "using 8- or 16-bit relocation in ELF32, a GNU extension",
121     "floating point overflow",
122     "floating point denormal",
123     "floating point underflow",
124     "too many digits in floating-point number"
125 };
126
127 /*
128  * This is a null preprocessor which just copies lines from input
129  * to output. It's used when someone explicitly requests that NASM
130  * not preprocess their source file.
131  */
132
133 static void no_pp_reset(char *, int, efunc, evalfunc, ListGen *);
134 static char *no_pp_getline(void);
135 static void no_pp_cleanup(int);
136 static Preproc no_pp = {
137     no_pp_reset,
138     no_pp_getline,
139     no_pp_cleanup
140 };
141
142 /*
143  * get/set current offset...
144  */
145 #define GET_CURR_OFFS (in_abs_seg?abs_offset:\
146                       raa_read(offsets,location.segment))
147 #define SET_CURR_OFFS(x) (in_abs_seg?(void)(abs_offset=(x)):\
148                          (void)(offsets=raa_write(offsets,location.segment,(x))))
149
150 static int want_usage;
151 static int terminate_after_phase;
152 int user_nolist = 0;            /* fbk 9/2/00 */
153
154 static void nasm_fputs(const char *line, FILE * outfile)
155 {
156     if (outfile) {
157         fputs(line, outfile);
158         putc('\n', outfile);
159     } else
160         puts(line);
161 }
162
163 int main(int argc, char **argv)
164 {
165     pass0 = 1;
166     want_usage = terminate_after_phase = false;
167     report_error = report_error_gnu;
168
169     error_file = stderr;
170
171     nasm_set_malloc_error(report_error);
172     offsets = raa_init();
173     forwrefs = saa_init((int32_t)sizeof(struct forwrefinfo));
174
175     preproc = &nasmpp;
176     operating_mode = op_normal;
177
178     seg_init();
179
180     register_output_formats();
181
182     parse_cmdline(argc, argv);
183
184     if (terminate_after_phase) {
185         if (want_usage)
186             usage();
187         return 1;
188     }
189
190     /* If debugging info is disabled, suppress any debug calls */
191     if (!using_debug_info)
192         ofmt->current_dfmt = &null_debug_form;
193
194     if (ofmt->stdmac)
195         pp_extra_stdmac(ofmt->stdmac);
196     parser_global_info(ofmt, &location);
197     eval_global_info(ofmt, lookup_label, &location);
198
199     /* define some macros dependent of command-line */
200     {
201         char temp[64];
202         snprintf(temp, sizeof(temp), "__OUTPUT_FORMAT__=%s\n",
203                  ofmt->shortname);
204         pp_pre_define(temp);
205     }
206
207     switch (operating_mode) {
208     case op_depend_missing_ok:
209         pp_include_path(NULL);  /* "assume generated" */
210         /* fall through */
211     case op_depend:
212         {
213             char *line;
214             preproc->reset(inname, 0, report_error, evaluate, &nasmlist);
215             if (outname[0] == '\0')
216                 ofmt->filename(inname, outname, report_error);
217             ofile = NULL;
218             fprintf(stdout, "%s: %s", outname, inname);
219             while ((line = preproc->getline()))
220                 nasm_free(line);
221             preproc->cleanup(0);
222             putc('\n', stdout);
223         }
224         break;
225
226     case op_preprocess:
227         {
228             char *line;
229             char *file_name = NULL;
230             int32_t prior_linnum = 0;
231             int lineinc = 0;
232
233             if (*outname) {
234                 ofile = fopen(outname, "w");
235                 if (!ofile)
236                     report_error(ERR_FATAL | ERR_NOFILE,
237                                  "unable to open output file `%s'",
238                                  outname);
239             } else
240                 ofile = NULL;
241
242             location.known = false;
243
244 /*      pass = 1; */
245             preproc->reset(inname, 2, report_error, evaluate, &nasmlist);
246             while ((line = preproc->getline())) {
247                 /*
248                  * We generate %line directives if needed for later programs
249                  */
250                 int32_t linnum = prior_linnum += lineinc;
251                 int altline = src_get(&linnum, &file_name);
252                 if (altline) {
253                     if (altline == 1 && lineinc == 1)
254                         nasm_fputs("", ofile);
255                     else {
256                         lineinc = (altline != -1 || lineinc != 1);
257                         fprintf(ofile ? ofile : stdout,
258                                 "%%line %"PRId32"+%d %s\n", linnum, lineinc,
259                                 file_name);
260                     }
261                     prior_linnum = linnum;
262                 }
263                 nasm_fputs(line, ofile);
264                 nasm_free(line);
265             }
266             nasm_free(file_name);
267             preproc->cleanup(0);
268             if (ofile)
269                 fclose(ofile);
270             if (ofile && terminate_after_phase)
271                 remove(outname);
272         }
273         break;
274
275     case op_normal:
276         {
277             /*
278              * We must call ofmt->filename _anyway_, even if the user
279              * has specified their own output file, because some
280              * formats (eg OBJ and COFF) use ofmt->filename to find out
281              * the name of the input file and then put that inside the
282              * file.
283              */
284             ofmt->filename(inname, outname, report_error);
285
286             ofile = fopen(outname, "wb");
287             if (!ofile) {
288                 report_error(ERR_FATAL | ERR_NOFILE,
289                              "unable to open output file `%s'", outname);
290             }
291
292             /*
293              * We must call init_labels() before ofmt->init() since
294              * some object formats will want to define labels in their
295              * init routines. (eg OS/2 defines the FLAT group)
296              */
297             init_labels();
298
299             ofmt->init(ofile, report_error, define_label, evaluate);
300
301             assemble_file(inname);
302
303             if (!terminate_after_phase) {
304                 ofmt->cleanup(using_debug_info);
305                 cleanup_labels();
306             } else {
307                 /*
308                  * We had an fclose on the output file here, but we
309                  * actually do that in all the object file drivers as well,
310                  * so we're leaving out the one here.
311                  *     fclose (ofile);
312                  */
313                 remove(outname);
314                 if (listname[0])
315                     remove(listname);
316             }
317         }
318         break;
319     }
320
321     if (want_usage)
322         usage();
323
324     raa_free(offsets);
325     saa_free(forwrefs);
326     eval_cleanup();
327     stdscan_cleanup();
328
329     if (terminate_after_phase)
330         return 1;
331     else
332         return 0;
333 }
334
335 /*
336  * Get a parameter for a command line option.
337  * First arg must be in the form of e.g. -f...
338  */
339 static char *get_param(char *p, char *q, bool *advance)
340 {
341     *advance = false;
342     if (p[2]) {                 /* the parameter's in the option */
343         p += 2;
344         while (isspace(*p))
345             p++;
346         return p;
347     }
348     if (q && q[0]) {
349         *advance = true;
350         return q;
351     }
352     report_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
353                  "option `-%c' requires an argument", p[1]);
354     return NULL;
355 }
356
357 /*
358  * Copy a filename
359  */
360 static void copy_filename(char *dst, const char *src)
361 {
362     size_t len = strlen(src);
363
364     if (len >= (size_t)FILENAME_MAX) {
365         report_error(ERR_FATAL | ERR_NOFILE, "file name too long");
366         return;
367     }
368     strncpy(dst, src, FILENAME_MAX);
369 }
370
371 struct textargs {
372     const char *label;
373     int value;
374 };
375
376 #define OPT_PREFIX 0
377 #define OPT_POSTFIX 1
378 struct textargs textopts[] = {
379     {"prefix", OPT_PREFIX},
380     {"postfix", OPT_POSTFIX},
381     {NULL, 0}
382 };
383
384 static bool stopoptions = false;
385 static bool process_arg(char *p, char *q)
386 {
387     char *param;
388     int i;
389     bool advance = false;
390     bool suppress;
391
392     if (!p || !p[0])
393         return false;
394
395     if (p[0] == '-' && !stopoptions) {
396         if (strchr("oOfpPdDiIlFXuUZwW", p[1])) {
397             /* These parameters take values */
398             if (!(param = get_param(p, q, &advance)))
399                 return advance;
400         }
401
402         switch (p[1]) {
403         case 's':
404             error_file = stdout;
405             break;
406
407         case 'o':               /* output file */
408             copy_filename(outname, param);
409             break;
410
411         case 'f':               /* output format */
412             ofmt = ofmt_find(param);
413             if (!ofmt) {
414                 report_error(ERR_FATAL | ERR_NOFILE | ERR_USAGE,
415                              "unrecognised output format `%s' - "
416                              "use -hf for a list", param);
417             } else {
418                 ofmt->current_dfmt = ofmt->debug_formats[0];
419             }
420             break;
421
422         case 'O':               /* Optimization level */
423         {
424             int opt;
425             
426             if (!*param) {
427                 /* Naked -O == -Ox */
428                 optimizing = INT_MAX >> 1; /* Almost unlimited */
429             } else {
430                 while (*param) {
431                     switch (*param) {
432                     case '0': case '1': case '2': case '3': case '4':
433                     case '5': case '6': case '7': case '8': case '9':
434                         opt = strtoul(param, &param, 10);
435                         
436                         /* -O0 -> optimizing == -1, 0.98 behaviour */
437                         /* -O1 -> optimizing == 0, 0.98.09 behaviour */
438                         if (opt < 2)
439                             optimizing = opt - 1;
440                         else if (opt <= 5)
441                             /* The optimizer seems to have problems with
442                                < 5 passes?  Hidden bug? */
443                             optimizing = 5;     /* 5 passes */
444                         else
445                             optimizing = opt;   /* More than 5 passes */
446                         break;
447                         
448                     case 'v':
449                     case '+':
450                         param++;
451                         opt_verbose_info = true;
452                         break;
453                         
454                     case 'x':
455                         param++;
456                         optimizing = INT_MAX >> 1; /* Almost unlimited */
457                         break;
458                         
459                     default:
460                         report_error(ERR_FATAL,
461                                      "unknown optimization option -O%c\n",
462                                      *param);
463                         break;
464                     }
465                 }
466             }
467             break;
468         }
469
470         case 'p':                       /* pre-include */
471         case 'P':
472             pp_pre_include(param);
473             break;
474
475         case 'd':                       /* pre-define */
476         case 'D':
477             pp_pre_define(param);
478             break;
479
480         case 'u':                       /* un-define */
481         case 'U':
482             pp_pre_undefine(param);
483             break;
484
485         case 'i':                       /* include search path */
486         case 'I':
487             pp_include_path(param);
488             break;
489
490         case 'l':                       /* listing file */
491             copy_filename(listname, param);
492             break;
493
494         case 'Z':                       /* error messages file */
495             strcpy(errname, param);
496             break;
497
498         case 'F':                       /* specify debug format */
499             ofmt->current_dfmt = dfmt_find(ofmt, param);
500             if (!ofmt->current_dfmt) {
501                 report_error(ERR_FATAL | ERR_NOFILE | ERR_USAGE,
502                              "unrecognized debug format `%s' for"
503                              " output format `%s'",
504                              param, ofmt->shortname);
505             }
506             break;
507
508         case 'X':               /* specify error reporting format */
509             if (nasm_stricmp("vc", param) == 0)
510                 report_error = report_error_vc;
511             else if (nasm_stricmp("gnu", param) == 0)
512                 report_error = report_error_gnu;
513             else
514                 report_error(ERR_FATAL | ERR_NOFILE | ERR_USAGE,
515                              "unrecognized error reporting format `%s'",
516                              param);
517             break;
518
519         case 'g':
520             using_debug_info = true;
521             break;
522
523         case 'h':
524             printf
525                 ("usage: nasm [-@ response file] [-o outfile] [-f format] "
526                  "[-l listfile]\n"
527                  "            [options...] [--] filename\n"
528                  "    or nasm -v   for version info\n\n"
529                  "    -t          assemble in SciTech TASM compatible mode\n"
530                  "    -g          generate debug information in selected format.\n");
531             printf
532                 ("    -E (or -e)  preprocess only (writes output to stdout by default)\n"
533                  "    -a          don't preprocess (assemble only)\n"
534                  "    -M          generate Makefile dependencies on stdout\n"
535                  "    -MG         d:o, missing files assumed generated\n\n"
536                  "    -Z<file>    redirect error messages to file\n"
537                  "    -s          redirect error messages to stdout\n\n"
538                  "    -F format   select a debugging format\n\n"
539                  "    -I<path>    adds a pathname to the include file path\n");
540             printf
541                 ("    -O<digit>   optimize branch offsets (-O0 disables, default)\n"
542                  "    -P<file>    pre-includes a file\n"
543                  "    -D<macro>[=<value>] pre-defines a macro\n"
544                  "    -U<macro>   undefines a macro\n"
545                  "    -X<format>  specifies error reporting format (gnu or vc)\n"
546                  "    -w+foo      enables warning foo (equiv. -Wfoo)\n"
547                  "    -w-foo      disable warning foo (equiv. -Wno-foo)\n"
548                  "Warnings:\n");
549             for (i = 0; i <= ERR_WARN_MAX; i++)
550                 printf("    %-23s %s (default %s)\n",
551                        suppressed_names[i], suppressed_what[i],
552                        suppressed[i] ? "off" : "on");
553             printf
554                 ("\nresponse files should contain command line parameters"
555                  ", one per line.\n");
556             if (p[2] == 'f') {
557                 printf("\nvalid output formats for -f are"
558                        " (`*' denotes default):\n");
559                 ofmt_list(ofmt, stdout);
560             } else {
561                 printf("\nFor a list of valid output formats, use -hf.\n");
562                 printf("For a list of debug formats, use -f <form> -y.\n");
563             }
564             exit(0);            /* never need usage message here */
565             break;
566
567         case 'y':
568             printf("\nvalid debug formats for '%s' output format are"
569                    " ('*' denotes default):\n", ofmt->shortname);
570             dfmt_list(ofmt, stdout);
571             exit(0);
572             break;
573
574         case 't':
575             tasm_compatible_mode = true;
576             break;
577
578         case 'v':
579             {
580                 const char *nasm_version_string =
581                     "NASM version " NASM_VER " compiled on " __DATE__
582 #ifdef DEBUG
583                     " with -DDEBUG"
584 #endif
585                     ;
586                 puts(nasm_version_string);
587                 exit(0);        /* never need usage message here */
588             }
589             break;
590
591         case 'e':              /* preprocess only */
592         case 'E':
593             operating_mode = op_preprocess;
594             break;
595
596         case 'a':              /* assemble only - don't preprocess */
597             preproc = &no_pp;
598             break;
599
600         case 'W':
601             if (param[0] == 'n' && param[1] == 'o' && param[2] == '-') {
602                 suppress = true;
603                 param += 3;
604             } else {
605                 suppress = false;
606             }
607             goto set_warning;
608
609         case 'w':
610             if (param[0] != '+' && param[0] != '-') {
611                 report_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
612                              "invalid option to `-w'");
613                 break;
614             }
615             suppress = (param[0] == '-');
616             param++;
617             goto set_warning;
618         set_warning:
619             for (i = 0; i <= ERR_WARN_MAX; i++)
620                 if (!nasm_stricmp(param, suppressed_names[i]))
621                     break;
622             if (i <= ERR_WARN_MAX)
623                 suppressed[i] = suppress;
624             else if (!nasm_stricmp(param, "all"))
625                 for (i = 1; i <= ERR_WARN_MAX; i++)
626                     suppressed[i] = suppress;
627             else if (!nasm_stricmp(param, "none"))
628                 for (i = 1; i <= ERR_WARN_MAX; i++)
629                     suppressed[i] = !suppress;
630             else
631                 report_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
632                              "invalid warning `%s'", param);
633             break;
634
635         case 'M':
636             operating_mode = p[2] == 'G' ? op_depend_missing_ok : op_depend;
637             break;
638
639         case '-':
640             {
641                 int s;
642
643                 if (p[2] == 0) {        /* -- => stop processing options */
644                     stopoptions = 1;
645                     break;
646                 }
647                 for (s = 0; textopts[s].label; s++) {
648                     if (!nasm_stricmp(p + 2, textopts[s].label)) {
649                         break;
650                     }
651                 }
652
653                 switch (s) {
654
655                 case OPT_PREFIX:
656                 case OPT_POSTFIX:
657                     {
658                         if (!q) {
659                             report_error(ERR_NONFATAL | ERR_NOFILE |
660                                          ERR_USAGE,
661                                          "option `--%s' requires an argument",
662                                          p + 2);
663                             break;
664                         } else {
665                             advance = 1, param = q;
666                         }
667
668                         if (s == OPT_PREFIX) {
669                             strncpy(lprefix, param, PREFIX_MAX - 1);
670                             lprefix[PREFIX_MAX - 1] = 0;
671                             break;
672                         }
673                         if (s == OPT_POSTFIX) {
674                             strncpy(lpostfix, param, POSTFIX_MAX - 1);
675                             lpostfix[POSTFIX_MAX - 1] = 0;
676                             break;
677                         }
678                         break;
679                     }
680                 default:
681                     {
682                         report_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
683                                      "unrecognised option `--%s'", p + 2);
684                         break;
685                     }
686                 }
687                 break;
688             }
689
690         default:
691             if (!ofmt->setinfo(GI_SWITCH, &p))
692                 report_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
693                              "unrecognised option `-%c'", p[1]);
694             break;
695         }
696     } else {
697         if (*inname) {
698             report_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
699                          "more than one input file specified");
700         } else {
701             copy_filename(inname, p);
702         }
703     }
704
705     return advance;
706 }
707
708 #define ARG_BUF_DELTA 128
709
710 static void process_respfile(FILE * rfile)
711 {
712     char *buffer, *p, *q, *prevarg;
713     int bufsize, prevargsize;
714
715     bufsize = prevargsize = ARG_BUF_DELTA;
716     buffer = nasm_malloc(ARG_BUF_DELTA);
717     prevarg = nasm_malloc(ARG_BUF_DELTA);
718     prevarg[0] = '\0';
719
720     while (1) {                 /* Loop to handle all lines in file */
721         p = buffer;
722         while (1) {             /* Loop to handle long lines */
723             q = fgets(p, bufsize - (p - buffer), rfile);
724             if (!q)
725                 break;
726             p += strlen(p);
727             if (p > buffer && p[-1] == '\n')
728                 break;
729             if (p - buffer > bufsize - 10) {
730                 int offset;
731                 offset = p - buffer;
732                 bufsize += ARG_BUF_DELTA;
733                 buffer = nasm_realloc(buffer, bufsize);
734                 p = buffer + offset;
735             }
736         }
737
738         if (!q && p == buffer) {
739             if (prevarg[0])
740                 process_arg(prevarg, NULL);
741             nasm_free(buffer);
742             nasm_free(prevarg);
743             return;
744         }
745
746         /*
747          * Play safe: remove CRs, LFs and any spurious ^Zs, if any of
748          * them are present at the end of the line.
749          */
750         *(p = &buffer[strcspn(buffer, "\r\n\032")]) = '\0';
751
752         while (p > buffer && isspace(p[-1]))
753             *--p = '\0';
754
755         p = buffer;
756         while (isspace(*p))
757             p++;
758
759         if (process_arg(prevarg, p))
760             *p = '\0';
761
762         if ((int) strlen(p) > prevargsize - 10) {
763             prevargsize += ARG_BUF_DELTA;
764             prevarg = nasm_realloc(prevarg, prevargsize);
765         }
766         strncpy(prevarg, p, prevargsize);
767     }
768 }
769
770 /* Function to process args from a string of args, rather than the
771  * argv array. Used by the environment variable and response file
772  * processing.
773  */
774 static void process_args(char *args)
775 {
776     char *p, *q, *arg, *prevarg;
777     char separator = ' ';
778
779     p = args;
780     if (*p && *p != '-')
781         separator = *p++;
782     arg = NULL;
783     while (*p) {
784         q = p;
785         while (*p && *p != separator)
786             p++;
787         while (*p == separator)
788             *p++ = '\0';
789         prevarg = arg;
790         arg = q;
791         if (process_arg(prevarg, arg))
792             arg = NULL;
793     }
794     if (arg)
795         process_arg(arg, NULL);
796 }
797
798 static void parse_cmdline(int argc, char **argv)
799 {
800     FILE *rfile;
801     char *envreal, *envcopy = NULL, *p, *arg;
802
803     *inname = *outname = *listname = *errname = '\0';
804
805     /*
806      * First, process the NASMENV environment variable.
807      */
808     envreal = getenv("NASMENV");
809     arg = NULL;
810     if (envreal) {
811         envcopy = nasm_strdup(envreal);
812         process_args(envcopy);
813         nasm_free(envcopy);
814     }
815
816     /*
817      * Now process the actual command line.
818      */
819     while (--argc) {
820         bool advance;
821         argv++;
822         if (argv[0][0] == '@') {
823             /* We have a response file, so process this as a set of
824              * arguments like the environment variable. This allows us
825              * to have multiple arguments on a single line, which is
826              * different to the -@resp file processing below for regular
827              * NASM.
828              */
829             char *str = malloc(2048);
830             FILE *f = fopen(&argv[0][1], "r");
831             if (!str) {
832                 printf("out of memory");
833                 exit(-1);
834             }
835             if (f) {
836                 while (fgets(str, 2048, f)) {
837                     process_args(str);
838                 }
839                 fclose(f);
840             }
841             free(str);
842             argc--;
843             argv++;
844         }
845         if (!stopoptions && argv[0][0] == '-' && argv[0][1] == '@') {
846             p = get_param(argv[0], argc > 1 ? argv[1] : NULL, &advance);
847             if (p) {
848                 rfile = fopen(p, "r");
849                 if (rfile) {
850                     process_respfile(rfile);
851                     fclose(rfile);
852                 } else
853                     report_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
854                                  "unable to open response file `%s'", p);
855             }
856         } else
857             advance = process_arg(argv[0], argc > 1 ? argv[1] : NULL);
858         argv += advance, argc -= advance;
859     }
860
861     if (!*inname)
862         report_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
863                      "no input file specified");
864
865     /* Look for basic command line typos.  This definitely doesn't
866        catch all errors, but it might help cases of fumbled fingers. */
867     if (!strcmp(inname, errname) || !strcmp(inname, outname) ||
868         !strcmp(inname, listname))
869         report_error(ERR_FATAL | ERR_NOFILE | ERR_USAGE,
870                      "file `%s' is both input and output file",
871                      inname);
872
873     if (*errname) {
874         error_file = fopen(errname, "w");
875         if (!error_file) {
876             error_file = stderr;        /* Revert to default! */
877             report_error(ERR_FATAL | ERR_NOFILE | ERR_USAGE,
878                          "cannot open file `%s' for error messages",
879                          errname);
880         }
881     }
882 }
883
884 /* List of directives */
885 enum directives {
886     D_NONE, D_ABSOLUTE, D_BITS, D_COMMON, D_CPU, D_DEBUG, D_DEFAULT,
887     D_EXTERN, D_FLOAT, D_GLOBAL, D_LIST, D_SECTION, D_SEGMENT, D_WARNING
888 };
889 static const char *directives[] = {
890     "", "absolute", "bits", "common", "cpu", "debug", "default",
891     "extern", "float", "global", "list", "section", "segment", "warning"
892 };
893 static enum directives getkw(char **directive, char **value);
894
895 static void assemble_file(char *fname)
896 {
897     char *directive, *value, *p, *q, *special, *line, debugid[80];
898     insn output_ins;
899     int i, validid;
900     bool rn_error;
901     int32_t seg;
902     int64_t offs;
903     struct tokenval tokval;
904     expr *e;
905     int pass, pass_max;
906     int pass_cnt = 0;           /* count actual passes */
907
908     if (cmd_sb == 32 && cmd_cpu < IF_386)
909         report_error(ERR_FATAL, "command line: "
910                      "32-bit segment size requires a higher cpu");
911
912     pass_max = (optimizing > 0 ? optimizing : 0) + 2;   /* passes 1, optimizing, then 2 */
913     pass0 = !(optimizing > 0);  /* start at 1 if not optimizing */
914     for (pass = 1; pass <= pass_max && pass0 <= 2; pass++) {
915         int pass1, pass2;
916         ldfunc def_label;
917
918         pass1 = pass < pass_max ? 1 : 2;        /* seq is 1, 1, 1,..., 1, 2 */
919         pass2 = pass > 1 ? 2 : 1;       /* seq is 1, 2, 2,..., 2, 2 */
920         /*      pass0                            seq is 0, 0, 0,..., 1, 2 */
921
922         def_label = pass > 1 ? redefine_label : define_label;
923
924         globalbits = sb = cmd_sb;   /* set 'bits' to command line default */
925         cpu = cmd_cpu;
926         if (pass0 == 2) {
927             if (*listname)
928                 nasmlist.init(listname, report_error);
929         }
930         in_abs_seg = false;
931         global_offset_changed = false;  /* set by redefine_label */
932         location.segment = ofmt->section(NULL, pass2, &sb);
933         globalbits = sb;
934         if (pass > 1) {
935             saa_rewind(forwrefs);
936             forwref = saa_rstruct(forwrefs);
937             raa_free(offsets);
938             offsets = raa_init();
939         }
940         preproc->reset(fname, pass1, report_error, evaluate, &nasmlist);
941         globallineno = 0;
942         if (pass == 1)
943             location.known = true;
944         location.offset = offs = GET_CURR_OFFS;
945
946         while ((line = preproc->getline())) {
947             enum directives d;
948             globallineno++;
949
950             /* here we parse our directives; this is not handled by the 'real'
951              * parser. */
952             directive = line;
953             d = getkw(&directive, &value);
954             if (d) {
955                 int err = 0;
956
957                 switch (d) {
958                 case D_SEGMENT:         /* [SEGMENT n] */
959                 case D_SECTION:
960                     seg = ofmt->section(value, pass2, &sb);
961                     if (seg == NO_SEG) {
962                         report_error(pass1 == 1 ? ERR_NONFATAL : ERR_PANIC,
963                                      "segment name `%s' not recognized",
964                                      value);
965                     } else {
966                         in_abs_seg = false;
967                         location.segment = seg;
968                     }
969                     break;
970                 case D_EXTERN:          /* [EXTERN label:special] */
971                     if (*value == '$')
972                         value++;        /* skip initial $ if present */
973                     if (pass0 == 2) {
974                         q = value;
975                         while (*q && *q != ':')
976                             q++;
977                         if (*q == ':') {
978                             *q++ = '\0';
979                             ofmt->symdef(value, 0L, 0L, 3, q);
980                         }
981                     } else if (pass == 1) {     /* pass == 1 */
982                         q = value;
983                         validid = true;
984                         if (!isidstart(*q))
985                             validid = false;
986                         while (*q && *q != ':') {
987                             if (!isidchar(*q))
988                                 validid = false;
989                             q++;
990                         }
991                         if (!validid) {
992                             report_error(ERR_NONFATAL,
993                                          "identifier expected after EXTERN");
994                             break;
995                         }
996                         if (*q == ':') {
997                             *q++ = '\0';
998                             special = q;
999                         } else
1000                             special = NULL;
1001                         if (!is_extern(value)) {        /* allow re-EXTERN to be ignored */
1002                             int temp = pass0;
1003                             pass0 = 1;  /* fake pass 1 in labels.c */
1004                             declare_as_global(value, special,
1005                                               report_error);
1006                             define_label(value, seg_alloc(), 0L, NULL,
1007                                          false, true, ofmt, report_error);
1008                             pass0 = temp;
1009                         }
1010                     }           /* else  pass0 == 1 */
1011                     break;
1012                 case D_BITS:            /* [BITS bits] */
1013                     globalbits = sb = get_bits(value);
1014                     break;
1015                 case D_GLOBAL:          /* [GLOBAL symbol:special] */
1016                     if (*value == '$')
1017                         value++;        /* skip initial $ if present */
1018                     if (pass0 == 2) {   /* pass 2 */
1019                         q = value;
1020                         while (*q && *q != ':')
1021                             q++;
1022                         if (*q == ':') {
1023                             *q++ = '\0';
1024                             ofmt->symdef(value, 0L, 0L, 3, q);
1025                         }
1026                     } else if (pass2 == 1) {    /* pass == 1 */
1027                         q = value;
1028                         validid = true;
1029                         if (!isidstart(*q))
1030                             validid = false;
1031                         while (*q && *q != ':') {
1032                             if (!isidchar(*q))
1033                                 validid = false;
1034                             q++;
1035                         }
1036                         if (!validid) {
1037                             report_error(ERR_NONFATAL,
1038                                          "identifier expected after GLOBAL");
1039                             break;
1040                         }
1041                         if (*q == ':') {
1042                             *q++ = '\0';
1043                             special = q;
1044                         } else
1045                             special = NULL;
1046                         declare_as_global(value, special, report_error);
1047                     }           /* pass == 1 */
1048                     break;
1049                 case D_COMMON:          /* [COMMON symbol size:special] */
1050                     if (*value == '$')
1051                         value++;        /* skip initial $ if present */
1052                     if (pass0 == 1) {
1053                         p = value;
1054                         validid = true;
1055                         if (!isidstart(*p))
1056                             validid = false;
1057                         while (*p && !isspace(*p)) {
1058                             if (!isidchar(*p))
1059                                 validid = false;
1060                             p++;
1061                         }
1062                         if (!validid) {
1063                             report_error(ERR_NONFATAL,
1064                                          "identifier expected after COMMON");
1065                             break;
1066                         }
1067                         if (*p) {
1068                             int64_t size;
1069
1070                             while (*p && isspace(*p))
1071                                 *p++ = '\0';
1072                             q = p;
1073                             while (*q && *q != ':')
1074                                 q++;
1075                             if (*q == ':') {
1076                                 *q++ = '\0';
1077                                 special = q;
1078                             } else
1079                                 special = NULL;
1080                             size = readnum(p, &rn_error);
1081                             if (rn_error)
1082                                 report_error(ERR_NONFATAL,
1083                                              "invalid size specified"
1084                                              " in COMMON declaration");
1085                             else
1086                                 define_common(value, seg_alloc(), size,
1087                                               special, ofmt, report_error);
1088                         } else
1089                             report_error(ERR_NONFATAL,
1090                                          "no size specified in"
1091                                          " COMMON declaration");
1092                     } else if (pass0 == 2) {    /* pass == 2 */
1093                         q = value;
1094                         while (*q && *q != ':') {
1095                             if (isspace(*q))
1096                                 *q = '\0';
1097                             q++;
1098                         }
1099                         if (*q == ':') {
1100                             *q++ = '\0';
1101                             ofmt->symdef(value, 0L, 0L, 3, q);
1102                         }
1103                     }
1104                     break;
1105                 case D_ABSOLUTE:                /* [ABSOLUTE address] */
1106                     stdscan_reset();
1107                     stdscan_bufptr = value;
1108                     tokval.t_type = TOKEN_INVALID;
1109                     e = evaluate(stdscan, NULL, &tokval, NULL, pass2,
1110                                  report_error, NULL);
1111                     if (e) {
1112                         if (!is_reloc(e))
1113                             report_error(pass0 ==
1114                                          1 ? ERR_NONFATAL : ERR_PANIC,
1115                                          "cannot use non-relocatable expression as "
1116                                          "ABSOLUTE address");
1117                         else {
1118                             abs_seg = reloc_seg(e);
1119                             abs_offset = reloc_value(e);
1120                         }
1121                     } else if (pass == 1)
1122                         abs_offset = 0x100;     /* don't go near zero in case of / */
1123                     else
1124                         report_error(ERR_PANIC, "invalid ABSOLUTE address "
1125                                      "in pass two");
1126                     in_abs_seg = true;
1127                     location.segment = NO_SEG;
1128                     break;
1129                 case D_DEBUG:           /* [DEBUG] */
1130                     p = value;
1131                     q = debugid;
1132                     validid = true;
1133                     if (!isidstart(*p))
1134                         validid = false;
1135                     while (*p && !isspace(*p)) {
1136                         if (!isidchar(*p))
1137                             validid = false;
1138                         *q++ = *p++;
1139                     }
1140                     *q++ = 0;
1141                     if (!validid) {
1142                         report_error(pass == 1 ? ERR_NONFATAL : ERR_PANIC,
1143                                      "identifier expected after DEBUG");
1144                         break;
1145                     }
1146                     while (*p && isspace(*p))
1147                         p++;
1148                     if (pass == pass_max)
1149                         ofmt->current_dfmt->debug_directive(debugid, p);
1150                     break;
1151                 case D_WARNING:         /* [WARNING {+|-}warn-name] */
1152                     if (pass1 == 1) {
1153                         while (*value && isspace(*value))
1154                             value++;
1155
1156                         if (*value == '+' || *value == '-') {
1157                             validid = (*value == '-') ? true : false;
1158                             value++;
1159                         } else
1160                             validid = false;
1161
1162                         for (i = 1; i <= ERR_WARN_MAX; i++)
1163                             if (!nasm_stricmp(value, suppressed_names[i]))
1164                                 break;
1165                         if (i <= ERR_WARN_MAX)
1166                             suppressed[i] = validid;
1167                         else
1168                             report_error(ERR_NONFATAL,
1169                                          "invalid warning id in WARNING directive");
1170                     }
1171                     break;
1172                 case D_CPU:             /* [CPU] */
1173                     cpu = get_cpu(value);
1174                     break;
1175                 case D_LIST:            /* [LIST {+|-}] */
1176                     while (*value && isspace(*value))
1177                         value++;
1178
1179                     if (*value == '+') {
1180                         user_nolist = 0;
1181                     } else {
1182                         if (*value == '-') {
1183                             user_nolist = 1;
1184                         } else {
1185                             err = 1;
1186                         }
1187                     }
1188                     break;
1189                 case D_DEFAULT:         /* [DEFAULT] */
1190                     stdscan_reset();
1191                     stdscan_bufptr = value;
1192                     tokval.t_type = TOKEN_INVALID;
1193                     if (stdscan(NULL, &tokval) == TOKEN_SPECIAL) {
1194                         switch ((int)tokval.t_integer) {
1195                         case S_REL:
1196                             globalrel = 1;
1197                             break;
1198                         case S_ABS:
1199                             globalrel = 0;
1200                             break;
1201                         default:
1202                             err = 1;
1203                             break;
1204                         }
1205                     } else {
1206                         err = 1;
1207                     }
1208                     break;
1209                 case D_FLOAT:
1210                     if (float_option(value)) {
1211                         report_error(pass1 == 1 ? ERR_NONFATAL : ERR_PANIC,
1212                                      "unknown 'float' directive: %s",
1213                                      value);
1214                     }
1215                     break;
1216                 default:
1217                     if (!ofmt->directive(directive, value, pass2))
1218                         report_error(pass1 == 1 ? ERR_NONFATAL : ERR_PANIC,
1219                                      "unrecognised directive [%s]",
1220                                      directive);
1221                 }
1222                 if (err) {
1223                     report_error(ERR_NONFATAL,
1224                                  "invalid parameter to [%s] directive",
1225                                  directive);
1226                 }
1227             } else {            /* it isn't a directive */
1228
1229                 parse_line(pass1, line, &output_ins,
1230                            report_error, evaluate, def_label);
1231
1232                 if (!(optimizing > 0) && pass == 2) {
1233                     if (forwref != NULL && globallineno == forwref->lineno) {
1234                         output_ins.forw_ref = true;
1235                         do {
1236                             output_ins.oprs[forwref->operand].opflags |=
1237                                 OPFLAG_FORWARD;
1238                             forwref = saa_rstruct(forwrefs);
1239                         } while (forwref != NULL
1240                                  && forwref->lineno == globallineno);
1241                     } else
1242                         output_ins.forw_ref = false;
1243                 }
1244
1245                 if (!(optimizing > 0) && output_ins.forw_ref) {
1246                     if (pass == 1) {
1247                         for (i = 0; i < output_ins.operands; i++) {
1248                             if (output_ins.oprs[i].
1249                                 opflags & OPFLAG_FORWARD) {
1250                                 struct forwrefinfo *fwinf =
1251                                     (struct forwrefinfo *)
1252                                     saa_wstruct(forwrefs);
1253                                 fwinf->lineno = globallineno;
1254                                 fwinf->operand = i;
1255                             }
1256                         }
1257                     } else {    /* pass == 2 */
1258                         /*
1259                          * Hack to prevent phase error in the code
1260                          *   rol ax,x
1261                          *   x equ 1
1262                          *
1263                          * If the second operand is a forward reference,
1264                          * the UNITY property of the number 1 in that
1265                          * operand is cancelled. Otherwise the above
1266                          * sequence will cause a phase error.
1267                          *
1268                          * This hack means that the above code will
1269                          * generate 286+ code.
1270                          *
1271                          * The forward reference will mean that the
1272                          * operand will not have the UNITY property on
1273                          * the first pass, so the pass behaviours will
1274                          * be consistent.
1275                          */
1276
1277                         if (output_ins.operands >= 2 &&
1278                             (output_ins.oprs[1].opflags & OPFLAG_FORWARD) &&
1279                             !(IMMEDIATE & ~output_ins.oprs[1].type))
1280                         {
1281                             /* Remove special properties bits */
1282                             output_ins.oprs[1].type &= ~REG_SMASK;
1283                         }
1284
1285                     }           /* pass == 2 */
1286
1287                 }
1288
1289                 /*  forw_ref */
1290                 if (output_ins.opcode == I_EQU) {
1291                     if (pass1 == 1) {
1292                         /*
1293                          * Special `..' EQUs get processed in pass two,
1294                          * except `..@' macro-processor EQUs which are done
1295                          * in the normal place.
1296                          */
1297                         if (!output_ins.label)
1298                             report_error(ERR_NONFATAL,
1299                                          "EQU not preceded by label");
1300
1301                         else if (output_ins.label[0] != '.' ||
1302                                  output_ins.label[1] != '.' ||
1303                                  output_ins.label[2] == '@') {
1304                             if (output_ins.operands == 1 &&
1305                                 (output_ins.oprs[0].type & IMMEDIATE) &&
1306                                 output_ins.oprs[0].wrt == NO_SEG) {
1307                                 int isext =
1308                                     output_ins.oprs[0].
1309                                     opflags & OPFLAG_EXTERN;
1310                                 def_label(output_ins.label,
1311                                           output_ins.oprs[0].segment,
1312                                           output_ins.oprs[0].offset, NULL,
1313                                           false, isext, ofmt,
1314                                           report_error);
1315                             } else if (output_ins.operands == 2
1316                                        && (output_ins.oprs[0].
1317                                            type & IMMEDIATE)
1318                                        && (output_ins.oprs[0].type & COLON)
1319                                        && output_ins.oprs[0].segment ==
1320                                        NO_SEG
1321                                        && output_ins.oprs[0].wrt == NO_SEG
1322                                        && (output_ins.oprs[1].
1323                                            type & IMMEDIATE)
1324                                        && output_ins.oprs[1].segment ==
1325                                        NO_SEG
1326                                        && output_ins.oprs[1].wrt ==
1327                                        NO_SEG) {
1328                                 def_label(output_ins.label,
1329                                           output_ins.oprs[0].
1330                                           offset | SEG_ABS,
1331                                           output_ins.oprs[1].offset, NULL,
1332                                           false, false, ofmt,
1333                                           report_error);
1334                             } else
1335                                 report_error(ERR_NONFATAL,
1336                                              "bad syntax for EQU");
1337                         }
1338                     } else {    /* pass == 2 */
1339                         /*
1340                          * Special `..' EQUs get processed here, except
1341                          * `..@' macro processor EQUs which are done above.
1342                          */
1343                         if (output_ins.label[0] == '.' &&
1344                             output_ins.label[1] == '.' &&
1345                             output_ins.label[2] != '@') {
1346                             if (output_ins.operands == 1 &&
1347                                 (output_ins.oprs[0].type & IMMEDIATE)) {
1348                                 define_label(output_ins.label,
1349                                              output_ins.oprs[0].segment,
1350                                              output_ins.oprs[0].offset,
1351                                              NULL, false, false, ofmt,
1352                                              report_error);
1353                             } else if (output_ins.operands == 2
1354                                        && (output_ins.oprs[0].
1355                                            type & IMMEDIATE)
1356                                        && (output_ins.oprs[0].type & COLON)
1357                                        && output_ins.oprs[0].segment ==
1358                                        NO_SEG
1359                                        && (output_ins.oprs[1].
1360                                            type & IMMEDIATE)
1361                                        && output_ins.oprs[1].segment ==
1362                                        NO_SEG) {
1363                                 define_label(output_ins.label,
1364                                              output_ins.oprs[0].
1365                                              offset | SEG_ABS,
1366                                              output_ins.oprs[1].offset,
1367                                              NULL, false, false, ofmt,
1368                                              report_error);
1369                             } else
1370                                 report_error(ERR_NONFATAL,
1371                                              "bad syntax for EQU");
1372                         }
1373                     }           /* pass == 2 */
1374                 } else {        /* instruction isn't an EQU */
1375
1376                     if (pass1 == 1) {
1377
1378                         int64_t l = insn_size(location.segment, offs, sb, cpu,
1379                                            &output_ins, report_error);
1380
1381                         /* if (using_debug_info)  && output_ins.opcode != -1) */
1382                         if (using_debug_info)
1383                         {       /* fbk 03/25/01 */
1384                             /* this is done here so we can do debug type info */
1385                             int32_t typeinfo =
1386                                 TYS_ELEMENTS(output_ins.operands);
1387                             switch (output_ins.opcode) {
1388                             case I_RESB:
1389                                 typeinfo =
1390                                     TYS_ELEMENTS(output_ins.oprs[0].
1391                                                  offset) | TY_BYTE;
1392                                 break;
1393                             case I_RESW:
1394                                 typeinfo =
1395                                     TYS_ELEMENTS(output_ins.oprs[0].
1396                                                  offset) | TY_WORD;
1397                                 break;
1398                             case I_RESD:
1399                                 typeinfo =
1400                                     TYS_ELEMENTS(output_ins.oprs[0].
1401                                                  offset) | TY_DWORD;
1402                                 break;
1403                             case I_RESQ:
1404                                 typeinfo =
1405                                     TYS_ELEMENTS(output_ins.oprs[0].
1406                                                  offset) | TY_QWORD;
1407                                 break;
1408                             case I_REST:
1409                                 typeinfo =
1410                                     TYS_ELEMENTS(output_ins.oprs[0].
1411                                                  offset) | TY_TBYTE;
1412                                 break;
1413                             case I_DB:
1414                                 typeinfo |= TY_BYTE;
1415                                 break;
1416                             case I_DW:
1417                                 typeinfo |= TY_WORD;
1418                                 break;
1419                             case I_DD:
1420                                 if (output_ins.eops_float)
1421                                     typeinfo |= TY_FLOAT;
1422                                 else
1423                                     typeinfo |= TY_DWORD;
1424                                 break;
1425                             case I_DQ:
1426                                 typeinfo |= TY_QWORD;
1427                                 break;
1428                             case I_DT:
1429                                 typeinfo |= TY_TBYTE;
1430                                 break;
1431                             case I_DO:
1432                                 typeinfo |= TY_OWORD;
1433                                 break;
1434                             default:
1435                                 typeinfo = TY_LABEL;
1436
1437                             }
1438
1439                             ofmt->current_dfmt->debug_typevalue(typeinfo);
1440
1441                         }
1442                         if (l != -1) {
1443                             offs += l;
1444                             SET_CURR_OFFS(offs);
1445                         }
1446                         /*
1447                          * else l == -1 => invalid instruction, which will be
1448                          * flagged as an error on pass 2
1449                          */
1450
1451                     } else {    /* pass == 2 */
1452                         offs += assemble(location.segment, offs, sb, cpu,
1453                                          &output_ins, ofmt, report_error,
1454                                          &nasmlist);
1455                         SET_CURR_OFFS(offs);
1456
1457                     }
1458                 }               /* not an EQU */
1459                 cleanup_insn(&output_ins);
1460             }
1461             nasm_free(line);
1462             location.offset = offs = GET_CURR_OFFS;
1463         }                       /* end while (line = preproc->getline... */
1464
1465         if (pass1 == 2 && global_offset_changed)
1466             report_error(ERR_NONFATAL,
1467                          "phase error detected at end of assembly.");
1468
1469         if (pass1 == 1)
1470             preproc->cleanup(1);
1471
1472         if (pass1 == 1 && terminate_after_phase) {
1473             fclose(ofile);
1474             remove(outname);
1475             if (want_usage)
1476                 usage();
1477             exit(1);
1478         }
1479         pass_cnt++;
1480         if (pass > 1 && !global_offset_changed) {
1481             pass0++;
1482             if (pass0 == 2)
1483                 pass = pass_max - 1;
1484         } else if (!(optimizing > 0))
1485             pass0++;
1486
1487     }                           /* for (pass=1; pass<=2; pass++) */
1488
1489     preproc->cleanup(0);
1490     nasmlist.cleanup();
1491 #if 1
1492     if (optimizing > 0 && opt_verbose_info)     /*  -On and -Ov switches */
1493         fprintf(stdout,
1494                 "info:: assembly required 1+%d+1 passes\n", pass_cnt - 2);
1495 #endif
1496 }                               /* exit from assemble_file (...) */
1497
1498 static enum directives getkw(char **directive, char **value)
1499 {
1500     char *p, *q, *buf;
1501
1502     buf = *directive;
1503
1504     /*  allow leading spaces or tabs */
1505     while (*buf == ' ' || *buf == '\t')
1506         buf++;
1507
1508     if (*buf != '[')
1509         return 0;
1510
1511     p = buf;
1512
1513     while (*p && *p != ']')
1514         p++;
1515
1516     if (!*p)
1517         return 0;
1518
1519     q = p++;
1520
1521     while (*p && *p != ';') {
1522         if (!isspace(*p))
1523             return 0;
1524         p++;
1525     }
1526     q[1] = '\0';
1527
1528     *directive = p = buf + 1;
1529     while (*buf && *buf != ' ' && *buf != ']' && *buf != '\t')
1530         buf++;
1531     if (*buf == ']') {
1532         *buf = '\0';
1533         *value = buf;
1534     } else {
1535         *buf++ = '\0';
1536         while (isspace(*buf))
1537             buf++;              /* beppu - skip leading whitespace */
1538         *value = buf;
1539         while (*buf != ']')
1540             buf++;
1541         *buf++ = '\0';
1542     }
1543
1544     return bsii(*directive, directives, elements(directives));
1545 }
1546
1547 /**
1548  * gnu style error reporting
1549  * This function prints an error message to error_file in the
1550  * style used by GNU. An example would be:
1551  * file.asm:50: error: blah blah blah
1552  * where file.asm is the name of the file, 50 is the line number on
1553  * which the error occurs (or is detected) and "error:" is one of
1554  * the possible optional diagnostics -- it can be "error" or "warning"
1555  * or something else.  Finally the line terminates with the actual
1556  * error message.
1557  *
1558  * @param severity the severity of the warning or error
1559  * @param fmt the printf style format string
1560  */
1561 static void report_error_gnu(int severity, const char *fmt, ...)
1562 {
1563     va_list ap;
1564
1565     if (is_suppressed_warning(severity))
1566         return;
1567
1568     if (severity & ERR_NOFILE)
1569         fputs("nasm: ", error_file);
1570     else {
1571         char *currentfile = NULL;
1572         int32_t lineno = 0;
1573         src_get(&lineno, &currentfile);
1574         fprintf(error_file, "%s:%"PRId32": ", currentfile, lineno);
1575         nasm_free(currentfile);
1576     }
1577     va_start(ap, fmt);
1578     report_error_common(severity, fmt, ap);
1579     va_end(ap);
1580 }
1581
1582 /**
1583  * MS style error reporting
1584  * This function prints an error message to error_file in the
1585  * style used by Visual C and some other Microsoft tools. An example
1586  * would be:
1587  * file.asm(50) : error: blah blah blah
1588  * where file.asm is the name of the file, 50 is the line number on
1589  * which the error occurs (or is detected) and "error:" is one of
1590  * the possible optional diagnostics -- it can be "error" or "warning"
1591  * or something else.  Finally the line terminates with the actual
1592  * error message.
1593  *
1594  * @param severity the severity of the warning or error
1595  * @param fmt the printf style format string
1596  */
1597 static void report_error_vc(int severity, const char *fmt, ...)
1598 {
1599     va_list ap;
1600
1601     if (is_suppressed_warning(severity))
1602         return;
1603
1604     if (severity & ERR_NOFILE)
1605         fputs("nasm: ", error_file);
1606     else {
1607         char *currentfile = NULL;
1608         int32_t lineno = 0;
1609         src_get(&lineno, &currentfile);
1610         fprintf(error_file, "%s(%"PRId32") : ", currentfile, lineno);
1611         nasm_free(currentfile);
1612     }
1613     va_start(ap, fmt);
1614     report_error_common(severity, fmt, ap);
1615     va_end(ap);
1616 }
1617
1618 /**
1619  * check for supressed warning
1620  * checks for suppressed warning or pass one only warning and we're
1621  * not in pass 1
1622  *
1623  * @param severity the severity of the warning or error
1624  * @return true if we should abort error/warning printing
1625  */
1626 static int is_suppressed_warning(int severity)
1627 {
1628     /*
1629      * See if it's a suppressed warning.
1630      */
1631     return ((severity & ERR_MASK) == ERR_WARNING &&
1632             (severity & ERR_WARN_MASK) != 0 &&
1633             suppressed[(severity & ERR_WARN_MASK) >> ERR_WARN_SHR]) ||
1634         /*
1635          * See if it's a pass-one only warning and we're not in pass one.
1636          */
1637         ((severity & ERR_PASS1) && pass0 == 2);
1638 }
1639
1640 /**
1641  * common error reporting
1642  * This is the common back end of the error reporting schemes currently
1643  * implemented.  It prints the nature of the warning and then the
1644  * specific error message to error_file and may or may not return.  It
1645  * doesn't return if the error severity is a "panic" or "debug" type.
1646  *
1647  * @param severity the severity of the warning or error
1648  * @param fmt the printf style format string
1649  */
1650 static void report_error_common(int severity, const char *fmt,
1651                                 va_list args)
1652 {
1653     switch (severity & ERR_MASK) {
1654     case ERR_WARNING:
1655         fputs("warning: ", error_file);
1656         break;
1657     case ERR_NONFATAL:
1658         fputs("error: ", error_file);
1659         break;
1660     case ERR_FATAL:
1661         fputs("fatal: ", error_file);
1662         break;
1663     case ERR_PANIC:
1664         fputs("panic: ", error_file);
1665         break;
1666     case ERR_DEBUG:
1667         fputs("debug: ", error_file);
1668         break;
1669     }
1670
1671     vfprintf(error_file, fmt, args);
1672     putc('\n', error_file);
1673
1674     if (severity & ERR_USAGE)
1675         want_usage = true;
1676
1677     switch (severity & ERR_MASK) {
1678     case ERR_DEBUG:
1679         /* no further action, by definition */
1680         break;
1681     case ERR_WARNING:
1682         if (!suppressed[0])     /* Treat warnings as errors */
1683             terminate_after_phase = true;
1684         break;
1685     case ERR_NONFATAL:
1686         terminate_after_phase = true;
1687         break;
1688     case ERR_FATAL:
1689         if (ofile) {
1690             fclose(ofile);
1691             remove(outname);
1692         }
1693         if (want_usage)
1694             usage();
1695         exit(1);                /* instantly die */
1696         break;                  /* placate silly compilers */
1697     case ERR_PANIC:
1698         fflush(NULL);
1699         /*      abort();        *//* halt, catch fire, and dump core */
1700         exit(3);
1701         break;
1702     }
1703 }
1704
1705 static void usage(void)
1706 {
1707     fputs("type `nasm -h' for help\n", error_file);
1708 }
1709
1710 static void register_output_formats(void)
1711 {
1712     ofmt = ofmt_register(report_error);
1713 }
1714
1715 #define BUF_DELTA 512
1716
1717 static FILE *no_pp_fp;
1718 static efunc no_pp_err;
1719 static ListGen *no_pp_list;
1720 static int32_t no_pp_lineinc;
1721
1722 static void no_pp_reset(char *file, int pass, efunc error, evalfunc eval,
1723                         ListGen * listgen)
1724 {
1725     src_set_fname(nasm_strdup(file));
1726     src_set_linnum(0);
1727     no_pp_lineinc = 1;
1728     no_pp_err = error;
1729     no_pp_fp = fopen(file, "r");
1730     if (!no_pp_fp)
1731         no_pp_err(ERR_FATAL | ERR_NOFILE,
1732                   "unable to open input file `%s'", file);
1733     no_pp_list = listgen;
1734     (void)pass;                 /* placate compilers */
1735     (void)eval;                 /* placate compilers */
1736 }
1737
1738 static char *no_pp_getline(void)
1739 {
1740     char *buffer, *p, *q;
1741     int bufsize;
1742
1743     bufsize = BUF_DELTA;
1744     buffer = nasm_malloc(BUF_DELTA);
1745     src_set_linnum(src_get_linnum() + no_pp_lineinc);
1746
1747     while (1) {                 /* Loop to handle %line */
1748
1749         p = buffer;
1750         while (1) {             /* Loop to handle long lines */
1751             q = fgets(p, bufsize - (p - buffer), no_pp_fp);
1752             if (!q)
1753                 break;
1754             p += strlen(p);
1755             if (p > buffer && p[-1] == '\n')
1756                 break;
1757             if (p - buffer > bufsize - 10) {
1758                 int offset;
1759                 offset = p - buffer;
1760                 bufsize += BUF_DELTA;
1761                 buffer = nasm_realloc(buffer, bufsize);
1762                 p = buffer + offset;
1763             }
1764         }
1765
1766         if (!q && p == buffer) {
1767             nasm_free(buffer);
1768             return NULL;
1769         }
1770
1771         /*
1772          * Play safe: remove CRs, LFs and any spurious ^Zs, if any of
1773          * them are present at the end of the line.
1774          */
1775         buffer[strcspn(buffer, "\r\n\032")] = '\0';
1776
1777         if (!nasm_strnicmp(buffer, "%line", 5)) {
1778             int32_t ln;
1779             int li;
1780             char *nm = nasm_malloc(strlen(buffer));
1781             if (sscanf(buffer + 5, "%"PRId32"+%d %s", &ln, &li, nm) == 3) {
1782                 nasm_free(src_set_fname(nm));
1783                 src_set_linnum(ln);
1784                 no_pp_lineinc = li;
1785                 continue;
1786             }
1787             nasm_free(nm);
1788         }
1789         break;
1790     }
1791
1792     no_pp_list->line(LIST_READ, buffer);
1793
1794     return buffer;
1795 }
1796
1797 static void no_pp_cleanup(int pass)
1798 {
1799     (void)pass;                     /* placate GCC */
1800     fclose(no_pp_fp);
1801 }
1802
1803 static uint32_t get_cpu(char *value)
1804 {
1805     if (!strcmp(value, "8086"))
1806         return IF_8086;
1807     if (!strcmp(value, "186"))
1808         return IF_186;
1809     if (!strcmp(value, "286"))
1810         return IF_286;
1811     if (!strcmp(value, "386"))
1812         return IF_386;
1813     if (!strcmp(value, "486"))
1814         return IF_486;
1815     if (!strcmp(value, "586") || !nasm_stricmp(value, "pentium"))
1816         return IF_PENT;
1817     if (!strcmp(value, "686") ||
1818         !nasm_stricmp(value, "ppro") ||
1819         !nasm_stricmp(value, "pentiumpro") || !nasm_stricmp(value, "p2"))
1820         return IF_P6;
1821     if (!nasm_stricmp(value, "p3") || !nasm_stricmp(value, "katmai"))
1822         return IF_KATMAI;
1823     if (!nasm_stricmp(value, "p4") ||   /* is this right? -- jrc */
1824         !nasm_stricmp(value, "willamette"))
1825         return IF_WILLAMETTE;
1826     if (!nasm_stricmp(value, "prescott"))
1827         return IF_PRESCOTT;
1828     if (!nasm_stricmp(value, "x64") ||
1829         !nasm_stricmp(value, "x86-64"))
1830         return IF_X86_64;
1831     if (!nasm_stricmp(value, "ia64") ||
1832         !nasm_stricmp(value, "ia-64") ||
1833         !nasm_stricmp(value, "itanium") ||
1834         !nasm_stricmp(value, "itanic") || !nasm_stricmp(value, "merced"))
1835         return IF_IA64;
1836
1837     report_error(pass0 < 2 ? ERR_NONFATAL : ERR_FATAL,
1838                  "unknown 'cpu' type");
1839
1840     return IF_PLEVEL;           /* the maximum level */
1841 }
1842
1843 static int get_bits(char *value)
1844 {
1845     int i;
1846
1847     if ((i = atoi(value)) == 16)
1848         return i;               /* set for a 16-bit segment */
1849     else if (i == 32) {
1850         if (cpu < IF_386) {
1851             report_error(ERR_NONFATAL,
1852                          "cannot specify 32-bit segment on processor below a 386");
1853             i = 16;
1854         }
1855     } else if (i == 64) {
1856         if (cpu < IF_X86_64) {
1857             report_error(ERR_NONFATAL,
1858                          "cannot specify 64-bit segment on processor below an x86-64");
1859             i = 16;
1860         }
1861         if (i != maxbits) {
1862             report_error(ERR_NONFATAL,
1863                          "%s output format does not support 64-bit code",
1864                          ofmt->shortname);
1865             i = 16;
1866         }
1867     } else {
1868         report_error(pass0 < 2 ? ERR_NONFATAL : ERR_FATAL,
1869                      "`%s' is not a valid segment size; must be 16, 32 or 64",
1870                      value);
1871         i = 16;
1872     }
1873     return i;
1874 }
1875
1876 /* end of nasm.c */