1 /****************************************************************************
2 *
3 *                            Open Watcom Project
4 *
5 *    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
6 *
7 *  ========================================================================
8 *
9 *    This file contains Original Code and/or Modifications of Original
10 *    Code as defined in and that are subject to the Sybase Open Watcom
11 *    Public License version 1.0 (the 'License'). You may not use this file
12 *    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
13 *    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
14 *    provided with the Original Code and Modifications, and is also
15 *    available at www.sybase.com/developer/opensource.
16 *
17 *    The Original Code and all software distributed under the License are
18 *    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
19 *    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
20 *    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
21 *    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
22 *    NON-INFRINGEMENT. Please see the License for the specific language
23 *    governing rights and limitations under the License.
24 *
25 *  ========================================================================
26 *
27 * Description:  command line argument parser
28 *
29 ****************************************************************************/
30 
31 //#include <stdarg.h>
32 #include <stddef.h>
33 #include <ctype.h>
34 
35 #include "globals.h"
36 #include "memalloc.h"
37 #include "parser.h"
38 #include "msgtext.h"
39 #include "dbgcv.h"
40 #include "cmdline.h"
41 #include "myassert.h"
42 #include "input.h" /* GetFNamePart() */
43 
44 //#ifdef __OSI__
45 //  #include "ostype.h"
46 //#endif
47 
48 #if defined(__UNIX__) || defined(__CYGWIN__) || defined(__DJGPP__)
49 
50 #define HANDLECTRLZ 0
51 #define SWITCHCHAR 0
52 
53 #else
54 
55 #define HANDLECTRLZ 1
56 #define SWITCHCHAR 1
57 
58 #endif
59 
60 #ifdef __I86__
61 #define OPTQUAL __near
62 #else
63 #define OPTQUAL
64 #endif
65 
66 extern char     banner_printed;
67 
68 struct global_options Options = {
69     /* quiet            */          FALSE,
70     /* line_numbers     */          FALSE,
71     /* debug_symbols    */          0,
72     /* debug_ext        */          0, /* v2.10 */
73     /* floating_point   */          FPO_NO_EMULATION,
74 
75     /* error_limit      */          50,
76     /* no error display */          FALSE,
77     /* warning_level    */          2,
78     /* warning_error    */          FALSE,
79 #ifdef DEBUG_OUT
80     /* debug            */          FALSE,
81     /* nobackpatch      */          FALSE,
82 #if FASTPASS
83     /* nofastpass       */          FALSE,
84     /* print_linestore  */          FALSE,
85 #endif
86     /* max_passes       */          0,
87     /* skip_preprocessor */         0,
88     /* log_all_files    */          0,
89     /* dump_reswords    */          FALSE,
90     /* dump_reswords_hash */        FALSE,
91     /* dump_symbols     */          FALSE,
92     /* dump_symbols_hash */         FALSE,
93 #endif
94     /* names            */          {
95         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
96 #if BUILD_TARGET
97         NULL,
98 #endif
99 #if MANGLERSUPP
100         NULL
101 #endif
102     },
103     /* queues           */          { NULL, NULL, NULL },
104 #if COCTALS
105     /* allow_c_octals        */     FALSE,
106 #endif
107     /* no_comment_data_in_code_records */   FALSE,
108     /* no_opt_farcall        */     FALSE,
109     /* no_dependencies       */ //    FALSE,
110 #if COFF_SUPPORT
111     /* no_file_entry         */     FALSE,
112     /* no_static_procs       */     FALSE,
113     /* no_section_aux_entry  */     FALSE,
114 #endif
115     /* no_cdecl_decoration   */     FALSE,
116     /* stdcall_decoration    */     STDCALL_FULL,
117     /* no_export_decoration  */     FALSE,
118     /* entry_decorated       */     FALSE,
119     /* write_listing         */     FALSE,
120     /* write_impdef          */     FALSE,
121     /* case_sensitive        */     FALSE,
122     /* convert_uppercase     */     FALSE,
123     /* preprocessor_stdout   */     FALSE,
124     /* masm51_compat         */     FALSE,
125     /* strict_masm_compat    */     FALSE,
126     /* masm_compat_gencode   */     FALSE,
127     /* masm8_proc_visibility */     FALSE,
128 
129     /* listif                */     FALSE,
130     /* list_generated_code   */     FALSE,
131     /* list_macro            */     LM_LISTMACRO,
132     /* no_symbol_listing     */     FALSE,
133     /* first_pass_listing    */     FALSE,
134     /* no_final_msg_listing  */     FALSE, /* v2.13 */
135 
136     /* all_symbols_public    */     FALSE,
137     /* safeseh               */     FALSE,
138     /* ignore_include        */     FALSE,
139     /* output_format         */     OFORMAT_OMF,
140     /* sub_format            */     SFORMAT_NONE,
141     /* alignment_default     */     0,
142     /* langtype              */     LANG_NONE,
143     /* model                 */     MODEL_NONE,
144     /* cpu                   */     P_86,
145     /* fastcall type         */     FCT_MSC,
146     /* syntax check only     */     FALSE,
147 #if MANGLERSUPP
148     /* naming_convention*/          NC_DO_NOTHING,
149 #endif
150 };
151 
152 char *DefaultDir[NUM_FILE_TYPES] = { NULL, NULL, NULL, NULL };
153 //static char *DefaultExt[NUM_FILE_TYPES] = { OBJ_EXT, LST_EXT, ERR_EXT };
154 
155 #define MAX_RSP_NESTING 15  /* nesting of response files */
156 
157 static unsigned         OptValue;  /* value of option's numeric argument  */
158 static char             *OptName;  /* value of option's name argument     */
159 static const char       *cmdsave[MAX_RSP_NESTING]; /* response files */
160 static const char       *cmdbuffers[MAX_RSP_NESTING]; /* response files */
161 static int              rspidx = 0; /* response file level */
162 
163 /* array for options -0 ... -10 */
164 static const enum cpu_info cpuoption[] = {
165     P_86, P_186, P_286, P_386,          /* 0-3 */
166     P_486, P_586, P_686, P_686 | P_MMX, /* 4-7 */
167     P_686 | P_MMX | P_SSE1,             /* 8   */
168     P_686 | P_MMX | P_SSE1 | P_SSE2,    /* 9   */
169 #if AMD64_SUPPORT
170     P_64,                               /* 10  */
171 #endif
172 };
173 
174 #if 0 /* v2.10: removed, because factually never called */
StripQuotes(char * fname)175 static void StripQuotes( char *fname )
176 /************************************/
177 {
178     char *s;
179     char *d;
180 
181     if( *fname == '"' ) {
182         /* string will shrink so we can reduce in place */
183         d = fname;
184         for( s = d + 1; *s && *s != '"'; ++s ) {
185             /* collapse double backslashes, only then look for escaped quotes */
186             if( s[0] == '\\' && s[1] == '\\' ) {
187                 ++s;
188             } else if( s[0] == '\\' && s[1] == '"' ) {
189                 ++s;
190             }
191             *d++ = *s;
192         }
193         *d = '\0';
194     }
195 }
196 
GetAFileName(void)197 static char *GetAFileName( void )
198 /*******************************/
199 {
200     DebugMsg(("GetAFileName() enter, OptName=>%s<\n", OptName ));
201     StripQuotes( OptName );
202     return( OptName );
203 }
204 #else
205 #define GetAFileName() OptName
206 #endif
207 
208 #if BUILD_TARGET
SetTargName(char * name,unsigned len)209 static void SetTargName( char *name, unsigned len )
210 /*************************************************/
211 {
212     if( Options.names[OPTN_BUILD_TARGET] != NULL ) {
213         MemFree( Options.names[OPTN_BUILD_TARGET] );
214         Options.names[OPTN_BUILD_TARGET] = NULL;
215     }
216     if( name == NULL || len == 0 )
217         return;
218     Options.names[OPTN_BUILD_TARGET] = MemAlloc( len + 1 );
219     strcpy( Options.names[OPTN_BUILD_TARGET], name );
220     _strupr( Options.names[OPTN_BUILD_TARGET] );
221 }
222 #endif
223 
224 /* called by -0, -1, ... argument */
225 
SetCpuCmdline(enum cpu_info value,const char * parm)226 static void SetCpuCmdline( enum cpu_info value, const char *parm )
227 /****************************************************************/
228 {
229 
230     Options.cpu &= ~(P_CPU_MASK | P_EXT_MASK | P_PM);
231     Options.cpu |= value;
232 
233     for( ; *parm ; parm++ ) {
234         if( *parm == 'p' && Options.cpu >= P_286 ) {
235             Options.cpu |= P_PM;      /* set privileged mode */
236 #if MANGLERSUPP
237         } else if( *parm == '"' ) {       /* set default mangler */
238             char *dest;
239             parm++;
240             dest = strchr( parm, '"' );
241             if( Options.names[OPTN_DEFNAME_MANGLER] != NULL ) {
242                 MemFree( Options.names[OPTN_DEFNAME_MANGLER );
243             }
244             Options.names[OPTN_DEFNAME_MANGLER = MemAlloc( dest - parm + 1 );
245             dest = Options.names[OPTN_DEFNAME_MANGLER];
246             for( ; *parm != '"'; dest++, parm++ ) {
247                 *dest = *parm;
248             }
249             *dest = NULLC;
250 #endif
251         } else {
252             EmitWarn( 1, CPU_OPTION_INVALID, parm );
253             break;
254         }
255     }
256 }
257 
258 /* queue a text macro, include path or "forced" include files.
259    this is called for cmdline options -D, -I and -Fi
260  */
261 
262 static void queue_item( int i, const char *string )
263 /*************************************************/
264 {
265     struct qitem *p;
266     struct qitem *q;
267 
268     DebugMsg(("queue_item(%u, %s) enter\n", i, string));
269     p = MemAlloc( sizeof(struct qitem) + strlen( string ) );
270     p->next = NULL;
271     strcpy( p->value, string );
272     q = Options.queues[i];
273     if ( q ) {
274         for ( ; q->next; q = q->next );
275         q->next = p;
276     } else
277         Options.queues[i] = p;
278     return;
279 }
280 
281 static void get_fname( int type, const char *token )
282 /**************************************************/
283 /*
284  * called by -Fo, -Fw or -Fl (for .OBJ, .ERR or .LST filenames ).
285  * also called by -Fd; in this case type is > NUM_FILE_TYPES!
286  * v2.12: _splitpath()/_makepath() removed.
287  */
288 {
289     const char  *pName;
290     char        name [ FILENAME_MAX ];
291 
292     DebugMsg(("get_fname( type=%u, >%s< ) enter\n", type, token ));
293     //_splitpath( token, drive, dir, fname, ext );
294     pName = GetFNamePart( token );
295     /*
296      * If name's ending with a '\' (or '/' in Unix), it's supposed
297      * to be a directory name only.
298      */
299     if( *pName == NULLC ) {
300         DebugMsg(("get_fname(%u, >%s< ) name is empty or a directory\n", type, token ));
301         /* v2.10: ensure type is < NUM_FILE_TYPES */
302         if ( type < NUM_FILE_TYPES ) {
303             if ( DefaultDir[type] )
304                 MemFree( DefaultDir[type]);
305             DefaultDir[type] = MemAlloc( strlen( token ) + 1 );
306             strcpy( DefaultDir[type], token );
307         }
308         return;
309     }
310     /* v2.10: ensure type is < NUM_FILE_TYPES */
311     //if ( drive[0] == NULLC && dir[0] == NULLC && type < NUM_FILE_TYPES && DefaultDir[type] ) {
312     name[0] = NULLC;
313     if ( pName == token && type < NUM_FILE_TYPES && DefaultDir[type] ) {
314         DebugMsg(("get_fname: default drive+dir used: %s\n" ));
315         //_splitpath( DefaultDir[type], drive, dir, NULL, NULL );
316         strcpy( name, DefaultDir[type] );
317     }
318     strcat( name, token );
319 #if 0 /* v2.12: extension will be set in SetFileNames() */
320     if( type && type < NUM_FILE_TYPES ) {
321         char *pExt = GetExtPart( name );
322         if ( *pExt == NULLC ) {
323             *pExt++ = '.';
324             strcpy( pExt, DefaultExt[type-1] );
325         }
326     }
327 #endif
328     //_makepath( name, drive, dir, fname, pExt );
329     if( Options.names[type] != NULL ) {
330         MemFree( Options.names[type] );
331     }
332     Options.names[type] = MemAlloc( strlen( name ) + 1 );
333     strcpy( Options.names[type], name );
334 }
335 
336 static void set_option_n_name( int idx, const char *name )
337 /********************************************************/
338 /* option -n: set name of
339  * - nd: data seg
340  * - nm: module name
341  * - nt: text seg
342  * - nc: code class
343  */
344 {
345     if ( *name != '.' && !is_valid_id_char( *name ) ) {
346         EmitError( N_OPTION_NEEDS_A_NAME_PARAMETER );
347         return;
348     }
349 
350     if( Options.names[idx] != NULL ) {
351         MemFree( Options.names[idx] );
352     }
353     Options.names[idx] = MemAlloc( strlen( name ) + 1 );
354     strcpy( Options.names[idx], name );
355 }
356 
357 //static void OPTQUAL Ignore( void ) {};
358 
359 #if BUILD_TARGET
360 static void OPTQUAL Set_bt( void ) { SetTargName( OptName,  strlen(OptName) ); }
361 #endif
362 
363 static void OPTQUAL Set_c( void ) { }
364 
365 #ifdef DEBUG_OUT
366 static void OPTQUAL Set_ce( void ) { rspidx = 1 / rspidx; }
367 #endif
368 
369 static void OPTQUAL Set_Cp( void ) { Options.case_sensitive = TRUE;   Options.convert_uppercase = FALSE; }
370 static void OPTQUAL Set_Cu( void ) { Options.case_sensitive = FALSE;  Options.convert_uppercase = TRUE;  }
371 static void OPTQUAL Set_Cx( void ) { Options.case_sensitive = FALSE;  Options.convert_uppercase = FALSE; }
372 
373 static void OPTQUAL Set_Zd( void ) { Options.line_numbers = TRUE; }
374 static void OPTQUAL Set_Zi( void )
375 {
376     Set_Zd();
377     Options.debug_symbols = CV_SIGNATURE;
378     /* v2.10: added optional numeric argument for -Zi */
379     if ( OptValue <= CVEX_MAX )
380         Options.debug_ext = OptValue;
381     else
382         EmitWarn( 1, INVALID_CMDLINE_VALUE, "Zi" );
383 }
384 
385 static void OPTQUAL Set_Zp( void )
386 /********************************/
387 {
388     uint_8 power;
389     for ( power = 0; (1 << power) <= MAX_STRUCT_ALIGN; power++ )
390         if ( ( 1 << power ) == OptValue ) {
391             Options.fieldalign = power;
392             return;
393         }
394     EmitWarn( 1, INVALID_CMDLINE_VALUE, "Zp" );
395     return;
396 }
397 
398 static void OPTQUAL Set_D( void )  { queue_item( OPTQ_MACRO,    GetAFileName() ); }
399 static void OPTQUAL Set_Fi( void ) { queue_item( OPTQ_FINCLUDE, GetAFileName() ); }
400 static void OPTQUAL Set_I( void )  { queue_item( OPTQ_INCPATH,  GetAFileName() ); }
401 
402 static void OPTQUAL Set_e( void ) { Options.error_limit = OptValue; }
403 
404 static void OPTQUAL Set_nologo( void ) { banner_printed = TRUE; }
405 static void OPTQUAL Set_q( void )      { Set_nologo(); Options.quiet = TRUE; }
406 static void OPTQUAL Set_EP( void ) { Options.preprocessor_stdout = TRUE; Set_q(); }
407 
408 #if DLLIMPORT
409 static void OPTQUAL Set_Fd( void ) { get_fname( OPTN_LNKDEF_FN, GetAFileName() ); Options.write_impdef = TRUE;}
410 #endif
411 static void OPTQUAL Set_Fw( void ) { get_fname( OPTN_ERR_FN, GetAFileName() ); }
412 static void OPTQUAL Set_Fl( void ) { get_fname( OPTN_LST_FN, GetAFileName() ); Options.write_listing = TRUE;}
413 static void OPTQUAL Set_Fo( void ) { get_fname( OPTN_OBJ_FN, GetAFileName() ); }
414 
415 static void OPTQUAL Set_fp( void ) { Options.cpu &= ~P_FPU_MASK; Options.cpu = OptValue; }
416 static void OPTQUAL Set_FPx( void ) { Options.floating_point = OptValue; }
417 static void OPTQUAL Set_G( void ) { Options.langtype = OptValue; }
418 
419 static void OPTQUAL Set_Sa( void )
420 /********************************/
421 {
422     Options.listif = TRUE;
423     Options.list_generated_code = TRUE;
424     Options.list_macro = LM_LISTMACROALL;
425 }
426 
427 static void OPTQUAL Set_True( void )
428 /**********************************/
429 {
430     char *p = ((char *)&Options) + OptValue;
431     *p = TRUE;
432 }
433 
434 static void OPTQUAL Set_m( void ) { Options.model = OptValue; }
435 static void OPTQUAL Set_n( void ) { set_option_n_name( OptValue, OptName ); }
436 
437 #ifdef DEBUG_OUT
438 static void OPTQUAL Set_pm( void ) { Options.max_passes = OptValue; };
439 #endif
440 
441 static void OPTQUAL Set_WX( void ) { Options.warning_error = TRUE; }
442 
443 static void OPTQUAL Set_w( void ) { Set_WX(); Options.warning_level = 0; }
444 
445 static void OPTQUAL Set_W( void )
446 /*******************************/
447 {
448     if ( OptValue <= 4 )
449         Options.warning_level = OptValue;
450     else
451         EmitWarn( 1, INVALID_CMDLINE_VALUE, "W" );
452 }
453 
454 static void OPTQUAL Set_ofmt( void )
455 /**********************************/
456 {
457     Options.output_format = OptValue & 0xff;
458     Options.sub_format = OptValue >> 8;
459 }
460 
461 static void OPTQUAL Set_zcm( void ) { Options.no_cdecl_decoration = FALSE; }
462 #if OWFC_SUPPORT
463 static void OPTQUAL Set_zf( void )  { Options.fctype = OptValue; }
464 #endif
465 
466 static void OPTQUAL Set_zt( void ) { Options.stdcall_decoration = OptValue; }
467 #ifndef __SW_BD
468 static void OPTQUAL Set_h( void ) {  PrintUsage();  exit(1); }
469 #endif
470 
471 #ifdef DEBUG_OUT
472 static void OPTQUAL Set_dm( void )
473 {
474     int i;
475     for ( i = 0; i < MSG_LAST; i++ ) {
476         printf("%3u: %s\n", i, MsgGetEx(i) );
477     }
478 }
479 static void OPTQUAL Set_dt( void )
480 /********************************/
481 {
482     Options.debug = TRUE;
483     ModuleInfo.cref = TRUE; /* enable debug displays */
484     DebugMsg(( "debugging output on\n" ));
485 }
486 #if FASTPASS
487 static void OPTQUAL Set_nfp( void )
488 /*********************************/
489 {
490     Options.nofastpass = TRUE;
491     DebugMsg(( "FASTPASS disabled\n" ));
492 }
493 #endif
494 static void OPTQUAL Set_nbp( void )
495 /*********************************/
496 {
497     Options.nobackpatch = TRUE;
498     DebugMsg(( "backpatching disabled\n" ));
499 }
500 #endif
501 
502 struct  cmdloption {
503     const char  *name;
504     unsigned    value;
505     void OPTQUAL (*function)( void );
506 };
507 
508 #define optofs( x ) offsetof( struct global_options, x )
509 
510 /*
511  * '#': collect a number
512  * '$': collect an identifer[=value]
513  * '@': collect a filename
514  * '=': collect an optional '='
515  * '^': skip spaces before argument
516  */
517 static struct cmdloption const cmdl_options[] = {
518 #ifndef __SW_BD
519     { "?",      0,        Set_h },
520 #endif
521 #ifdef DEBUG_OUT
522     { "af",     optofs( log_all_files ), Set_True },
523 #endif
524 #if BIN_SUPPORT
525     { "bin",    OFORMAT_BIN | (SFORMAT_NONE << 8), Set_ofmt },
526 #endif
527 #if BUILD_TARGET
528     { "bt=$",   0,        Set_bt },
529 #endif
530     { "Cp",     0,        Set_Cp },
531     { "Cu",     0,        Set_Cu },
532     { "Cx",     0,        Set_Cx },
533 #ifdef DEBUG_OUT
534     { "ce",     0,        Set_ce },
535 #endif
536 #if COFF_SUPPORT
537     { "coff",   OFORMAT_COFF | (SFORMAT_NONE << 8), Set_ofmt },
538 #endif
539     { "c",      0,        Set_c },
540 #if COFF_SUPPORT && DJGPP_SUPPORT
541     { "djgpp",  OFORMAT_COFF | (SFORMAT_DJGPP << 8), Set_ofmt },
542 #endif
543 #ifdef DEBUG_OUT
544     { "dm",     0,        Set_dm },
545     { "drh",    optofs( dump_reswords_hash ), Set_True },
546     { "dr",     optofs( dump_reswords ),    Set_True },
547     { "dsh",    optofs( dump_symbols_hash ), Set_True },
548     { "ds",     optofs( dump_symbols ),     Set_True },
549     { "dt",     0,        Set_dt },
550 #endif
551     { "D^$",    0,        Set_D },
552 #if ELF_SUPPORT
553 #if AMD64_SUPPORT
554     { "elf64",  OFORMAT_ELF | (SFORMAT_64BIT << 8), Set_ofmt },
555 #endif
556     { "elf",    OFORMAT_ELF | (SFORMAT_NONE << 8), Set_ofmt },
557 #endif
558     { "EP",     0,        Set_EP },
559     { "eq",     optofs( no_error_disp ),        Set_True },
560     { "e=#",    0,        Set_e },
561 #if DLLIMPORT
562     { "Fd=@",   0,        Set_Fd },
563 #endif
564     { "Fi=^@",  0,        Set_Fi },
565     { "Fl=@",   0,        Set_Fl },
566     { "Fo=^@",  0,        Set_Fo },
567     { "FPi87",  FPO_NO_EMULATION, Set_FPx },
568     { "FPi",    FPO_EMULATION,    Set_FPx },
569     { "fp0",    P_87,     Set_fp },
570     { "fp2",    P_287,    Set_fp },
571     { "fp3",    P_387,    Set_fp },
572 #if 0
573     { "fp5",    P_387,    Set_fp },
574     { "fp6",    P_387,    Set_fp },
575 #endif
576     { "fpc",    P_NO87,   Set_fp },
577     { "Fw=^@",  0,        Set_Fw },
578     { "Gc",     LANG_PASCAL,  Set_G },
579     { "Gd",     LANG_C,       Set_G },
580     { "Gr",     LANG_FASTCALL,Set_G },
581     { "Gz",     LANG_STDCALL, Set_G },
582     { "h",      0,        Set_h },
583     { "I=^@",   0,        Set_I },
584 #ifdef DEBUG_OUT
585 #if FASTPASS
586     { "ls",     optofs( print_linestore ), Set_True },
587 #endif
588 #endif
589     { "mc",     MODEL_COMPACT, Set_m },
590     { "mf",     MODEL_FLAT,    Set_m },
591     { "mh",     MODEL_HUGE,    Set_m },
592     { "ml",     MODEL_LARGE,   Set_m },
593     { "mm",     MODEL_MEDIUM,  Set_m },
594     { "ms",     MODEL_SMALL,   Set_m },
595     { "mt",     MODEL_TINY,    Set_m },
596 #if BIN_SUPPORT
597 #if MZ_SUPPORT
598     { "mz",     OFORMAT_BIN | (SFORMAT_MZ << 8), Set_ofmt },
599 #endif
600 #endif
601 #ifdef DEBUG_OUT
602     { "nbp",    0,                  Set_nbp },
603 #endif
604     { "nc=$",   OPTN_CODE_CLASS,    Set_n },
605     { "nd=$",   OPTN_DATA_SEG,      Set_n },
606 #if FASTPASS && defined(DEBUG_OUT)
607     { "nfp",    0,                  Set_nfp },
608 #endif
609     { "nm=$",   OPTN_MODULE_NAME,   Set_n },
610     { "nologo", 0,                  Set_nologo },
611     { "nt=$",   OPTN_TEXT_SEG,      Set_n },
612     { "omf",    OFORMAT_OMF | (SFORMAT_NONE << 8), Set_ofmt },
613 #if COCTALS
614     { "o",      optofs( allow_c_octals ),   Set_True },
615 #endif
616 #if PE_SUPPORT
617     { "pe",     OFORMAT_BIN | (SFORMAT_PE << 8), Set_ofmt },
618 #endif
619 #ifdef DEBUG_OUT
620     { "pm=#",   0,        Set_pm },
621 #endif
622     { "q",      0,        Set_q },
623     { "Sa",     0,        Set_Sa },
624     { "Sf",     optofs( first_pass_listing  ), Set_True },
625     { "Sg",     optofs( list_generated_code ), Set_True },
626     { "Sn",     optofs( no_symbol_listing   ), Set_True },
627     { "Sx",     optofs( listif              ), Set_True },
628     { "Sz",     optofs( no_final_msg_listing), Set_True }, /* v2.13 */
629 #if COFF_SUPPORT
630     { "safeseh",optofs( safeseh ),        Set_True },
631 #endif
632 #ifdef DEBUG_OUT
633     { "sp",     optofs( skip_preprocessor ), Set_True },
634 #endif
635     { "WX",     0,        Set_WX },
636     { "W=#",    0,        Set_W },
637 #if AMD64_SUPPORT
638     { "win64",  OFORMAT_COFF | (SFORMAT_64BIT << 8), Set_ofmt },
639 #endif
640     { "w",      0,        Set_w },
641     { "X",      optofs( ignore_include ), Set_True },
642     { "Zd",     0,        Set_Zd },
643     { "Zf",     optofs( all_symbols_public ),  Set_True },
644     { "Zg",     optofs( masm_compat_gencode ), Set_True },
645     { "Zi=#",   CVEX_NORMAL, Set_Zi },
646     { "Zm",     optofs( masm51_compat ),      Set_True },
647     { "Zne",    optofs( strict_masm_compat ), Set_True },
648     { "Zp=#",   0,        Set_Zp },
649     { "zcm",    0,        Set_zcm },
650     { "zcw",    optofs( no_cdecl_decoration ), Set_True },
651 #if OWFC_SUPPORT
652     { "zf0",    FCT_MSC,     Set_zf },
653     { "zf1",    FCT_WATCOMC, Set_zf },
654 #endif
655     { "zlc",    optofs( no_comment_data_in_code_records ), Set_True },
656     { "zld",    optofs( no_opt_farcall ),       Set_True },
657 #if COFF_SUPPORT
658     { "zlf",    optofs( no_file_entry ),        Set_True },
659     { "zlp",    optofs( no_static_procs ),      Set_True }, /* v2.10: added */
660     { "zls",    optofs( no_section_aux_entry ), Set_True },
661 #endif
662     { "Zs",     optofs( syntax_check_only ), Set_True },
663     { "zt0",    STDCALL_NONE, Set_zt },
664     { "zt1",    STDCALL_HALF, Set_zt },
665     { "zt2",    STDCALL_FULL, Set_zt },
666     { "Zv8",    optofs( masm8_proc_visibility ), Set_True },
667     { "zze",    optofs( no_export_decoration ),  Set_True },
668 #if COFF_SUPPORT
669     { "zzs",    optofs( entry_decorated ),       Set_True },
670 #endif
671 //    { NULL,     0,        0 }
672 };
673 
674 /*
675  * get a "name"
676  * type=@ : filename ( -Fd, -Fi, -Fl, -Fo, -Fw, -I )
677  * type=$ : (macro) identifier [=value] ( -D, -nc, -nd, -nm, -nt )
678  * type=0 : something else ( -0..-10 )
679  */
680 static const char *GetNameToken( char *dst, const char *str, int max, char type )
681 /*******************************************************************************/
682 {
683     bool equatefound = FALSE;
684 
685     DebugMsg(("GetNameToken( %s, %u, '%c' ) enter, rspidx=%u\n", str, max, type, rspidx ));
686     //while( isspace( *str ) ) ++str;  /* no spaces allowed! */
687 is_quote:
688     if( *str == '"' ) {
689         ++str;
690         for( ; max && *str; max-- ) {
691             if ( *str == '"' ) {
692                 ++str;
693                 break;
694             }
695             /* handle the \" case */
696             if ( *str == '\\' && *(str+1) == '"' ) {
697                 ++str;
698             }
699             *dst++ = *str++;
700         }
701     } else {
702         for( ; max; max-- ) {
703             /* v2.10: don't stop for white spaces */
704             //if ( *str == NULLC || *str == ' ' || *str == '\t' )
705             if ( *str == NULLC )
706                 break;
707             /* v2.10: don't stop for white spaces if filename is expected and true cmdline is parsed */
708             if ( ( *str == ' ' || *str == '\t' ) && ( rspidx || type != '@' ) )
709                 break;
710             if ( type == 0 )
711                 if ( *str == '-'
712 #if SWITCHCHAR
713                     || *str == '/'
714 #endif
715                    )
716                     break;
717             if ( *str == '=' && type == '$' && equatefound == FALSE ) {
718                 equatefound = TRUE;
719                 *dst++ = *str++;
720                 if (*str == '"')
721                     goto is_quote;
722             }
723             *dst++ = *str++;
724         }
725     }
726     *dst = NULLC;
727     return( str );
728 }
729 
730 /*
731  * A '@' was found in the cmdline. It's not an environment variable,
732  * so check if it is a file and, if yes, read it.
733  */
734 
735 static char *ReadParamFile( const char *name )
736 /********************************************/
737 {
738     char        *env;
739     char        *str;
740     FILE        *file;
741     int         len;
742     char        ch;
743 
744     DebugMsg(("ReadParamFile(%s) enter\n"));
745 
746     env = NULL;
747     file = fopen( name, "rb" );
748     if( file == NULL ) {
749         /* v2.10: changed to fatal error */
750         //EmitErr( CANNOT_OPEN_FILE, name, ErrnoStr() );
751         Fatal( CANNOT_OPEN_FILE, name, ErrnoStr() );
752         return( NULL );
753     }
754     len = 0;
755     if ( fseek( file, 0, SEEK_END ) == 0 ) {
756         len = ftell( file );
757         rewind( file );
758         env = MemAlloc( len + 1 );
759 #if defined(__GNUC__) /* gcc warns if return value of fread() is "ignored" */
760         if ( fread( env, 1, len, file ) )
761             ; /* 2.12: semi-colon needs extra line to avoid warning msg */
762 #else
763         fread( env, 1, len, file );
764 #endif
765         env[len] = NULLC;
766     }
767     fclose( file );
768     if ( len == 0)
769         return( NULL );
770     /* zip through characters changing \r, \n etc into ' ' */
771     str = env;
772     while( *str ) {
773         ch = *str;
774         if( ch == '\r' || ch == '\n' ) {
775             *str = ' ';
776         }
777 #if HANDLECTRLZ
778         if( ch == 0x1A ) {      /* if end of file */
779             *str = '\0';        /* - mark end of str */
780             break;
781         }
782 #endif
783         ++str;
784     }
785     return( env );
786 }
787 
788 /* current cmdline string is done, get the next one! */
789 
790 static const char *getnextcmdstring( const char **cmdline )
791 /*********************************************************/
792 {
793     const char **src;
794     const char **dst;
795 
796     /* something onto the response file stack? */
797     if ( rspidx ) {
798         rspidx--;
799         if ( cmdbuffers[rspidx] )
800             MemFree( (void *)cmdbuffers[rspidx] );
801         return( cmdsave[rspidx] );
802     }
803     for ( dst = cmdline, src = cmdline+1; *src; )
804         *dst++ = *src++;
805     *dst = *src;
806     return( *cmdline );
807 }
808 
809 static const char *GetNumber( const char *p )
810 /*******************************************/
811 {
812     OptValue = 0;
813     for( ;*p >= '0' && *p <= '9'; p++ )
814         OptValue = OptValue * 10 + *p - '0';
815     return( p );
816 }
817 
818 #if SWITCHCHAR
819 #define IsOptionDelimiter( x ) (x == ' ' || x == '-' || x == '/' || x == NULLC || x == '\t' )
820 #else
821 #define IsOptionDelimiter( x ) (x == ' ' || x == '-' || x == NULLC || x == '\t' )
822 #endif
823 
824 /* scan option table and if option is known, process it */
825 
826 static void ProcessOption( const char **cmdline, char *buffer )
827 /*************************************************************/
828 {
829     int   i;
830     int   j;
831     const char *p = *cmdline;
832     const char *opt;
833     //char  c;
834 
835     DebugMsg(("ProcessOption(%s)\n", p ));
836 
837     /* numeric option (-0, -1, ... ) handled separately since
838      * the value can be >= 10.
839      */
840     if ( *p >= '0' && *p <= '9' ) {
841         p = GetNumber( p );
842         if ( OptValue < sizeof(cpuoption)/sizeof(cpuoption[0]) ) {
843             p = GetNameToken( buffer, p, 16, 0 ); /* get optional 'p' */
844             *cmdline = p;
845             SetCpuCmdline( cpuoption[OptValue], buffer );
846             return;
847         }
848         p = *cmdline; /* v2.11: restore option pointer */
849     }
850     for( i = 0; i < ( sizeof(cmdl_options) / sizeof(cmdl_options[0]) ); i++ ) {
851         //DebugMsg(("ProcessOption(%s): %s\n", p, opt ));
852         if( *p == *cmdl_options[i].name ) {
853             for ( opt = cmdl_options[i].name+1, j = 1 ; isalnum(*opt) && *opt == p[j]; opt++, j++ );
854             /* make sure end of option is reached */
855             if ( isalnum(*opt) )
856                 continue;
857             p += j;
858             OptValue = cmdl_options[i].value;
859             //DebugMsg(("ProcessOption(%s): Option found\n", p ));
860             for( ;; opt++) {
861                 switch ( *opt ) {
862                 //case '*': /* don't know what this is supposed to do? */
863                 case NULLC:
864                     if ( !IsOptionDelimiter( *p ) )
865                         goto opt_error_exit;
866                     *cmdline = p;
867                     cmdl_options[i].function();
868                     return; /* option processed successfully */
869                     break;
870                 case '#':             /* collect a number */
871                     if( *p >= '0' && *p <= '9' )
872                         p = GetNumber( p );
873                     break;
874                 case '$':      /* collect an identifer+value */
875                 case '@':      /* collect a filename */
876                     OptName = buffer;
877 #if 0  /* v2.05: removed */
878                     if ( rspidx )
879                         p = GetNameToken( buffer, p, FILENAME_MAX - 1, *opt );
880                     else {
881                         j = strlen( p );
882                         memcpy( buffer, p, (j >= FILENAME_MAX) ? FILENAME_MAX : j + 1 );
883                         p += j;
884                     }
885 #else
886                     /* v2.10: spaces in filename now handled inside GetNameToken() */
887                     p = GetNameToken( buffer, p, FILENAME_MAX - 1, *opt );
888 #endif
889                     break;
890                 case '=':    /* collect an optional '=' */
891                     if ( *p == '=' || *p == '#' )
892                         p++;
893                     break;
894                 case '^':    /* skip spaces before argument */
895                     while ( isspace(*p) ) p++;
896                     if ( *p == NULLC ) {
897                         p = getnextcmdstring( cmdline );
898                         if ( p == NULL ) {
899                             EmitWarn( 1, MISSING_ARGUMENT_FOR_CMDLINE_OPTION );
900                             return;
901                         }
902                     }
903                     break;
904                 default:
905                     /* internal error: unknown format of option item! */
906                     DebugMsg(( "ProcessOption: unknown option specifier: %s\n", opt ));
907                     /**/myassert( 0 );
908                     break;
909                 }
910             }
911         }
912     }
913 opt_error_exit:
914     EmitWarn( 1, INVALID_CMDLINE_OPTION, *cmdline - 1 );
915     *cmdline = "";
916     return;
917 }
918 
919 #if BUILD_TARGET
920 
921 #define MAX_OS_NAME_SIZE 7
922 
923 static void set_default_build_target( void )
924 /******************************************/
925 {
926 
927     if( Options.names[OPTN_BUILD_TARGET] == NULL ) {
928         Options.names[OPTN_BUILD_TARGET] = MemAlloc( MAX_OS_NAME_SIZE + 1 );
929 #if defined(__OSI__)
930         if( __OS == OS_DOS ) {
931             strcpy( Options.names[OPTN_BUILD_TARGET], "DOS" );
932         } else if( __OS == OS_OS2 ) {
933             strcpy( Options.names[OPTN_BUILD_TARGET], "OS2" );
934         } else if( __OS == OS_NT ) {
935             strcpy( Options.names[OPTN_BUILD_TARGET], "NT" );
936         } else if( __OS == OS_WIN ) {
937             strcpy( Options.names[OPTN_BUILD_TARGET], "WINDOWS" );
938         } else {
939             strcpy( Options.names[OPTN_BUILD_TARGET], "XXX" );
940         }
941 #elif defined(__QNX__)
942         strcpy( Options.names[OPTN_BUILD_TARGET], "QNX" );
943 #elif defined(__LINUX__)
944         strcpy( Options.names[OPTN_BUILD_TARGET], "LINUX" );
945 #elif defined(__BSD__)
946         strcpy( Options.names[OPTN_BUILD_TARGET], "BSD" );
947 #elif defined(__OSX__) || defined(__APPLE__)
948         strcpy( Options.names[OPTN_BUILD_TARGET], "OSX" );
949 #elif defined(__DOS__)
950         strcpy( Options.names[OPTN_BUILD_TARGET], "DOS" );
951 #elif defined(__OS2__)
952         strcpy( Options.names[OPTN_BUILD_TARGET], "OS2" );
953 #elif defined(__NT__)
954         strcpy( Options.names[OPTN_BUILD_TARGET], "NT" );
955 #else
956         #error unknown host OS
957 #endif
958     }
959     return;
960 }
961 #endif
962 
963 /* parse cmdline:
964  * - process cmdline options
965  * - get filename argument
966  * - handle (nested) response files
967  */
968 
969 char * EXPQUAL ParseCmdline( const char **cmdline, int *pCntArgs )
970 /****************************************************************/
971 {
972     int i;
973     const char *str = *cmdline;
974     char paramfile[FILENAME_MAX];
975 
976     for ( i = 0; i < NUM_FILE_TYPES; i++ )
977         if ( Options.names[i] != NULL ) {
978             MemFree( Options.names[i] );
979             Options.names[i] = NULL;
980         }
981 
982     /* enable next line if debug log is to be active, but -dt cannot be set */
983     //Set_dt();
984 
985     for( ; str; ) {
986         switch( *str ) {
987         case ' ':
988         case '\t':
989             str++;
990             break;
991         case NULLC:
992             str = getnextcmdstring( cmdline );
993             break;
994         case '-':
995 #if SWITCHCHAR
996         case '/':
997 #endif
998             str++;
999             *cmdline = str;
1000             ProcessOption( cmdline, paramfile );
1001             (*pCntArgs)++;
1002             str = *cmdline;
1003             break;
1004         case '@':
1005             if ( rspidx >= MAX_RSP_NESTING ) {
1006                 EmitErr( NESTING_LEVEL_TOO_DEEP );
1007                 *cmdline = "";
1008                 return( NULL );
1009             }
1010             str++;
1011 #if 1 /* v2.06: was '0' in v2.05, now '1' again since it didn't work with quoted names */
1012             /* todo: might be unnecessary since v.2.10, since GetNameToken() handles spaces inside filenames differently */
1013             if ( rspidx ) {
1014                 cmdsave[rspidx] = GetNameToken( paramfile, str, sizeof( paramfile ) - 1, '@' );
1015             } else {
1016                 strcpy( paramfile, str ); /* fixme: no overflow check */
1017                 cmdsave[rspidx] = str + strlen(str);
1018             }
1019 #else
1020             cmdsave[rspidx] = GetNameToken( paramfile, str, sizeof( paramfile ) - 1, '@' );
1021 #endif
1022             cmdbuffers[rspidx] = NULL;
1023             str = NULL;
1024             if ( paramfile[0] )
1025                 str = getenv( paramfile );
1026             if( str == NULL ) {
1027                 str = ReadParamFile( paramfile );
1028                 cmdbuffers[rspidx] = str;
1029                 if ( str == NULL ) {
1030                     str = cmdsave[rspidx];
1031                     break;
1032                 }
1033             }
1034             rspidx++;
1035             break;
1036         default: /* collect  file name */
1037 #if BUILD_TARGET
1038             set_default_build_target();
1039 #endif
1040 #if 1 /* v2.06: activated (was removed in v2.05). Needed for quoted filenames */
1041             if ( rspidx ) {
1042                 str = GetNameToken( paramfile, str, sizeof( paramfile ) - 1, '@' );
1043                 get_fname( OPTN_ASM_FN, paramfile );
1044             } else {
1045                 int len;
1046                 len = strlen( str );
1047                 get_fname( OPTN_ASM_FN, str );
1048                 str += len;
1049             }
1050 #else
1051             str = GetNameToken( paramfile, str, sizeof( paramfile ) - 1, '@' );
1052             Options.names[ASM] = MemAlloc( strlen( paramfile ) + 1 );
1053             strcpy( Options.names[ASM], paramfile );
1054 #endif
1055             DebugMsg(("ParseCmdLine: file=>%s< rest=>%s<\n", Options.names[ASM], str ? str : "NULL" ));
1056             (*pCntArgs)++;
1057             *cmdline = str;
1058             return( Options.names[ASM] );
1059         }
1060     }
1061     *cmdline = str;
1062     return( NULL );
1063 }
1064 
1065 void EXPQUAL CmdlineFini( void )
1066 /******************************/
1067 /* Free resources allocated by cmdline options */
1068 {
1069     int i;
1070     DebugMsg(("CmdLineFini enter\n" ));
1071     for ( i = 0; i < NUM_FILE_TYPES; i++ ) {
1072         if ( DefaultDir[i] != NULL ) {
1073             MemFree( DefaultDir[i] );
1074             DefaultDir[i] = NULL;
1075         }
1076     }
1077     for ( i = 0; i < OPTN_LAST; i++ )
1078         if ( Options.names[i] != NULL ) {
1079             MemFree( Options.names[i] );
1080             Options.names[i] = NULL;
1081         }
1082     for ( i = 0; i < OPTQ_LAST; i++ ) {
1083         struct qitem *p;
1084         struct qitem *q;
1085         for ( q = Options.queues[i]; q; ) {
1086             p = q->next;
1087             MemFree( q );
1088             q = p;
1089         }
1090         Options.queues[i] = NULL;
1091     }
1092     DebugMsg(("CmdLineFini exit\n" ));
1093     return;
1094 }
1095 
1096