| File: | dmake/dmake.c | 
| Location: | line 682, column 47 | 
| Description: | Value stored to 'fname' is never read | 
| 1 | /* | 
| 2 | -- | 
| 3 | -- SYNOPSIS | 
| 4 | -- The main program. | 
| 5 | -- | 
| 6 | -- DESCRIPTION | 
| 7 | -- | 
| 8 | -- dmake [-#dbug_string] [ options ] | 
| 9 | -- [ macro definitions ] [ target ... ] | 
| 10 | -- | 
| 11 | -- This file contains the main command line parser for the | 
| 12 | -- make utility. The valid flags recognized are as follows: | 
| 13 | -- | 
| 14 | -- -f file - use file as the makefile | 
| 15 | -- -C file - duplicate console output to file (MSDOS only) | 
| 16 | -- -K file - .KEEP_STATE file | 
| 17 | -- -#dbug_string - dump out debugging info, see below | 
| 18 | -- -v[cdfimrtw] - verbose, print what we are doing, as we do it | 
| 19 | -- -m[trae] - measure timing information | 
| 20 | -- | 
| 21 | -- options: (can be catenated, ie -irn == -i -r -n) | 
| 22 | -- | 
| 23 | -- -A - enable AUGMAKE special target mapping | 
| 24 | -- -B - enable non-use of TABS to start recipe lines | 
| 25 | -- -c - use non-standard comment scanning | 
| 26 | -- -d - do not use directory cache | 
| 27 | -- -i - ignore errors | 
| 28 | -- -n - trace and print, do not execute commands | 
| 29 | -- -t - touch, update dates without executing commands | 
| 30 | -- -T - do not apply transitive closure on inference rules | 
| 31 | -- -r - don't use internal rules | 
| 32 | -- -s - do your work silently | 
| 33 | -- -S - force Sequential make, overrides -P | 
| 34 | -- -q - check if target is up to date. Does not | 
| 35 | -- do anything. Returns 0 if up to date, -1 | 
| 36 | -- otherwise. | 
| 37 | -- -p - print out a version of the makefile | 
| 38 | -- -P# - set value of MAXPROCESS | 
| 39 | -- -E - define environment strings as macros | 
| 40 | -- -e - as -E but done after parsing makefile | 
| 41 | -- -u - force unconditional update of target | 
| 42 | -- -k - make all independent targets even if errors | 
| 43 | -- -V - print out this make version number | 
| 44 | -- -M - Microsoft make compatibility, (* disabled *) | 
| 45 | -- -h - print out usage info | 
| 46 | -- -x - export macro defs to environment | 
| 47 | -- -X - ignore #! lines found in makefile | 
| 48 | -- | 
| 49 | -- NOTE: - #ddbug_string is only availabe for versions of dmake that | 
| 50 | -- have been compiled with -DDBUG switch on. Not the case for | 
| 51 | -- distributed versions. Any such versions must be linked | 
| 52 | -- together with a version of Fred Fish's debug code. | 
| 53 | -- | 
| 54 | -- NOTE: - in order to compile the code the include file stddef.h | 
| 55 | -- must be shipped with the bundled code. | 
| 56 | -- | 
| 57 | -- AUTHOR | 
| 58 | -- Dennis Vadura, dvadura@dmake.wticorp.com | 
| 59 | -- | 
| 60 | -- WWW | 
| 61 | -- http://dmake.wticorp.com/ | 
| 62 | -- | 
| 63 | -- COPYRIGHT | 
| 64 | -- Copyright (c) 1996,1997 by WTI Corp. All rights reserved. | 
| 65 | -- | 
| 66 | -- This program is NOT free software; you can redistribute it and/or | 
| 67 | -- modify it under the terms of the Software License Agreement Provided | 
| 68 | -- in the file <distribution-root>/readme/license.txt. | 
| 69 | -- | 
| 70 | -- LOG | 
| 71 | -- Use cvs log to obtain detailed change logs. | 
| 72 | */ | 
| 73 | |
| 74 | /* Set this flag to one, and the global variables in vextern.h will not | 
| 75 | * be defined as 'extern', instead they will be defined as global vars | 
| 76 | * when this module is compiled. */ | 
| 77 | #define _DEFINE_GLOBALS_1 1 | 
| 78 | |
| 79 | #include "extern.h" /* this includes config.h */ | 
| 80 | #include "sysintf.h" | 
| 81 | |
| 82 | #ifndef MSDOS | 
| 83 | #define USAGE"Usage:\n%s [-P#] [-{f|K} file] [-{w|W} target ...] [macro[!][[*][+][:]]=value ...]\n" \ | 
| 84 | "Usage:\n%s [-P#] [-{f|K} file] [-{w|W} target ...] [macro[!][[*][+][:]]=value ...]\n" | 
| 85 | #define USAGE2"%s [-v[cdfimrtw]] [-m[trae]] [-ABcdeEghiknpqrsStTuVxX] [target ...]\n" \ | 
| 86 | "%s [-v[cdfimrtw]] [-m[trae]] [-ABcdeEghiknpqrsStTuVxX] [target ...]\n" | 
| 87 | #else | 
| 88 | #define USAGE"Usage:\n%s [-P#] [-{f|K} file] [-{w|W} target ...] [macro[!][[*][+][:]]=value ...]\n" \ | 
| 89 | "Usage:\n%s [-P#] [-{f|C|K} file] [-{w|W} target ...] [macro[!][[*][+][:]]=value ...]\n" | 
| 90 | #define USAGE2"%s [-v[cdfimrtw]] [-m[trae]] [-ABcdeEghiknpqrsStTuVxX] [target ...]\n" \ | 
| 91 | "%s [-v[cdfimrtw]] [-m[trae]] [-ABcdeEghiknpqrsStTuVxX] [target ...]\n" | 
| 92 | #endif | 
| 93 | |
| 94 | /* We don't use va_end at all, so define it out so that it doesn't produce | 
| 95 | * lots of "Value not used" warnings. */ | 
| 96 | #ifdef va_end | 
| 97 | #undef va_end | 
| 98 | #endif | 
| 99 | #define va_end(expand_to_null) | 
| 100 | |
| 101 | /* Make certain that ARG macro is correctly defined. */ | 
| 102 | #ifdef ARG | 
| 103 | #undef ARG | 
| 104 | #endif | 
| 105 | #define ARG(a,b)a b a b | 
| 106 | |
| 107 | static char *sccid = "Copyright (c) 1990,...,1997 by WTI Corp."; | 
| 108 | static char _warn = TRUE1; /* warnings on by default */ | 
| 109 | |
| 110 | static void _do_VPATH(); | 
| 111 | static void _do_ReadEnvironment(); | 
| 112 | #if !defined(__GNUC__4) && !defined(__IBMC__) | 
| 113 | static void _do_f_flag ANSI((char, char *, char **))(char, char *, char **); | 
| 114 | #else | 
| 115 | static void _do_f_flag ANSI((int, char *, char **))(int, char *, char **); | 
| 116 | #endif | 
| 117 | |
| 118 | PUBLIC int | 
| 119 | main(argc, argv) | 
| 120 | int argc; | 
| 121 | char **argv; | 
| 122 | { | 
| 123 | #ifdef MSDOS | 
| 124 | char* std_fil_name = NIL(char)((char*)((void*)0)); | 
| 125 | #endif | 
| 126 | |
| 127 | char* fil_name = NIL(char)((char*)((void*)0)); | 
| 128 | char* state_name = NIL(char)((char*)((void*)0)); | 
| 129 | char* whatif = NIL(char)((char*)((void*)0)); | 
| 130 | char* cmdmacs; | 
| 131 | char* targets; | 
| 132 | STRINGPTR cltarget = NIL(STRING)((STRING*)((void*)0)); /* list of targets from command line. */ | 
| 133 | STRINGPTR cltarget_first = NIL(STRING)((STRING*)((void*)0)); /* Pointer to first element. */ | 
| 134 | FILE* mkfil; | 
| 135 | int ex_val; | 
| 136 | int m_export; | 
| 137 | |
| 138 | /* Uncomment the following line to pass commands to the DBUG engine | 
| 139 | * before the command line switches (-#..) are evaluated. */ | 
| 140 | /* | 
| 141 | DB_PUSH("d,path"); | 
| 142 | */ | 
| 143 | DB_ENTER("main"); | 
| 144 | |
| 145 | /* Initialize Global variables to their default values */ | 
| 146 | Prolog(argc, argv); | 
| 147 | /* Set internal macros to their initial values, some are changed | 
| 148 | * later again by Make_rules() that parses the values from ruletab.c. */ | 
| 149 | Create_macro_vars(); | 
| 150 | Catch_signals(Quit); | 
| 151 | |
| 152 | /* This macro is only defined for some OSs, see sysintf.c for details * | 
| 153 | * and NULL if undefined. */ | 
| 154 | Def_macro("ABSMAKECMD", AbsPname, M_PRECIOUS0x0002|M_NOEXPORT0x0040|M_EXPANDED0x0008 ); | 
| 155 | |
| 156 | Def_macro( "MAKECMD", Pname, M_PRECIOUS0x0002|M_NOEXPORT0x0040|M_EXPANDED0x0008 ); | 
| 157 | Pname = Basename(Pname); | 
| 158 | |
| 159 | DB_PROCESS(Pname); | 
| 160 | (void) setvbuf(stdoutstdout, NULL((void*)0), _IOLBF1, BUFSIZ8192); /* stdout line buffered */ | 
| 161 | |
| 162 | Continue = FALSE0; | 
| 163 | Comment = FALSE0; | 
| 164 | Get_env = FALSE0; | 
| 165 | Force = FALSE0; | 
| 166 | Target = FALSE0; | 
| 167 | If_expand = FALSE0; | 
| 168 | Listing = FALSE0; | 
| 169 | Readenv = FALSE0; | 
| 170 | Rules = TRUE1; | 
| 171 | Trace = FALSE0; | 
| 172 | Touch = FALSE0; | 
| 173 | Check = FALSE0; | 
| 174 | Microsoft = FALSE0; | 
| 175 | Makemkf = FALSE0; | 
| 176 | UseWinpath= FALSE0; | 
| 177 | No_exec = FALSE0; | 
| 178 | m_export = FALSE0; | 
| 179 | cmdmacs = NIL(char)((char*)((void*)0)); | 
| 180 | targets = NIL(char)((char*)((void*)0)); | 
| 181 | Is_exec_shell = FALSE0; | 
| 182 | Shell_exec_target = NIL(CELL)((CELL*)((void*)0)); | 
| 183 | stdout_redir = NIL(FILE)((FILE*)((void*)0)); | 
| 184 | |
| 185 | /* Get fd for for @@-recipe silencing. */ | 
| 186 | if( (zerofd = open(NULLDEV"/dev/null", O_WRONLY01)) == -1 ) | 
| 187 | Fatal( "Error opening %s !", NULLDEV"/dev/null" ); | 
| 188 | |
| 189 | Verbose = V_NOFLAG0x00; | 
| 190 | Measure = M_NOFLAG0x00; | 
| 191 | Transitive = TRUE1; | 
| 192 | Nest_level = 0; | 
| 193 | Line_number = 0; | 
| 194 | Suppress_temp_file = FALSE0; | 
| 195 | Skip_to_eof = FALSE0; | 
| 196 | |
| 197 | while( --argc > 0 ) { | 
| 198 | register char *p; | 
| 199 | char *q; | 
| 200 | |
| 201 | if( *(p = *++argv) == '-' ) { | 
| 202 | if( p[1] == '\0' ) Fatal("Missing option letter"); | 
| 203 | |
| 204 | /* copy options to Buffer for $(MFLAGS), strip 'f' and 'C'*/ | 
| 205 | q = strchr(Buffer, '\0'); | 
| 206 | while (*p != '\0') { | 
| 207 | char c = (*q++ = *p++); | 
| 208 | if( c == 'f' || c == 'C' ) q--; | 
| 209 | } | 
| 210 | |
| 211 | if( *(q-1) == '-' ) | 
| 212 | q--; | 
| 213 | else | 
| 214 | *q++ = ' '; | 
| 215 | |
| 216 | *q = '\0'; | 
| 217 | |
| 218 | for( p = *argv+1; *p; p++) switch (*p) { | 
| 219 | case 'f': | 
| 220 | _do_f_flag( 'f', *++argv, &fil_name ); argc--; | 
| 221 | break; | 
| 222 | |
| 223 | #if defined(MSDOS) && !defined(OS2) | 
| 224 | case 'C': | 
| 225 | _do_f_flag( 'C', *++argv, &std_fil_name ); argc--; | 
| 226 | Hook_std_writes( std_fil_name ); | 
| 227 | break; | 
| 228 | #endif | 
| 229 | |
| 230 | case 'K': | 
| 231 | _do_f_flag( 'K', *++argv, &state_name ); argc--; | 
| 232 | Def_macro(".KEEP_STATE", state_name, M_EXPANDED0x0008|M_PRECIOUS0x0002); | 
| 233 | break; | 
| 234 | |
| 235 | case 'W': | 
| 236 | case 'w': { | 
| 237 | CELLPTR wif; | 
| 238 | _do_f_flag( 'w', *++argv, &whatif ); argc--; | 
| 239 | wif = Def_cell(whatif); | 
| 240 | wif->ce_attr |= A_WHATIF0x04000000; | 
| 241 | whatif = NIL(char)((char*)((void*)0)); | 
| 242 | |
| 243 | if ( *p == 'W') | 
| 244 | break; | 
| 245 | } | 
| 246 | /*FALLTHRU*/ | 
| 247 | |
| 248 | case 'n': | 
| 249 | Trace = TRUE1; | 
| 250 | break; | 
| 251 | |
| 252 | case 'k': Continue = TRUE1; break; | 
| 253 | case 'c': Comment = TRUE1; break; | 
| 254 | case 'p': Listing = TRUE1; break; | 
| 255 | case 'r': Rules = FALSE0; break; | 
| 256 | case 't': Touch = TRUE1; break; | 
| 257 | case 'q': Check = TRUE1; break; | 
| 258 | case 'u': Force = TRUE1; break; | 
| 259 | case 'x': m_export = TRUE1; break; | 
| 260 | case 'X': No_exec = TRUE1; break; | 
| 261 | case 'T': Transitive = FALSE0; break; | 
| 262 | case 'e': Get_env = 'e'; break; | 
| 263 | case 'E': Get_env = 'E'; break; | 
| 264 | |
| 265 | case 'V': Version(); Quit(0); break; | 
| 266 | case 'A': Def_macro("AUGMAKE", "y", M_EXPANDED0x0008); break; | 
| 267 | case 'B': Def_macro(".NOTABS", "y", M_EXPANDED0x0008); break; | 
| 268 | case 'i': Def_macro(".IGNORE", "y", M_EXPANDED0x0008); break; | 
| 269 | case 's': Def_macro(".SILENT", "y", M_EXPANDED0x0008); break; | 
| 270 | case 'S': Def_macro(".SEQUENTIAL", "y", M_EXPANDED0x0008); break; | 
| 271 | case 'g': Def_macro(".IGNOREGROUP","y", M_EXPANDED0x0008); break; | 
| 272 | case 'd': Def_macro(".DIRCACHE",NIL(char)((char*)((void*)0)),M_EXPANDED0x0008); break; | 
| 273 | |
| 274 | case 'v': | 
| 275 | if( p[-1] != '-' ) Usage(TRUE1); | 
| 276 | while( p[1] ) switch( *++p ) { | 
| 277 | case 'c': Verbose |= V_DIR_CACHE0x04; break; | 
| 278 | case 'd': Verbose |= V_DIR_SET0x02; break; | 
| 279 | case 'f': Verbose |= V_FILE_IO0x20; break; | 
| 280 | case 'i': Verbose |= V_INFER0x08; break; | 
| 281 | case 'm': Verbose |= V_MAKE0x10; break; | 
| 282 | case 'r': Verbose |= V_FORCEECHO0x80; break; | 
| 283 | case 't': Verbose |= V_LEAVE_TMP0x01; break; | 
| 284 | case 'w': Verbose |= V_WARNALL0x40; break; | 
| 285 | |
| 286 | default: Usage(TRUE1); break; | 
| 287 | } | 
| 288 | if( !Verbose ) Verbose = V_ALL(0x01 | 0x02 | 0x08 | 0x10 | 0x20 | 0x04 | 0x40 | 0x80); | 
| 289 | if( Verbose & V_FORCEECHO0x80 ) { | 
| 290 | HASHPTR hp; | 
| 291 | /* This cleans the .SILENT setting */ | 
| 292 | hp = Def_macro(".SILENT", "", M_EXPANDED0x0008); | 
| 293 | /* This overrides the bitmask for further occurrences of | 
| 294 | * .SILENT to "no bits allowed", see bit variables in the | 
| 295 | * set_macro_value() definition in dag.c. | 
| 296 | * The bitmask is already set by Create_macro_vars() in | 
| 297 | * imacs.c and is overridden for the V_FORCEECHO case. */ | 
| 298 | hp->MV_MASKvar.mv_mask = A_DEFAULT0x00000; | 
| 299 | } | 
| 300 | break; | 
| 301 | |
| 302 | case 'm': | 
| 303 | if( p[-1] != '-' ) Usage(TRUE1); | 
| 304 | while( p[1] ) switch( *++p ) { | 
| 305 | case 't': Measure |= M_TARGET0x01; break; | 
| 306 | case 'r': Measure |= M_RECIPE0x02; break; | 
| 307 | case 'a': Measure |= M_ABSPATH0x04; break; | 
| 308 | case 'e': Measure |= M_SHELLESC0x08; break; | 
| 309 | |
| 310 | default: Usage(TRUE1); break; | 
| 311 | } | 
| 312 | if( !Measure ) Measure = M_TARGET0x01; | 
| 313 | break; | 
| 314 | |
| 315 | case 'P': | 
| 316 | if( p[1] ) { | 
| 317 | /* Only set MAXPROCESS if -S flag is *not* used. */ | 
| 318 | if( !(Glob_attr & A_SEQ0x00200) ) { | 
| 319 | Def_macro( "MAXPROCESS", p+1, M_MULTI0x0004|M_EXPANDED0x0008 ); | 
| 320 | } | 
| 321 | p += strlen(p)-1; | 
| 322 | } | 
| 323 | else | 
| 324 | Fatal( "Missing number for -P flag" ); | 
| 325 | break; | 
| 326 | |
| 327 | #ifdef DBUG | 
| 328 | case '#': | 
| 329 | DB_PUSH(p+1); | 
| 330 | p += strlen(p)-1; | 
| 331 | break; | 
| 332 | #endif | 
| 333 | |
| 334 | case 'h': Usage(FALSE0); break; | 
| 335 | case 0: break; /* lone - */ | 
| 336 | default: Usage(TRUE1); break; | 
| 337 | } | 
| 338 | } | 
| 339 | else if( (q = strchr(p, '=')) != NIL(char)((char*)((void*)0)) ) { | 
| 340 | cmdmacs = DmStrAdd( cmdmacs, DmStrDup2(p), TRUE1 ); | 
| 341 | /* Macros defined on the command line are marked precious. | 
| 342 | * FIXME: The exception for += appears to be bogus. */ | 
| 343 | Parse_macro( p, (q[-1]!='+')?M_PRECIOUS0x0002:M_DEFAULT0x0000 ); | 
| 344 | } | 
| 345 | else { | 
| 346 | /* Remember the targets from the command line. */ | 
| 347 | register STRINGPTR nsp; | 
| 348 | |
| 349 | targets = DmStrAdd( targets, DmStrDup(p), TRUE1 ); | 
| 350 | |
| 351 | TALLOC(nsp, 1, STRING)if ((nsp = (STRING*) calloc((unsigned int)(1), (size_t)sizeof (STRING))) == (STRING*)0) {No_ram();}; | 
| 352 | nsp->st_string = DmStrDup( p ); | 
| 353 | nsp->st_next = NIL(STRING)((STRING*)((void*)0)); | 
| 354 | |
| 355 | if(cltarget != NIL(STRING)((STRING*)((void*)0)) ) | 
| 356 | cltarget->st_next = nsp; | 
| 357 | else | 
| 358 | cltarget_first = nsp; | 
| 359 | |
| 360 | cltarget = nsp; | 
| 361 | } | 
| 362 | } | 
| 363 | |
| 364 | Def_macro( "MAKEMACROS", cmdmacs, M_PRECIOUS0x0002|M_NOEXPORT0x0040 ); | 
| 365 | Def_macro( "MAKETARGETS", targets, M_PRECIOUS0x0002|M_NOEXPORT0x0040 ); | 
| 366 | if( cmdmacs != NIL(char)((char*)((void*)0)) ) FREE(cmdmacs)free((char*)(cmdmacs)); | 
| 367 | if( targets != NIL(char)((char*)((void*)0)) ) FREE(targets)free((char*)(targets)); | 
| 368 | |
| 369 | Def_macro( "MFLAGS", Buffer, M_PRECIOUS0x0002|M_NOEXPORT0x0040 ); | 
| 370 | Def_macro( "%", "$@", M_PRECIOUS0x0002|M_NOEXPORT0x0040 ); | 
| 371 | |
| 372 | if( *Buffer ) Def_macro( "MAKEFLAGS", Buffer+1, M_PRECIOUS0x0002|M_NOEXPORT0x0040 ); | 
| 373 | |
| 374 | _warn = FALSE0; /* disable warnings for builtin rules */ | 
| 375 | Target = TRUE1; /* make sure we don't mark any of the default rules as | 
| 376 | * potential targets. */ | 
| 377 | Make_rules(); /* Parse the strings stored in Rule_tab. */ | 
| 378 | _warn = TRUE1; | 
| 379 | |
| 380 | /* If -r was not given find and parse startup-makefile. */ | 
| 381 | if( Rules ) | 
| 382 | { | 
| 383 | char *fname = NIL(char)((char*)((void*)0)); | 
| 384 | |
| 385 | /* Search_file() also checks the environment variable. */ | 
| 386 | if( (mkfil=Search_file("MAKESTARTUP", &fname)) != NIL(FILE)((FILE*)((void*)0)) ) | 
| 387 | { | 
| 388 | Parse(mkfil); | 
| 389 | Def_macro( "MAKESTARTUP", fname, M_EXPANDED0x0008|M_MULTI0x0004|M_FORCE0x0080 ); | 
| 390 | } | 
| 391 | else | 
| 392 | Fatal( "Configuration file `%s' not found", fname ); | 
| 393 | if ( fname != NIL(char)((char*)((void*)0))) { FREE( fname )free((char*)(fname)); fname = NIL(char)((char*)((void*)0)); } | 
| 394 | } | 
| 395 | |
| 396 | /* Define the targets set on the command line now. */ | 
| 397 | Target = FALSE0; /* Will be set to TRUE when the default targets are set. */ | 
| 398 | for( cltarget = cltarget_first; cltarget != NIL(STRING)((STRING*)((void*)0)); ) { | 
| 399 | CELLPTR cp; | 
| 400 | STRINGPTR nta = cltarget->st_next; | 
| 401 | |
| 402 | Add_prerequisite(Targets, cp = Def_cell(cltarget->st_string), | 
| 403 | FALSE0, FALSE0); | 
| 404 | cp->ce_flag |= F_TARGET0x0008; | 
| 405 | cp->ce_attr |= A_FRINGE0x100000; | 
| 406 | Target = TRUE1; | 
| 407 | |
| 408 | FREE(cltarget->st_string)free((char*)(cltarget->st_string)); | 
| 409 | FREE(cltarget)free((char*)(cltarget)); | 
| 410 | cltarget = nta; | 
| 411 | } | 
| 412 | |
| 413 | if( Get_env == 'E' ) _do_ReadEnvironment(); | 
| 414 | |
| 415 | /* Search for and parse user makefile. */ | 
| 416 | if( fil_name != NIL(char)((char*)((void*)0)) ) | 
| 417 | mkfil = Openfile( fil_name, FALSE0, TRUE1 ); | 
| 418 | else { | 
| 419 | /* Search .MAKEFILES dependent list looking for a makefile. | 
| 420 | */ | 
| 421 | register CELLPTR cp; | 
| 422 | |
| 423 | cp = Def_cell( ".MAKEFILES" ); | 
| 424 | mkfil = TryFiles(cp->CE_PRQce_prq); | 
| 425 | } | 
| 426 | |
| 427 | if( mkfil != NIL(FILE)((FILE*)((void*)0)) ) { | 
| 428 | char *f = Filename(); | 
| 429 | char *p; | 
| 430 | |
| 431 | if( strcmp(f, "stdin") == 0 ) f = "-"; | 
| 432 | Def_macro( "MAKEFILE", p = DmStrAdd( "-f", f, FALSE0 ), M_PRECIOUS0x0002|M_NOEXPORT0x0040 ); FREE(p)free((char*)(p)); | 
| 433 | Parse( mkfil ); | 
| 434 | } | 
| 435 | else if( !Rules ) | 
| 436 | Fatal( "No `makefile' present" ); | 
| 437 | |
| 438 | if( Nest_level ) Fatal( "Missing .END for .IF" ); | 
| 439 | if( Get_env == 'e' ) _do_ReadEnvironment(); | 
| 440 | |
| 441 | _do_VPATH(); /* kludge it up with .SOURCE */ | 
| 442 | |
| 443 | if( Listing ) Dump(); /* print out the structures */ | 
| 444 | if( Trace ) Glob_attr &= ~A_SILENT0x00002; /* make sure we see the trace */ | 
| 445 | |
| 446 | if( !Target ) | 
| 447 | Fatal( "No target" ); | 
| 448 | else { | 
| 449 | Test_circle( Root, TRUE1 ); | 
| 450 | Check_circle_dfa(); | 
| 451 | } | 
| 452 | |
| 453 | if( m_export ) { | 
| 454 | int i; | 
| 455 | |
| 456 | for( i=0; i<HASH_TABLE_SIZE200; ++i ) { | 
| 457 | HASHPTR hp = Macs[i]; | 
| 458 | |
| 459 | while( hp ) { | 
| 460 | if( !(hp->ht_flag & M_NOEXPORT0x0040) && hp->ht_value != NIL(char)((char*)((void*)0)) ) | 
| 461 | if( Write_env_string(hp->ht_name, hp->ht_value) != 0 ) | 
| 462 | Warning( "Could not export %s", hp->ht_name ); | 
| 463 | hp = hp->ht_next; | 
| 464 | } | 
| 465 | } | 
| 466 | } | 
| 467 | |
| 468 | if( Buffer != NIL(char)((char*)((void*)0)) ) {FREE( Buffer )free((char*)(Buffer)); Buffer = NIL(char)((char*)((void*)0));} | 
| 469 | if( Trace ) Def_macro(".SEQUENTIAL", "y", M_EXPANDED0x0008); | 
| 470 | |
| 471 | ex_val = Make_targets(); | 
| 472 | |
| 473 | Clear_signals(); | 
| 474 | |
| 475 | /* Close fd for for @@-recipe silencing. */ | 
| 476 | if( close(zerofd) ) | 
| 477 | Fatal( "Error closing %s !", NULLDEV"/dev/null" ); | 
| 478 | Epilog(ex_val); /* Does not return -- EVER */ | 
| 479 | return 0; | 
| 480 | } | 
| 481 | |
| 482 | |
| 483 | static void | 
| 484 | _do_f_flag( flag, name, fname ) | 
| 485 | char flag; | 
| 486 | char *name; | 
| 487 | char **fname; | 
| 488 | { | 
| 489 | if( *fname == NIL(char)((char*)((void*)0)) ) { | 
| 490 | if( name != NIL(char)((char*)((void*)0)) ) { | 
| 491 | *fname = name; | 
| 492 | } else | 
| 493 | Fatal("No file name for -%c", flag); | 
| 494 | } else | 
| 495 | Fatal("Only one `-%c file' allowed", flag); | 
| 496 | } | 
| 497 | |
| 498 | |
| 499 | static void | 
| 500 | _do_ReadEnvironment() | 
| 501 | { | 
| 502 | t_attr saveattr = Glob_attr; | 
| 503 | |
| 504 | Glob_attr |= A_SILENT0x00002; | 
| 505 | ReadEnvironment(); | 
| 506 | Glob_attr = saveattr; | 
| 507 | } | 
| 508 | |
| 509 | |
| 510 | static void | 
| 511 | _do_VPATH() | 
| 512 | { | 
| 513 | HASHPTR hp; | 
| 514 | char *_rl[2]; | 
| 515 | extern char **Rule_tab; | 
| 516 | |
| 517 | hp = GET_MACRO("VPATH")Get_name("VPATH", Macs, 0); | 
| 518 | if( hp == NIL(HASH)((HASH*)((void*)0)) ) return; | 
| 519 | |
| 520 | _rl[0] = ".SOURCE :^ $(VPATH:s/:/ /)"; | 
| 521 | _rl[1] = NIL(char)((char*)((void*)0)); | 
| 522 | |
| 523 | Rule_tab = _rl; | 
| 524 | Parse( NIL(FILE)((FILE*)((void*)0)) ); | 
| 525 | } | 
| 526 | |
| 527 | |
| 528 | /* The file table and pointer to the next FREE slot for use by both | 
| 529 | Openfile and Closefile. Each open stacks the new file onto the open | 
| 530 | file stack, and a corresponding close will close the passed file, and | 
| 531 | return the next file on the stack. The maximum number of nested | 
| 532 | include files is limited by the value of MAX_INC_DEPTH */ | 
| 533 | |
| 534 | static struct { | 
| 535 | FILE *file; /* file pointer */ | 
| 536 | char *name; /* name of file */ | 
| 537 | int numb; /* line number */ | 
| 538 | } ftab[ MAX_INC_DEPTH10 ]; | 
| 539 | |
| 540 | static int next_file_slot = 0; | 
| 541 | |
| 542 | /* Set the proper macro value to reflect the depth of the .INCLUDE directives | 
| 543 | * and the name of the file we are reading. | 
| 544 | */ | 
| 545 | static void | 
| 546 | _set_inc_depth() | 
| 547 | { | 
| 548 | char buf[10]; | 
| 549 | sprintf( buf, "%d", next_file_slot ); | 
| 550 | Def_macro( "INCDEPTH", buf, M_MULTI0x0004|M_NOEXPORT0x0040 ); | 
| 551 | Def_macro( "INCFILENAME", | 
| 552 | next_file_slot ? ftab[next_file_slot-1].name : "", | 
| 553 | M_MULTI0x0004|M_NOEXPORT0x0040|M_EXPANDED0x0008 ); | 
| 554 | } | 
| 555 | |
| 556 | |
| 557 | PUBLIC FILE * | 
| 558 | Openfile(name, mode, err)/* | 
| 559 | =========================== | 
| 560 | This routine opens a file for input or output depending on mode. | 
| 561 | If the file name is `-' then it returns standard input. | 
| 562 | The file is pushed onto the open file stack. */ | 
| 563 | char *name; | 
| 564 | int mode; | 
| 565 | int err; | 
| 566 | { | 
| 567 | FILE *fil; | 
| 568 | |
| 569 | DB_ENTER("Openfile"); | 
| 570 | |
| 571 | if( name == NIL(char)((char*)((void*)0)) || !*name ) { | 
| 572 | if( !err ) | 
| 573 | DB_RETURN(NIL(FILE))return (((FILE*)((void*)0))); | 
| 574 | else | 
| 575 | Fatal( "Openfile: NIL filename" ); | 
| 576 | } | 
| 577 | |
| 578 | if( next_file_slot == MAX_INC_DEPTH10 ) | 
| 579 | Fatal( "Too many open files. Max nesting level is %d.", MAX_INC_DEPTH10); | 
| 580 | |
| 581 | DB_PRINT( "io", ("Opening file [%s], in slot %d", name, next_file_slot) ); | 
| 582 | |
| 583 | if( strcmp("-", name) == 0 ) { | 
| 584 | name = "stdin"; | 
| 585 | fil = stdinstdin; | 
| 586 | } | 
| 587 | else | 
| 588 | fil = fopen( name, mode ? "w":"r" ); | 
| 589 | |
| 590 | if( Verbose & V_FILE_IO0x20 ) | 
| 591 | printf( "%s: Openning [%s] for %s", Pname, name, mode?"write":"read" ); | 
| 592 | |
| 593 | if( fil == NIL(FILE)((FILE*)((void*)0)) ) { | 
| 594 | if( Verbose & V_FILE_IO0x20 ) printf( " (fail)\n" ); | 
| 595 | if( err ) | 
| 596 | Fatal( mode ? "Cannot open file %s for write" : "File %s not found", | 
| 597 | name ); | 
| 598 | } | 
| 599 | else { | 
| 600 | if( Verbose & V_FILE_IO0x20 ) printf( " (success)\n" ); | 
| 601 | ftab[next_file_slot].file = fil; | 
| 602 | ftab[next_file_slot].numb = Line_number; | 
| 603 | ftab[next_file_slot++].name = DmStrDup(name); | 
| 604 | Line_number = 0; | 
| 605 | _set_inc_depth(); | 
| 606 | } | 
| 607 | |
| 608 | DB_RETURN(fil)return (fil); | 
| 609 | } | 
| 610 | |
| 611 | |
| 612 | PUBLIC FILE * | 
| 613 | Closefile()/* | 
| 614 | ============= | 
| 615 | This routine is used to close the last file opened. This forces make | 
| 616 | to open files in a last open first close fashion. It returns the | 
| 617 | file pointer to the next file on the stack, and NULL if the stack is empty.*/ | 
| 618 | { | 
| 619 | DB_ENTER("Closefile"); | 
| 620 | |
| 621 | if( !next_file_slot ) | 
| 622 | DB_RETURN( NIL(FILE) )return (((FILE*)((void*)0))); | 
| 623 | |
| 624 | if( ftab[--next_file_slot].file != stdinstdin ) { | 
| 625 | DB_PRINT( "io", ("Closing file in slot %d", next_file_slot) ); | 
| 626 | |
| 627 | if( Verbose & V_FILE_IO0x20 ) | 
| 628 | printf( "%s: Closing [%s]\n", Pname, ftab[next_file_slot].name ); | 
| 629 | |
| 630 | fclose( ftab[next_file_slot].file ); | 
| 631 | FREE( ftab[next_file_slot].name )free((char*)(ftab[next_file_slot].name)); | 
| 632 | } | 
| 633 | |
| 634 | _set_inc_depth(); | 
| 635 | |
| 636 | if( next_file_slot > 0 ) { | 
| 637 | Line_number = ftab[next_file_slot].numb; | 
| 638 | DB_RETURN( ftab[next_file_slot-1].file )return (ftab[next_file_slot-1].file); | 
| 639 | } | 
| 640 | else | 
| 641 | Line_number = 0; | 
| 642 | |
| 643 | DB_RETURN( NIL(FILE) )return (((FILE*)((void*)0))); | 
| 644 | } | 
| 645 | |
| 646 | |
| 647 | PUBLIC FILE * | 
| 648 | Search_file( macname, rname ) | 
| 649 | char *macname; | 
| 650 | char **rname; | 
| 651 | { | 
| 652 | HASHPTR hp; | 
| 653 | FILE *fil = NIL(FILE)((FILE*)((void*)0)); | 
| 654 | char *fname = NIL(char)((char*)((void*)0)); | 
| 655 | char *ename = NIL(char)((char*)((void*)0)); | 
| 656 | |
| 657 | /* order of precedence is: | 
| 658 | * | 
| 659 | * MACNAME from command line (precious is marked) | 
| 660 | * ... via MACNAME:=filename definition. | 
| 661 | * MACNAME from environment | 
| 662 | * MACNAME from builtin rules (not precious) | 
| 663 | */ | 
| 664 | |
| 665 | if( (hp = GET_MACRO(macname)Get_name(macname, Macs, 0)) != NIL(HASH)((HASH*)((void*)0)) ) { | 
| 666 | /* Only expand if needed. */ | 
| 667 | if( hp->ht_flag & M_EXPANDED0x0008 ) { | 
| 668 | ename = fname = DmStrDup(hp->ht_value); | 
| 669 | } else { | 
| 670 | ename = fname = Expand(hp->ht_value); | 
| 671 | } | 
| 672 | |
| 673 | if( hp->ht_flag & M_PRECIOUS0x0002 ) fil = Openfile(fname, FALSE0, FALSE0); | 
| 674 | } | 
| 675 | |
| 676 | if( fil == NIL(FILE)((FILE*)((void*)0)) ) { | 
| 677 | fname=Expand(Read_env_string(macname)); | 
| 678 | if( (fil = Openfile(fname, FALSE0, FALSE0)) != NIL(FILE)((FILE*)((void*)0)) ) FREE(ename)free((char*)(ename)); | 
| 679 | } | 
| 680 | |
| 681 | if( fil == NIL(FILE)((FILE*)((void*)0)) && hp != NIL(HASH)((HASH*)((void*)0)) ) { | 
| 682 | if ( fname != NIL(char)((char*)((void*)0)) ) { FREE(fname)free((char*)(fname)); fname = NIL(char)((char*)((void*)0)); } | 
| Value stored to 'fname' is never read | |
| 683 | fil = Openfile(fname=ename, FALSE0, FALSE0); | 
| 684 | } | 
| 685 | |
| 686 | if( rname ) *rname = fname; | 
| 687 | |
| 688 | return(fil); | 
| 689 | } | 
| 690 | |
| 691 | |
| 692 | PUBLIC char * | 
| 693 | Filename()/* | 
| 694 | ============ | 
| 695 | Return name of file on top of stack */ | 
| 696 | { | 
| 697 | return( next_file_slot==0 ? NIL(char)((char*)((void*)0)) : ftab[next_file_slot-1].name ); | 
| 698 | } | 
| 699 | |
| 700 | |
| 701 | PUBLIC int | 
| 702 | Nestlevel()/* | 
| 703 | ============= | 
| 704 | Return the file nesting level */ | 
| 705 | { | 
| 706 | return( next_file_slot ); | 
| 707 | } | 
| 708 | |
| 709 | |
| 710 | PUBLIC FILE * | 
| 711 | TryFiles(lp)/* | 
| 712 | ============== | 
| 713 | Try to open a makefile, try to make it if needed and return a | 
| 714 | filepointer to the first successful found or generated file. | 
| 715 | The function returns NIL(FILE) if nothing was found. */ | 
| 716 | LINKPTR lp; | 
| 717 | { | 
| 718 | FILE *mkfil = NIL(FILE)((FILE*)((void*)0)); | 
| 719 | |
| 720 | if( lp != NIL(LINK)((LINK*)((void*)0)) ) { | 
| 721 | int s_n, s_t, s_q; | 
| 722 | |
| 723 | s_n = Trace; | 
| 724 | s_t = Touch; | 
| 725 | s_q = Check; | 
| 726 | |
| 727 | Trace = Touch = Check = FALSE0; | 
| 728 | /* We are making a makefile. Wait for it. */ | 
| 729 | Makemkf = Wait_for_completion = TRUE1; | 
| 730 | mkfil = NIL(FILE)((FILE*)((void*)0)); | 
| 731 | |
| 732 | for(; lp != NIL(LINK)((LINK*)((void*)0)) && mkfil == NIL(FILE)((FILE*)((void*)0)); lp=lp->cl_next) { | 
| 733 | if( lp->cl_prq->ce_attr & A_FRINGE0x100000 ) continue; | 
| 734 | |
| 735 | mkfil = Openfile( lp->cl_prq->CE_NAMEce_name->ht_name, FALSE0, FALSE0 ); | 
| 736 | |
| 737 | /* Note that no error handling for failed Make() calls is possible | 
| 738 | * as expected errors (no rule to make the makefile) or unexpected | 
| 739 | * errors both return -1. */ | 
| 740 | if( mkfil == NIL(FILE)((FILE*)((void*)0)) && Make(lp->cl_prq, NIL(CELL)((CELL*)((void*)0))) != -1 ) { | 
| 741 | mkfil = Openfile( lp->cl_prq->CE_NAMEce_name->ht_name, FALSE0, FALSE0 ); | 
| 742 | /* Remove flags that indicate that the target was already made. | 
| 743 | * This is also needed to avoid conflicts with the circular | 
| 744 | * dependency check in rulparse(), see issues 62118 and 81296 | 
| 745 | * for details. */ | 
| 746 | Unmake(lp->cl_prq); | 
| 747 | } | 
| 748 | } | 
| 749 | |
| 750 | Trace = s_n; | 
| 751 | Touch = s_t; | 
| 752 | Check = s_q; | 
| 753 | Makemkf = Wait_for_completion = FALSE0; | 
| 754 | } | 
| 755 | |
| 756 | return(mkfil); | 
| 757 | } | 
| 758 | |
| 759 | |
| 760 | /* | 
| 761 | ** print error message from variable arg list | 
| 762 | */ | 
| 763 | |
| 764 | static int errflg = TRUE1; | 
| 765 | static int warnflg = FALSE0; | 
| 766 | |
| 767 | static void | 
| 768 | errargs(fmt, args) | 
| 769 | char *fmt; | 
| 770 | va_list args; | 
| 771 | { | 
| 772 | int warn = _warn && warnflg && !(Glob_attr & A_SILENT0x00002); | 
| 773 | |
| 774 | if( errflg || warn ) { | 
| 775 | char *f = Filename(); | 
| 776 | |
| 777 | fprintf( stderrstderr, "%s: ", Pname ); | 
| 778 | if( f != NIL(char)((char*)((void*)0)) ) fprintf(stderrstderr, "%s: line %d: ", f, Line_number); | 
| 779 | |
| 780 | if( errflg ) | 
| 781 | fprintf(stderrstderr, "Error: -- "); | 
| 782 | else if( warn ) | 
| 783 | fprintf(stderrstderr, "Warning: -- "); | 
| 784 | |
| 785 | vfprintf( stderrstderr, fmt, args ); | 
| 786 | putc( '\n', stderr )_IO_putc ('\n', stderr); | 
| 787 | if( errflg && !Continue ) Quit(0); | 
| 788 | } | 
| 789 | } | 
| 790 | |
| 791 | |
| 792 | /* | 
| 793 | ** Print error message and abort | 
| 794 | */ | 
| 795 | PUBLIC void | 
| 796 | Fatal(ARG(char *,fmt)char * fmt, ARG(va_alist_type,va_alist)...) | 
| 797 | DARG(char *,fmt) | 
| 798 | DARG(va_alist_type,va_alist) | 
| 799 | { | 
| 800 | va_list args; | 
| 801 | |
| 802 | va_start(args, fmt)__builtin_va_start(args, fmt); | 
| 803 | Continue = FALSE0; | 
| 804 | errargs(fmt, args); | 
| 805 | va_end(args); | 
| 806 | } | 
| 807 | |
| 808 | /* | 
| 809 | ** error message and exit (unless -k) | 
| 810 | */ | 
| 811 | PUBLIC void | 
| 812 | Error(ARG(char *,fmt)char * fmt, ARG(va_alist_type,va_alist)...) | 
| 813 | DARG(char *,fmt) | 
| 814 | DARG(va_alist_type,va_alist) | 
| 815 | { | 
| 816 | va_list args; | 
| 817 | |
| 818 | va_start(args, fmt)__builtin_va_start(args, fmt); | 
| 819 | errargs(fmt, args); | 
| 820 | va_end(args); | 
| 821 | } | 
| 822 | |
| 823 | |
| 824 | /* | 
| 825 | ** non-fatal message | 
| 826 | */ | 
| 827 | PUBLIC void | 
| 828 | Warning(ARG(char *,fmt)char * fmt, ARG(va_alist_type,va_alist)...) | 
| 829 | DARG(char *,fmt) | 
| 830 | DARG(va_alist_type,va_alist) | 
| 831 | { | 
| 832 | va_list args; | 
| 833 | |
| 834 | va_start(args, fmt)__builtin_va_start(args, fmt); | 
| 835 | warnflg = TRUE1; | 
| 836 | errflg = FALSE0; | 
| 837 | errargs(fmt, args); | 
| 838 | errflg = TRUE1; | 
| 839 | warnflg = FALSE0; | 
| 840 | va_end(args); | 
| 841 | } | 
| 842 | |
| 843 | |
| 844 | PUBLIC void | 
| 845 | No_ram() | 
| 846 | { | 
| 847 | Fatal( "No more memory" ); | 
| 848 | } | 
| 849 | |
| 850 | |
| 851 | PUBLIC void | 
| 852 | Usage( eflag ) | 
| 853 | int eflag; | 
| 854 | { | 
| 855 | register char *p; | 
| 856 | char *fill; | 
| 857 | |
| 858 | fill = DmStrDup(Pname); | 
| 859 | for(p=fill; *p; p++) *p=' '; | 
| 860 | |
| 861 | if( eflag ) { | 
| 862 | fprintf(stderrstderr, USAGE"Usage:\n%s [-P#] [-{f|K} file] [-{w|W} target ...] [macro[!][[*][+][:]]=value ...]\n", Pname); | 
| 863 | fprintf(stderrstderr, USAGE2"%s [-v[cdfimrtw]] [-m[trae]] [-ABcdeEghiknpqrsStTuVxX] [target ...]\n", fill); | 
| 864 | } | 
| 865 | else { | 
| 866 | printf(USAGE"Usage:\n%s [-P#] [-{f|K} file] [-{w|W} target ...] [macro[!][[*][+][:]]=value ...]\n", Pname); | 
| 867 | printf(USAGE2"%s [-v[cdfimrtw]] [-m[trae]] [-ABcdeEghiknpqrsStTuVxX] [target ...]\n", fill); | 
| 868 | puts(" -P# - set max number of child processes for parallel make"); | 
| 869 | puts(" -f file - use file as the makefile"); | 
| 870 | #ifdef MSDOS | 
| 871 | puts(" -C [+]file - duplicate console output to file, ('+' => append)"); | 
| 872 | #endif | 
| 873 | puts(" -K file - use file as the .KEEP_STATE file"); | 
| 874 | puts(" -w target - show what you would do if 'target' were out of date"); | 
| 875 | puts(" -W target - rebuild pretending that 'target' is out of date"); | 
| 876 | puts(" -v[cdfimrtw] - verbose, indicate what we are doing, (-v => -vcdfimrtw)"); | 
| 877 | puts(" c => dump directory cache info only" ); | 
| 878 | puts(" d => dump change of directory info only" ); | 
| 879 | puts(" f => dump file open/close info only" ); | 
| 880 | puts(" i => dump inference information only" ); | 
| 881 | puts(" m => dump make of target information only" ); | 
| 882 | puts(" r => Force output of recipe lines and warnings," ); | 
| 883 | puts(" overrides -s" ); | 
| 884 | puts(" t => keep temporary files when done" ); | 
| 885 | puts(" w => issue non-essential warnings\n" ); | 
| 886 | |
| 887 | puts(" -m[trae] - Measure timing information, (-m => -mt)"); | 
| 888 | puts(" t => display the start and end time of each target" ); | 
| 889 | puts(" r => display the start and end time of each recipe" ); | 
| 890 | puts(" a => display the target as an absolute path" ); | 
| 891 | puts(" e => display the timing of shell escape macros\n" ); | 
| 892 | |
| 893 | puts("Options: (can be catenated, ie -irn == -i -r -n)"); | 
| 894 | puts(" -A - enable AUGMAKE special target mapping"); | 
| 895 | puts(" -B - enable the use of spaces instead of tabs to start recipes"); | 
| 896 | puts(" -c - use non standard comment scanning"); | 
| 897 | puts(" -d - do not use directory cache"); | 
| 898 | puts(" -E - define environment strings as macros"); | 
| 899 | puts(" -e - same as -E but done after parsing makefile"); | 
| 900 | puts(" -g - disable the special meaning of [ ... ] for group recipes"); | 
| 901 | puts(" -h - print out usage info"); | 
| 902 | puts(" -i - ignore errors"); | 
| 903 | puts(" -k - make independent targets, even if errors"); | 
| 904 | puts(" -n - trace and print, do not execute commands"); | 
| 905 | puts(" -p - print out a version of the makefile"); | 
| 906 | puts(" -q - check if target is up to date. Does not do"); | 
| 907 | puts(" anything. Returns 0 if up to date, 1 otherwise"); | 
| 908 | puts(" -r - don't use internal rules"); | 
| 909 | puts(" -s - do your work silently"); | 
| 910 | puts(" -S - disable parallel (force sequential) make, overrides -P"); | 
| 911 | puts(" -t - touch, update time stamps without executing commands"); | 
| 912 | puts(" -T - do not apply transitive closure on inference rules"); | 
| 913 | puts(" -u - force unconditional update of target"); | 
| 914 | puts(" -V - print out version number"); | 
| 915 | puts(" -x - export macro values to environment"); | 
| 916 | puts(" -X - ignore #! lines at start of makefile"); | 
| 917 | } | 
| 918 | FREE(fill)free((char*)(fill)); | 
| 919 | |
| 920 | Quit(0); | 
| 921 | } | 
| 922 | |
| 923 | |
| 924 | PUBLIC void | 
| 925 | Version() | 
| 926 | { | 
| 927 | extern char **Rule_tab; | 
| 928 | char **p; | 
| 929 | |
| 930 | printf("%s - Version %s (%s)\n", Pname, VERSION"4.12", BUILDINFO"i686-pc-linux-gnu"); | 
| 931 | printf("%s\n\n", sccid); | 
| 932 | |
| 933 | puts("Default Configuration:"); | 
| 934 | for (p=Rule_tab; *p != NIL(char)((char*)((void*)0)); p++) | 
| 935 | printf("\t%s\n", *p); | 
| 936 | |
| 937 | printf("\n"); | 
| 938 | |
| 939 | #if defined(HAVE_SPAWN_H1) || defined(__CYGWIN__) | 
| 940 | /* Only systems that have spawn ar concerned whether spawn or fork/exec | 
| 941 | * are used. */ | 
| 942 | #if ENABLE_SPAWN | 
| 943 | printf("Subprocesses are executed using: spawn.\n\n"); | 
| 944 | #else | 
| 945 | printf("Subprocesses are executed using: fork/exec.\n\n"); | 
| 946 | #endif | 
| 947 | #endif | 
| 948 | |
| 949 | printf("Please read the NEWS file for the latest release notes.\n"); | 
| 950 | } |