1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1984-2012 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * Glenn Fowler
23 * AT&T Research
24 *
25 * make options support
26 *
27 * option name mappings are here
28 * option flag mappings are in options.h
29 */
30
31 #include "make.h"
32 #include "options.h"
33
34 #define OPT_OFFSET 10
35 #define OPT_NON '-'
36 #define OPT_SEP ';'
37
38 static const char usage1[] =
39 "+"
40 "[-?%s\n]"
41 USAGE_LICENSE
42 "[+NAME?nmake - configure, manage and update file hierarchies]"
43 "[+DESCRIPTION?\bnmake\b reads input \amakefiles\a and triggers shell"
44 " actions to build target files that are out of date with prerequisite"
45 " files. Most information used to build targets is contained in"
46 " the global \abase rules\a that are augmented by user \amakefiles\a."
47 " Each operand may be an option, script, or target. An option operand"
48 " is preceded by \b-\b or \b+\b. A script operand contains at least"
49 " one of \bspace\b, \btab\b, \bnewline\b, \b:\b, \b=\b, \b\"\b, or"
50 " \b\\\b and is parsed as a separate, complete makefile. Otherwise"
51 " the operand is a \atarget\a that is generated according to the"
52 " \amakefile\a and \aglobal\a rules. \atarget\a operands are made"
53 " in order from left to right and override the default targets.]"
54 "[+?Command line options, scripts and targets may appear in any order,"
55 " with the exception that no option operand may appear after a"
56 " \b--\b operand.]"
57 "[+?Options are qualified by the base name of the makefile that defined"
58 " them. Unqualified options are defined by \bnmake\b itself.]"
59 ;
60
61 static Option_t options[] = /* option table */
62 {
63
64 { "accept", OPT_accept, (char*)&state.accept, 0,
65 "Accept filesystem timestamps of existing targets." },
66 { "alias", OPT_alias, (char*)&state.alias, 0,
67 "Enable directory aliasing." },
68 { "base", OPT_base, (char*)&state.base, 0,
69 "Compile base or global rules." },
70 { "believe", OPT_believe, (char*)&state.believe, 0,
71 "Believe the state file time of files lower than view level"
72 " \alevel-1\a. The file system time will be checked for files with"
73 " no state or files in views equal to or higher than \alevel\a."
74 " \alevel=0\a causes the file system time to be checked for"
75 " files on all view levels. The top view is level 0.", "level:=0" },
76 { "compatibility",OPT_compatibility,(char*)&state.compatibility,0,
77 "Disable compatibility messages." },
78 { "compile", OPT_compile, (char*)&state.compileonly, 0,
79 "Compile the input makefile and exit." },
80 { "corrupt",OPT_corrupt, (char*)&state.corrupt, 0,
81 "\aaction\a determines the action to take for corrupt or invalid"
82 " top view state files. The top view default is \berror\b and the"
83 " lower view default is \baccept\b. \aaction\a may be one of:",
84 "[action:!accept]"
85 "{"
86 " [+accept?print a warning and set \b--accept\b]"
87 " [+error?print a diagnostic and exit]"
88 " [+ignore?print a warning and set \b--noreadstate\b]"
89 "}" },
90 { "cross", OPT_cross, (char*)&state.cross, 0,
91 "Don't run generated executables." },
92 { "debug", OPT_debug, 0, 0,
93 "Set the debug trace level to \alevel\a. Higher levels produce"
94 " more output.", "level" },
95 { "errorid", OPT_errorid, (char*)&state.errorid, 0,
96 "Add \aid\a to the error message command identifier.", "id:=make" },
97 { "exec", OPT_exec, (char*)&state.exec, 0,
98 "Enable shell action execution. \b--noexec\b"
99 " disables all but \b.ALWAYS\b shell actions and also disables"
100 " make object and state file generation/updates." },
101 { "expandview", OPT_expandview, (char*)&state.expandview, 0,
102 "Expand \a3d\a filesystem paths." },
103 { "explain", OPT_explain, (char*)&state.explain, 0,
104 "Explain each action." },
105 { "file", OPT_file, (char*)&internal.makefiles, 0,
106 "Read the makefile \afile\a. If \b--file\b is not specified then"
107 " the makefile names specified by \b$(MAKEFILES)\b are attempted in"
108 " order from left to right. The file \b-\b is equivalent"
109 " to \b/dev/null\b.", "file" },
110 { "force", OPT_force, (char*)&state.force, 0,
111 "Force all targets to be out of date." },
112 { "global", OPT_global, (char*)&internal.globalfiles, 0,
113 "Read the global makefile \afile\a. The \b--file\b search is not"
114 " affected.", "file" },
115 { "ignore", OPT_ignore, (char*)&state.ignore, 0,
116 "Ignore shell action errors." },
117 { "ignorelock", OPT_ignorelock, (char*)&state.ignorelock, 0,
118 "Ignore state file locks." },
119 { "include", OPT_include, 0, 0,
120 "Add \adirectory\a to the makefile search list.", "directory" },
121 { "intermediate", OPT_intermediate,(char*)&state.intermediate, 0,
122 "Force intermediate target generation." },
123 { "jobs", OPT_jobs, (char*)&state.jobs, 0,
124 "Set the shell action concurrency level to \alevel\a."
125 " Level \b1\b allows dependency checking while an action is"
126 " executing; level \b0\b stops all activity while an action"
127 " is executing.", "level:=${" CO_ENV_PROC "::-1}" },
128 { "keepgoing", OPT_keepgoing, (char*)&state.keepgoing, 0,
129 "Continue after error with sibling prerequisites." },
130 { "list", OPT_list, (char*)&state.list, 0,
131 "List the current rules and variables on the standard output in"
132 " makefile form." },
133 { "mam", OPT_mam, (char*)&state.mam.options, 0,
134 "Write \amake abstract machine\a output to \afile\a if specified or"
135 " to the standard output otherwise. See \bmam\b(5) for details on"
136 " the \amake abstract machine\a language. If \aparent\a !=0 then it is"
137 " the process id of a parent \amam\a process. \adirectory\a is"
138 " the working directory of the current \amam\a process relative"
139 " to the root \amam\a process, \b.\b if not specified. \atype\a"
140 " must be one of:",
141 "[type[,subtype]][::file[::parent[::directory]]]]]]]"
142 "{"
143 "[+dynamic?\amam\a trace of an actual build]"
144 "[+regress?\amam\a for regression testing; labels, path"
145 " names and time stamps are canonicalized for easy comparison]"
146 "[+static?\amam\a representation of the makefile assertions;"
147 " used for makefile conversion]"
148 "[+----?0 or more comma separated subtypes ----]"
149 "[+port?used by the base rules to generate portable"
150 " makefiles; some paths are parameterized; on by default]"
151 "[+----?mam options ----]"
152 "[+[no]]hold?\bhold\b \amam\a output until nested \bnohold\b]"
153 "}" },
154 { "never", OPT_never, (char*)&state.never, 0,
155 "Don't execute any shell actions. \b--noexec\b executes \b.ALWAYS\b"
156 " shell actions." },
157 { "option", OPT_option, 0, 0,
158 "Define a new option. The definition is a delimiter separated field"
159 " list. Any non-alpha-numeric delimiter other than \b-\b may be used."
160 " \b;\b is used in this description. Makefile \bset option\b"
161 " definitions must be '...' quoted. Two adjacent delimiters specifies"
162 " the literal delimiter character and a \b-\b field value specifies an"
163 " empty field. \achar\a is the single character option name, \aname\a"
164 " is the long option name, \aset\a is an optional \b.FUNCTION\b that"
165 " is called when the option value is changed by \bset\b, \avalues\a is"
166 " an \boptget\b(3) value list, and \aflags\a are a combination of:",
167 "['char;name;flags;set;description;values']"
168 "{"
169 " [+a?multiple values appended]"
170 " [+b?boolean value]"
171 " [+i?internal value inverted]"
172 " [+n?numeric value]"
173 " [+o?\a-char\a means \b--no\b\aname\a]"
174 " [+p?.mo probe prerequisite]"
175 " [+s?string value]"
176 " [+v?optional option argument]"
177 " [+x?not expanded in \b$(-)\b]"
178 "}" },
179 { "override", OPT_override, (char*)&state.override, 0,
180 "Implicit rules or metarules override explicit rules." },
181 { "questionable", OPT_questionable,(char*)&state.questionable, 0,
182 "Enable questionable features defined by \amask\a. Questionable"
183 " features are artifacts of previous implementations (\bnmake\b has"
184 " been around since 1984-11-01) that will eventually be dropped."
185 " The questionable \amask\a registry is in the \bmain.c\b \bnmake\b"
186 " source file.", "mask" },
187 { "readonly", OPT_readonly, (char*)&state.readonly, 0,
188 "Current assignments and assertions will be marked \breadonly\b." },
189 { "readstate", OPT_readstate, (char*)&state.readstate, 0,
190 "Ignore state files lower than view level \alevel\a. \alevel=0\a"
191 " ignores state files on all view levels. The top view is level 0.",
192 "level:=0" },
193 { "regress", OPT_regress, (char*)&state.regress, 0,
194 "Massage output for regression testing. \aaction\a may be one of:",
195 "[action:!message]"
196 "{"
197 " [+message?alter messages only]"
198 " [+sync?sync 1-second clocks if necessary and alter messages]"
199 "}" },
200 { "reread", OPT_reread, (char*)&state.reread, 0,
201 "Ignore any previously generated \b.mo\b files and re-read all"
202 " input makefiles." },
203 { "ruledump", OPT_ruledump, (char*)&state.ruledump, 0,
204 "Dump rule information in tabular form on the standard"
205 " error when \bnmake\b exits." },
206 { "scan", OPT_scan, (char*)&state.scan, 0,
207 "Scan for and/or check implicit file prerequisites. On by default." },
208 { "serialize", OPT_serialize, (char*)&state.serialize, 0,
209 "Serialize concurrent output by caching job stdout and stderr"
210 " output until job completion." },
211 { "silent", OPT_silent, (char*)&state.silent, 0,
212 "Do not trace shell actions as they are executed." },
213 { "strictview", OPT_strictview, (char*)&state.strictview, 0,
214 "Set \bVPATH\b \b.SOURCE\b rule interpretation to follow strict"
215 " \a3d\a filesystem semantics, where directories in the top views"
216 " take precedence. On by default when running in \a2d\a with"
217 " \bVPATH\b defined, off by default otherwise." },
218 { "target-context",OPT_targetcontext,(char*)&state.targetcontext, 0,
219 "Expand and execute shell actions in the target directory context."
220 " This allows a single makefile to control a directory tree while"
221 " generating target files at the source file directory level. By"
222 " default target files are generated in the current directory." },
223 { "target-prefix", OPT_targetprefix,(char*)&state.targetprefix, 0,
224 "Allow metarules to match \aseparator\a in the target to \b/\b"
225 " in the source. Used to disambiguate source file base name clashes"
226 " when target files are generated in the current directory."
227 " \aseparator\a must not contain metarule or shell pattern characters.",
228 "separator" },
229 { "test", OPT_test, (char*)&state.test, 0,
230 "Enable test code defined by \amask\a. Test code is implementation"
231 " specific. The test \amask\a registry is in the \bmain.c\b \bnmake\b"
232 " source file.", "mask" },
233 { "tolerance", OPT_tolerance, (char*)&state.tolerance, 0,
234 "Set the time comparison tolerance to \aseconds\a. Times within"
235 " the tolerance range compare equal. Useful on systems that can't"
236 " quite get the file system and local clocks in sync. A tolerance"
237 " of more that 5 seconds soon becomes intolerable.", "seconds" },
238 { "touch", OPT_touch, (char*)&state.touch, 0,
239 "Touch the time stamps of out of date targets rather than execute"
240 " the shell action." },
241 { "vardump", OPT_vardump, (char*)&state.vardump, 0,
242 "Dump variable information in tabular form on the standard"
243 " error when \bnmake\b exits." },
244 { "warn", OPT_warn, (char*)&state.warn, 0,
245 "Enable verbose warning messages." },
246 { "writeobject", OPT_writeobject,(char*)&state.writeobject, 0,
247 "Generate a \b.mo\b make object file in \afile\a that can be read"
248 " instead of the input makefiles on the next \bnmake\b invocation."
249 " On by default. \b--nowriteobject\b prevents the generation."
250 " The default name is used if \afile\a is omitted or \b-\b."
251 " If \afile\a is a directory then the default is placed in that"
252 " directory.",
253 "file:=$(MAKEFILE::B::S=.mo)" },
254 { "writestate", OPT_writestate, (char*)&state.writestate, 0,
255 "Generate a \b.ms\b make state file in \afile\a when \bnmake\b exits."
256 "The state contains the time stamps of all prerequisites and targets"
257 " that have been accessed since the state file was first generated."
258 " On by default. \b--nowritestate\b prevents the generation."
259 " The default name is used if \afile\a is omitted or \b-\b."
260 " If \afile\a is a directory then the default is placed in that"
261 " directory.",
262 "file:=$(MAKEFILE::B::S=.ms)" },
263
264 { "byname", OPT_byname, 0, 0,
265 "(obsolete) Set options by name.", "name[=value]]" },
266 { "define", OPT_define, 0, 0,
267 "(obsolete) Pass macro definition to the makefile preprocessor.",
268 "name[=value]]" },
269 { "preprocess", OPT_preprocess, 0, 0,
270 "(obsolete) Preprocess all makefiles." },
271 { "undef", OPT_undef, 0, 0,
272 "(obsolete) Pass macro deletion to the makefile preprocessor.",
273 "name" },
274
275 };
276
277 static const char usage2[] =
278 "\n"
279 "[ script ... ] [ target ... ]\n"
280 "\n"
281 "[+DIAGNOSTICS?Diagnostic messages are printed on the standard error and are"
282 " classified by levels. The level determines if the diagnostic"
283 " is printed, if it causes \bnmake\b to exit, and if it affects"
284 " the \bnmake\b exit status. The levels are:]{"
285 " [+<0?Debug message, enabled when the absolute value of"
286 " \alevel\a is greater than or equal to the"
287 " \b--debug\b level. Debug diagnostics are prefixed"
288 " by \bdebug\b-\alevel\a\b:\b.]"
289 " [+1?Warning message, disabled by \b--silent\b. Warning"
290 " diagnostics are prefixed by \bwarning\a\b:\b]"
291 " [+2?Non-fatal error message. Processing continues after"
292 " the diagnostic, but the eventual \bnmake\b exit"
293 " status will be non-zero.]"
294 " [+>2?Fatal error message. \bnmake\b exits after the"
295 " diagnostic (and internal cleanup) with exit"
296 " status \alevel\a-2.]"
297 "}"
298 "[+SEE ALSO?\b3d\b(1), \bar\b(1), \bcc\b(1), \bcoshell\b(1), \bcpp\b(1),"
299 " \bprobe\b(1), \bsh\b(1)]"
300 ;
301
302 struct Oplist_s; typedef struct Oplist_s Oplist_t;
303
304 struct Oplist_s /* linked option list */
305 {
306 char* option; /* option value for set() */
307 Oplist_t* next; /* next in list */
308 };
309
310 typedef struct Optstate_s /* option state */
311 {
312 Oplist_t* hidden; /* options hidden by cmd line */
313 Oplist_t* lasthidden; /* tail of hidden */
314 Oplist_t* delayed; /* delayed unknown options */
315 Oplist_t* lastdelayed; /* tail of delayed */
316
317 Option_t* head; /* head of external option list */
318 Option_t* tail; /* tail of external option list */
319
320 Hash_table_t* table;
321
322 Sfio_t* usage; /* generated optget() usage */
323 int usageindex; /* next user index */
324 } Optstate_t;
325
326 static Optstate_t opt;
327
328 static Option_t*
getoption(const char * name)329 getoption(const char* name)
330 {
331 register Option_t* op;
332 register int c;
333
334 if (!(op = (Option_t*)hashget(opt.table, name)) && (strchr(name, '-') || strchr(name, '_')))
335 {
336 while (c = *name++)
337 if (c != '-' && c != '_')
338 sfputc(internal.tmp, c);
339 op = (Option_t*)hashget(opt.table, sfstruse(internal.tmp));
340 }
341 return op;
342 }
343
344 static void
putoption(register Option_t * op,int index)345 putoption(register Option_t* op, int index)
346 {
347 register char* s;
348 register int c;
349 char buf[16];
350
351 hashput(opt.table, op->name, (char*)op);
352 if (strchr(op->name, '-') || strchr(op->name, '_'))
353 {
354 s = op->name;
355 while (c = *s++)
356 if (c != '-' && c != '_')
357 sfputc(internal.tmp, c);
358 hashput(opt.table, strdup(sfstruse(internal.tmp)), (char*)op);
359 }
360 if (op->flags & Of)
361 sfsprintf(buf, sizeof(buf), "+%d", OPT(op->flags));
362 else
363 {
364 buf[0] = '-';
365 buf[1] = OPT(op->flags);
366 buf[2] = 0;
367 }
368 hashput(opt.table, strdup(buf), (char*)op);
369 if (index >= elementsof(options))
370 {
371 sfsprintf(buf, sizeof(buf), "-%d", index);
372 hashput(opt.table, strdup(buf), (char*)op);
373 }
374 }
375
376 /*
377 * initialize the option hash table
378 */
379
380 void
optinit(void)381 optinit(void)
382 {
383 register int i;
384
385 opt.table = hashalloc(NiL, HASH_name, "options", 0);
386 for (i = 0; i < elementsof(options); i++)
387 {
388 options[i].flags |= Om;
389 switch (OPT(options[i].flags))
390 {
391 case OPT(OPT_debug):
392 options[i].value = (char*)&error_info.trace;
393 break;
394 }
395 putoption(&options[i], i);
396 if (opt.tail)
397 opt.tail->next = &options[i];
398 else
399 opt.head = &options[i];
400 opt.tail = &options[i];
401 }
402 }
403
404 /*
405 * return option table entry given OPT_[a-z]+ flag
406 * type==0 panics if not in table
407 */
408
409 Option_t*
optflag(register int flag)410 optflag(register int flag)
411 {
412 register Option_t* op;
413 char buf[8];
414
415 if (flag & Of)
416 sfsprintf(buf, sizeof(buf), "+%d", OPT(flag));
417 else
418 {
419 buf[0] = '-';
420 buf[1] = OPT(flag);
421 buf[2] = 0;
422 }
423 if (!(op = getoption(buf)) && (flag & ~((1<<8)-1)))
424 error(ERROR_PANIC, "%s: unknown option flag", buf);
425 return op;
426 }
427
428 /*
429 * call op->set with new value
430 */
431
432 static void
setcall(register Option_t * op,int readonly)433 setcall(register Option_t* op, int readonly)
434 {
435 Rule_t* r;
436 char* oset;
437 int oreadonly;
438 char buf[16];
439
440 if (op->set && (r = getrule(op->set)))
441 {
442 oset = op->set;
443 op->set = 0;
444 oreadonly = state.readonly;
445 state.readonly = readonly;
446 switch (op->flags & (Ob|On|Os))
447 {
448 case Ob:
449 call(r, *((unsigned char*)op->value) ? "1" : null);
450 break;
451 case On:
452 sfsprintf(buf, sizeof(buf), "%d", *((int*)op->value));
453 call(r, buf);
454 break;
455 case Os:
456 call(r, *((char**)op->value));
457 break;
458 }
459 state.readonly = oreadonly;
460 op->set = oset;
461 }
462 }
463
464 /*
465 * copy a declare() string entry s to sp
466 */
467
468 static void
declarestr(register Sfio_t * sp,register const char * s)469 declarestr(register Sfio_t* sp, register const char* s)
470 {
471 register int c;
472
473 if (s && *s)
474 while (c = *s++)
475 {
476 if (c == OPT_SEP)
477 sfputc(sp, c);
478 sfputc(sp, c);
479 }
480 else
481 sfputc(sp, OPT_NON);
482 sfputc(sp, OPT_SEP);
483 }
484
485 /*
486 * generate external option declaration
487 */
488
489 static void
declare(Sfio_t * sp,register Option_t * op)490 declare(Sfio_t* sp, register Option_t* op)
491 {
492 if (!(op->flags & Of))
493 sfputc(internal.tmp, OPT(op->flags));
494 sfputc(internal.tmp, OPT_SEP);
495 declarestr(internal.tmp, op->name);
496 if (op->flags & Oa)
497 sfputc(internal.tmp, 'a');
498 if (op->flags & Ob)
499 sfputc(internal.tmp, 'b');
500 if (op->flags & On)
501 sfputc(internal.tmp, 'n');
502 if (op->flags & Oo)
503 sfputc(internal.tmp, 'o');
504 if (op->flags & Op)
505 sfputc(internal.tmp, 'p');
506 if (op->flags & Os)
507 sfputc(internal.tmp, 's');
508 if (op->flags & Ov)
509 sfputc(internal.tmp, 'v');
510 if (op->flags & Ox)
511 sfputc(internal.tmp, 'x');
512 sfputc(internal.tmp, OPT_SEP);
513 declarestr(internal.tmp, op->set);
514 declarestr(internal.tmp, op->description);
515 declarestr(internal.tmp, op->arg);
516 shquote(sp, fmtesc(sfstruse(internal.tmp)));
517 }
518
519 /*
520 * generate optget() usage for op
521 */
522
523 static void
genusage(register Option_t * op,int index,int last)524 genusage(register Option_t* op, int index, int last)
525 {
526 long pos;
527
528 if (op)
529 {
530 sfputc(opt.usage, '[');
531 if (!(op->flags & Of))
532 {
533 sfputc(opt.usage, OPT(op->flags));
534 if (op->flags & Oo)
535 sfputc(opt.usage, '!');
536 sfputc(opt.usage, '=');
537 }
538 sfprintf(opt.usage, "%d:%s?%s]", index + OPT_OFFSET, op->name, op->description);
539 if (op->arg)
540 {
541 if (op->flags & On)
542 sfputc(opt.usage, '#');
543 else
544 sfputc(opt.usage, ':');
545 if (op->flags & Ov)
546 sfputc(opt.usage, '?');
547 if (*op->arg != '[')
548 sfputc(opt.usage, '[');
549 sfputr(opt.usage, op->arg, -1);
550 if (*op->arg != '[')
551 sfputc(opt.usage, ']');
552 }
553 sfputc(opt.usage, '\n');
554 }
555 pos = sfstrtell(opt.usage);
556 if (last)
557 sfprintf(opt.usage, usage2, version);
558 else
559 sfputc(opt.usage, 0);
560 sfstrseek(opt.usage, pos, SEEK_SET);
561 }
562
563 /*
564 * mam output discipline to parameterize local paths
565 */
566
567 static ssize_t
mamwrite(Sfio_t * fp,const void * buf,size_t n,Sfdisc_t * dp)568 mamwrite(Sfio_t* fp, const void* buf, size_t n, Sfdisc_t* dp)
569 {
570 char* s;
571 size_t z;
572
573 static char* tmp;
574 static int siz;
575
576 z = n;
577 if (n > 1 && ((char*)buf)[n-1] == '\n')
578 {
579 if (n >= siz)
580 {
581 siz = roundof(n + 1, 1024);
582 tmp = newof(tmp, char, siz, 0);
583 }
584 memcpy(tmp, buf, n);
585 tmp[n-1] = 0;
586 if (s = call(makerule(external.mamaction), tmp))
587 {
588 z = strlen(s);
589 if (z >= siz)
590 {
591 siz = roundof(z + 1, 1024);
592 tmp = newof(tmp, char, siz, 0);
593 }
594 memcpy(tmp, s, z);
595 tmp[z++] = '\n';
596 buf = (const char*)tmp;
597 }
598 }
599 return sfwr(fp, buf, z, dp) == z ? n : -1;
600 }
601
602 /*
603 * make sure the current time is > the start time
604 * to differentiate strtime() "recent" vs. "current"
605 * if the clock or filesystem doesn't support subsecond
606 * granularity then we sleep until the next integral
607 * second ticks off
608 *
609 * the filesystem checks assume that file time stamp
610 * subseconds==0 rarely happens by chance -- the penalty
611 * for a wrong guess is slightly slower but still correct
612 * regression tests
613 *
614 * this also assumes that regression tests run faster than
615 * the disk inode flush frequency on systems where the
616 * cache time resolution is higher than the disk
617 */
618
619 static void
regressinit(const char * type)620 regressinit(const char* type)
621 {
622 Stat_t st;
623 Time_t t;
624 int i;
625
626 error_info.version = 0;
627 if (*type == 's')
628 {
629 t = CURTIME;
630 if (i = tmxnsec(t) && !stat(".", &st) && !tmxnsec(tmxgetatime(&st)) && !tmxnsec(tmxgetmtime(&st)))
631 state.start = tmxsns(tmxsec(t)+1, 0);
632 while (state.start >= (t = CURTIME))
633 tmsleep(0L, 100000000L);
634 if (!i)
635 state.start = t;
636 }
637 }
638
639 /*
640 * return option name and details
641 */
642
643 static char*
showop(register Option_t * op)644 showop(register Option_t* op)
645 {
646 sfprintf(internal.tmp, "%s,", op->name);
647 sfprintf(internal.tmp, (op->flags & Of) ? "%03o," : "'%c',", op->flags & ((1<<8) - 1));
648 if (op->flags & Oa)
649 sfprintf(internal.tmp, "|a");
650 if (op->flags & Ob)
651 sfprintf(internal.tmp, "|b");
652 if (op->flags & Of)
653 sfprintf(internal.tmp, "|f");
654 if (op->flags & Oi)
655 sfprintf(internal.tmp, "|i");
656 if (op->flags & On)
657 sfprintf(internal.tmp, "|n");
658 if (op->flags & Oo)
659 sfprintf(internal.tmp, "|o");
660 if (op->flags & Op)
661 sfprintf(internal.tmp, "|p");
662 if (op->flags & Os)
663 sfprintf(internal.tmp, "|s");
664 if (op->flags & Ov)
665 sfprintf(internal.tmp, "|v");
666 if (op->flags & Ox)
667 sfprintf(internal.tmp, "|x");
668 if (op->flags & OPT_COMPILE)
669 sfprintf(internal.tmp, "|COMPILE");
670 if (op->flags & OPT_DECLARE)
671 sfprintf(internal.tmp, "|DECLARE");
672 if (op->flags & OPT_DEFAULT)
673 sfprintf(internal.tmp, "|DEFAULT");
674 if (op->flags & OPT_EXTERNAL)
675 sfprintf(internal.tmp, "|EXTERNAL");
676 if (op->flags & OPT_READONLY)
677 sfprintf(internal.tmp, "|READONLY");
678 if (op->flags & OPT_SET)
679 sfprintf(internal.tmp, "|SET");
680 sfputc(internal.tmp, '|');
681 return sfstruse(internal.tmp);
682 }
683
684 /*
685 * return next option definition field
686 */
687
688 static char*
field(char ** p,int sep,int app,int lenient)689 field(char** p, int sep, int app, int lenient)
690 {
691 register char* s;
692 register char* t;
693 char* v;
694 register int c;
695
696 if (!(c = *(s = *p)) || c == app)
697 return 0;
698 if (c == sep)
699 {
700 *p = s + 1;
701 return 0;
702 }
703 v = t = s;
704 for (;;)
705 {
706 if (!(c = *t++ = *s++))
707 {
708 s--;
709 t--;
710 break;
711 }
712 else if (c == sep)
713 {
714 if (lenient || !*s)
715 {
716 t--;
717 s--;
718 break;
719 }
720 if (*s == OPT_NON && (!*(s + 1) || *(s + 1) == sep))
721 {
722 t--;
723 s++;
724 break;
725 }
726 if (*s != c)
727 {
728 t--;
729 s--;
730 break;
731 }
732 s++;
733 }
734 }
735 if (*s)
736 s++;
737 *p = s;
738 if (*t)
739 *t = 0;
740 if (!*v || lenient && *v == OPT_NON && !*(v + 1))
741 return 0;
742 return v;
743 }
744
745 /*
746 * set an option given its pointer
747 */
748
749 static void
setop(register Option_t * op,register int n,char * s,int type)750 setop(register Option_t* op, register int n, char* s, int type)
751 {
752 char* t;
753 Rule_t* r;
754 int readonly;
755
756 readonly = state.readonly;
757 if (OPT(op->flags) != OPT(OPT_option))
758 {
759 if (type == ':' && (op->flags & OPT_SET))
760 return;
761 if (readonly)
762 op->flags |= OPT_READONLY;
763 else if (!state.user && (op->flags & OPT_READONLY))
764 {
765 Oplist_t* x;
766
767 /*
768 * save for listops(*,'@')
769 */
770
771 sfprintf(internal.tmp, "%s", op->name);
772 if (type == ':' && !(op->flags & OPT_SET))
773 sfputc(internal.tmp, ':');
774 if (!s)
775 sfprintf(internal.tmp, "=%d", n);
776 else if (!strchr(s, ' ') && !strchr(s, '\t'))
777 sfprintf(internal.tmp, "=%s", s);
778 else if (!strchr(s, '\''))
779 sfprintf(internal.tmp, "='%s'", s);
780 else
781 sfprintf(internal.tmp, "=\"%s\"", s);
782 n = sfstrtell(internal.tmp) + 1;
783 s = sfstruse(internal.tmp);
784 x = newof(0, Oplist_t, 1, n);
785 x->option = strcpy((char*)(x + 1), s);
786 if (opt.lasthidden)
787 opt.lasthidden = opt.lasthidden->next = x;
788 else
789 opt.hidden = opt.lasthidden = x;
790 return;
791 }
792 if (error_info.trace <= -3)
793 error(-3, "option(%s,%d,\"%s\")", showop(op), n, s ? s : null);
794 }
795 if (type != ':')
796 {
797 op->flags |= OPT_SET;
798 op->flags &= ~OPT_DEFAULT;
799 }
800 else if (!(op->flags & OPT_SET))
801 {
802 op->flags |= OPT_DEFAULT;
803 if (!state.loading)
804 op->flags |= OPT_COMPILE;
805 }
806 if (state.reading)
807 op->flags |= OPT_COMPILE;
808 if (op->flags & Oi)
809 {
810 if (op->flags & On)
811 n = -n;
812 else
813 n = !n;
814 }
815 else if (op->flags & Ob)
816 n = n != 0;
817 else if ((op->flags & (Os|Ov)) == Os && n && !s)
818 error(3, "-%c: option argument expected", OPT(op->flags));
819 if (!n)
820 s = 0;
821 switch (OPT(op->flags))
822 {
823 case OPT(OPT_believe):
824 if (state.compile < COMPILED)
825 state.believe = n;
826 else
827 error(2, "%s: option must be set before %s", op->name, external.makeinit);
828 return;
829 case OPT(OPT_byname):
830 if (s)
831 set(s, 1, NiL);
832 return;
833 case OPT(OPT_corrupt):
834 if (!s)
835 s = "-";
836 if ((*s == *(state.corrupt = "accept") || *s == '-') && (!*(s + 1) || !strcmp(s, state.corrupt)))
837 ;
838 else if (*s == *(state.corrupt = "error") && (!*(s + 1) || !strcmp(s, state.corrupt)))
839 state.corrupt = 0;
840 else if (*s == *(state.corrupt = "ignore") && (!*(s + 1) || !strcmp(s, state.corrupt)))
841 ;
842 else
843 {
844 state.corrupt = 0;
845 error(2, "%s: invalid corrupt action", s);
846 }
847 return;
848 case OPT(OPT_global):
849 if (!s)
850 {
851 if (n)
852 {
853 state.pushed = 1;
854 state.push_global = state.global;
855 state.global = 1;
856 state.push_user = state.user;
857 state.user = 0;
858 }
859 else if (state.pushed)
860 {
861 state.pushed = 0;
862 state.global = state.push_global;
863 state.user = state.push_user;
864 }
865 return;
866 }
867 break;
868 case OPT(OPT_include):
869 addprereq(catrule(internal.source->name, external.source, NiL, 1), makerule(s), PREREQ_APPEND);
870 if (!(op->flags & OPT_READONLY))
871 break;
872 /*FALLTHROUGH*/
873 case OPT(OPT_define):
874 case OPT(OPT_undef):
875 if (s)
876 {
877 sfprintf(internal.tmp, "-%c%s", OPT(op->flags), s);
878 if (!(r = getrule(sfstruse(internal.tmp))))
879 r = makerule(NiL);
880 addprereq(internal.preprocess, r, PREREQ_APPEND);
881 }
882 return;
883 case OPT(OPT_preprocess):
884 if (!state.preprocess)
885 {
886 state.preprocess = 1;
887 if (!state.compatibility)
888 error(1, "makefile preprocessing is obsolete -- use make statements");
889 }
890 return;
891 case OPT(OPT_errorid):
892 if (s && *s)
893 {
894 if (state.errorid)
895 sfprintf(internal.tmp, "%s/", state.errorid);
896 sfprintf(internal.tmp, "%s", s);
897 state.errorid = strdup(sfstruse(internal.tmp));
898 sfprintf(internal.tmp, "%s [%s]", idname, state.errorid);
899 error_info.id = strdup(sfstruse(internal.tmp));
900 }
901 else
902 {
903 op->flags &= ~OPT_SET;
904 error_info.id = idname;
905 }
906 return;
907 case OPT(OPT_jobs):
908 if (n >= MAXJOBS)
909 n = MAXJOBS - 1;
910 if (n < 1)
911 n = 0;
912 state.jobs = n;
913 return;
914 case OPT(OPT_mam):
915 if (t = s)
916 {
917 if (t[0] == 'n' && t[1] == 'o')
918 t += 2;
919 if (streq(t, "hold"))
920 {
921 if (t == s)
922 state.mam.hold++;
923 else
924 state.mam.hold--;
925 return;
926 }
927 }
928 if (state.mam.label != null)
929 {
930 if (state.mam.label)
931 free(state.mam.label);
932 state.mam.label = null;
933 }
934 if (state.mam.options)
935 {
936 free(state.mam.options);
937 state.mam.options = 0;
938 }
939 if (state.mam.root)
940 {
941 free(state.mam.root);
942 state.mam.root = 0;
943 }
944 if (state.mam.out)
945 {
946 if (state.mam.out != sfstdout && state.mam.out != sfstderr)
947 sfclose(state.mam.out);
948 state.mam.out = 0;
949 }
950 state.mam.dontcare = state.mam.dynamic = state.mam.regress = state.mam.statix = state.mam.parent = 0;
951 state.mam.port = 1;
952 if (s)
953 {
954 char* o;
955 char* u;
956 Sfio_t* tmp;
957
958 tmp = sfstropen();
959 sfputr(tmp, s, 0);
960 s = sfstruse(tmp);
961 if (t = strchr(s, ':'))
962 *t++ = 0;
963 if (o = strchr(s, ','))
964 *o++ = 0;
965 if (*s == *(state.mam.type = "dynamic") && (!*(s + 1) || !strcmp(s, state.mam.type)))
966 state.mam.dynamic = 1;
967 else if (*s == *(state.mam.type = "regress") && (!*(s + 1) || !strcmp(s, state.mam.type)))
968 {
969 state.regress = "sync";
970 state.mam.regress = 1;
971 state.silent = 1;
972 if (!table.regress)
973 table.regress = hashalloc(table.rule, HASH_name, "regress-paths", 0);
974 regressinit(state.regress);
975 }
976 else if (*s == *(state.mam.type = "static") && (!*(s + 1) || !strcmp(s, state.mam.type)))
977 state.mam.statix = 1;
978 else
979 error(3, "%s: invalid mam type: {dynamic,regress,static} expected", s);
980 while (s = o)
981 {
982 if (o = strchr(s, ','))
983 *o++ = 0;
984 if (*s == 'n' && *(s + 1) == 'o')
985 {
986 s += 2;
987 n = 0;
988 }
989 else
990 n = 1;
991 if (*s == *(u = "dontcare") && (!*(s + 1) || !strcmp(s, u)))
992 state.mam.dontcare = n;
993 else if (*s == *(u = "port") && (!*(s + 1) || !strcmp(s, u)))
994 state.mam.port = n;
995 else
996 error(3, "%s: invalid mam option: [no]{dontcare,port} expected", s);
997 }
998 if (t)
999 {
1000 s = t;
1001 if (t = strchr(s, ':'))
1002 {
1003 *t++ = 0;
1004 if (isdigit(*t))
1005 {
1006 while (isdigit(*t))
1007 state.mam.parent = state.mam.parent * 10 + *t++ - '0';
1008 if (!state.mam.regress)
1009 {
1010 sfprintf(internal.tmp, "%05d ", state.pid);
1011 state.mam.label = strdup(sfstruse(internal.tmp));
1012 }
1013 }
1014 if (*t)
1015 {
1016 if (*t != ':')
1017 error(3, "%s: mam label expected", t);
1018 t++;
1019 }
1020 if (!*t)
1021 t = 0;
1022 }
1023 }
1024 else s = null;
1025 if (!*s || streq(s, "-") || streq(s, "/dev/fd/1") || streq(s, "/dev/stdout"))
1026 {
1027 s = "/dev/stdout";
1028 state.mam.out = sfstdout;
1029 }
1030 else if (streq(s, "/dev/fd/2") || streq(s, "/dev/stderr"))
1031 state.mam.out = sfstderr;
1032 else if (!(state.mam.out = sfopen(NiL, s, state.mam.parent ? "ae" : "we")))
1033 error(ERROR_SYSTEM|3, "%s: cannot write mam output file", s);
1034 sfset(state.mam.out, SF_LINE, 1);
1035 state.mam.disc.writef = mamwrite;
1036 if (sfdisc(state.mam.out, &state.mam.disc) != &state.mam.disc)
1037 error(3, "%s: cannot push mam output discipline", s);
1038 sfprintf(internal.tmp, "%s", state.mam.type);
1039 if (state.mam.dontcare)
1040 sfprintf(internal.tmp, ",dontcare");
1041 sfprintf(internal.tmp, ",%sport", state.mam.port ? null : "no");
1042 sfprintf(internal.tmp, ":");
1043 if (*s != '/')
1044 sfprintf(internal.tmp, "%s/", internal.pwd);
1045 sfprintf(internal.tmp, "%s:%d", s, state.pid);
1046 if (t)
1047 {
1048 sfprintf(internal.nam, "$(\"%s\":P=A)", t);
1049 expand(tmp, sfstruse(internal.nam));
1050 state.mam.root = strdup(sfstruse(tmp));
1051 state.mam.rootlen = strlen(state.mam.root);
1052 sfprintf(internal.tmp, ":%s", state.mam.root);
1053 }
1054 state.mam.options = strdup(sfstruse(internal.tmp));
1055 sfstrclose(tmp);
1056 }
1057 return;
1058 case OPT(OPT_option):
1059 s = strdup(s);
1060 while (*s)
1061 {
1062 char* name;
1063 char* func;
1064 char* desc;
1065 char* args;
1066 Option_t* nop;
1067 int app;
1068 int sep;
1069 char buf[16];
1070
1071 sep = *s;
1072 if (isalpha(sep))
1073 {
1074 n = sep|Ob;
1075 sep = *++s;
1076 }
1077 else
1078 {
1079 n = '?'|Of|Ob;
1080 if (sep == OPT_NON)
1081 sep = *++s;
1082 }
1083 app = (sep == ':') ? ';' : ':';
1084 s++;
1085 if (!(name = field(&s, sep, app, 1)))
1086 {
1087 if (n & Of)
1088 error(2, "option flag and name omitted");
1089 else
1090 error(2, "-%c: option name omitted", OPT(n));
1091 break;
1092 }
1093 if (t = field(&s, sep, app, 1))
1094 {
1095 for (;;)
1096 {
1097 switch (*t++)
1098 {
1099 case 0:
1100 break;
1101 case 'a':
1102 n |= Oa;
1103 continue;
1104 case 'b':
1105 n &= ~(On|Os);
1106 n |= Ob;
1107 continue;
1108 case 'n':
1109 n &= ~(Ob|Os);
1110 n |= On;
1111 continue;
1112 case 'o':
1113 n |= Oo;
1114 continue;
1115 case 'p':
1116 n |= Op;
1117 continue;
1118 case 's':
1119 n &= ~(Ob|On);
1120 n |= Os;
1121 continue;
1122 case 'v':
1123 n |= Ov;
1124 continue;
1125 case 'x':
1126 n |= Ox;
1127 continue;
1128 /* no complaints here for future extensions */
1129 }
1130 break;
1131 }
1132 }
1133 func = field(&s, sep, app, 1);
1134 desc = field(&s, sep, app, 0);
1135 args = field(&s, sep, app, 0);
1136 if (!(n & Of))
1137 {
1138 for (nop = &options[0]; nop < &options[elementsof(options)]; nop++)
1139 if (OPT(nop->flags) == OPT(n))
1140 break;
1141 if (nop < &options[elementsof(options)])
1142 {
1143 error(2, "--%s: -%c conflicts with --%s", s, OPT(n), nop->name);
1144 return;
1145 }
1146 buf[0] = '-';
1147 buf[1] = OPT(n);
1148 buf[2] = 0;
1149 nop = getoption(buf);
1150 }
1151 else
1152 nop = 0;
1153 if (nop)
1154 {
1155 if (n & On)
1156 *((int*)nop->value) = *((unsigned char*)nop->value);
1157 else if (n & Os)
1158 *((char**)nop->value) = 0;
1159 nop->flags = n;
1160 set_insert:
1161 nop->name = name;
1162 nop->set = func;
1163 if ((n & (Os|Oa)) == (Os|Oa) && !op->value)
1164 *((Rule_t**)nop->value) = catrule(".OPTION.", nop->name, ".LIST.", 1);
1165 if (!desc)
1166 desc = "option.";
1167 if (*desc != '(')
1168 {
1169 sfputc(internal.tmp, '(');
1170 if ((t = parsefile()) && *t)
1171 edit(internal.tmp, t, DELETE, KEEP, DELETE);
1172 else
1173 sfputr(internal.tmp, state.base ? "rules" : state.global ? "global" : "user", -1);
1174 sfprintf(internal.tmp, ") %s", desc);
1175 desc = strdup(sfstruse(internal.tmp));
1176 }
1177 nop->description = desc;
1178 if (!args && (nop->flags & (On|Os)))
1179 args = (nop->flags & On) ? "number" : "string";
1180 if (args && (nop->flags & Ob))
1181 {
1182 nop->flags &= ~Ob;
1183 nop->flags |= Os;
1184 }
1185 nop->arg = args;
1186 putoption(nop, opt.usageindex);
1187 genusage(nop, opt.usageindex++, 1);
1188 if (nop->set && (nop->flags & OPT_SET))
1189 readonly = 1;
1190 }
1191 else if (!(nop = getoption(name)))
1192 {
1193 nop = newof(0, Option_t, 1, sizeof(char*));
1194 nop->value = (char*)(nop + 1);
1195 nop->flags = n|OPT_EXTERNAL;
1196 if (!state.loading)
1197 nop->flags |= OPT_DECLARE;
1198 if (opt.tail)
1199 opt.tail->next = nop;
1200 else
1201 opt.head = nop;
1202 opt.tail = nop;
1203 goto set_insert;
1204 }
1205 else if (!(nop->flags & OPT_EXTERNAL))
1206 error(1, "--%s is an internal option", nop->name);
1207 else
1208 {
1209 if (nop->flags & Of)
1210 {
1211 if ((n & (Ob|On|Os)) != (nop->flags & (Ob|On|Os)))
1212 {
1213 if (n & Os)
1214 *((char**)nop->value) = 0;
1215 else if ((n & On) && (nop->flags & Ob))
1216 *((int*)nop->value) = *((unsigned char*)nop->value);
1217 else if ((n & Ob) && (nop->flags & On))
1218 *((unsigned char*)nop->value) = *((int*)nop->value) != 0;
1219 }
1220 nop->flags = n;
1221 }
1222 else if (OPT(nop->flags) != OPT(n))
1223 error(1, "--%s: option flag -%c conflicts with -%c", nop->name, OPT(n), OPT(nop->flags));
1224 if ((nop->flags & (Ob|On|Os)) != (n & (Ob|On|Os)))
1225 error(1, "--%s: option is %s", nop->name, (nop->flags & Ob) ? "boolean" : (nop->flags & On) ? "numeric" : "string valued");
1226 }
1227
1228 /*
1229 * skip the remaining fields for future extensions
1230 * and consume the append char if specified
1231 */
1232
1233 while (field(&s, sep, app, 0));
1234 if (*s == app)
1235 s++;
1236 }
1237 optcheck(0);
1238 return;
1239 case OPT(OPT_never):
1240 if (state.never = n)
1241 state.exec = 0;
1242 return;
1243 case OPT(OPT_regress):
1244 if (n)
1245 {
1246 if (!s)
1247 s = "-";
1248 if ((*s == *(state.regress = "message") || *s == '-') && (!*(s + 1) || !strcmp(s, state.regress)))
1249 ;
1250 else if (*s == *(state.regress = "sync") && (!*(s + 1) || !strcmp(s, state.regress)))
1251 ;
1252 else
1253 {
1254 state.regress = 0;
1255 error(2, "%s: invalid regress action", s);
1256 }
1257 if (state.regress)
1258 regressinit(state.regress);
1259 }
1260 else
1261 state.regress = 0;
1262 return;
1263 case OPT(OPT_reread):
1264 if (state.reread = n)
1265 state.forceread = 1;
1266 return;
1267 case OPT(OPT_silent):
1268 if (state.silent = n)
1269 {
1270 if (!error_info.trace)
1271 error_info.trace = 2;
1272 }
1273 else
1274 if (error_info.trace > 0)
1275 error_info.trace = 0;
1276 return;
1277 case OPT(OPT_targetprefix):
1278 if (!n)
1279 s = 0;
1280 else if (s)
1281 s = strdup(s);
1282 if (state.targetprefix = s)
1283 for (;;)
1284 {
1285 switch (*s++)
1286 {
1287 case 0:
1288 break;
1289 case '%':
1290 case '*':
1291 case '?':
1292 case '&':
1293 case '"':
1294 case '\'':
1295 case '\\':
1296 error(3, "--%s: %s: value must not contain metarule or shell pattern characters", op->name, state.targetprefix);
1297 break;
1298 default:
1299 continue;
1300 }
1301 break;
1302 }
1303 return;
1304 case OPT(OPT_tolerance):
1305 if ((state.tolerance = n) > 60)
1306 error(1, "the time comparison tolerance should probably be less than a minute");
1307 return;
1308 case OPT(OPT_writeobject):
1309 if (!n)
1310 s = 0;
1311 else if (state.makefile)
1312 {
1313 error(1, "%s: object file name cannot change after %s read", op->name, state.makefile);
1314 return;
1315 }
1316 else if (!s)
1317 s = "-";
1318 else
1319 s = strdup(s);
1320 state.writeobject = s;
1321 return;
1322 case OPT(OPT_writestate):
1323 if (!n)
1324 s = 0;
1325 else if (!s)
1326 s = "-";
1327 else
1328 s = strdup(s);
1329 state.writestate = s;
1330 if (state.statefile)
1331 {
1332 free(state.statefile);
1333 state.statefile = 0;
1334 }
1335 return;
1336 }
1337 if (op->value)
1338 {
1339 if (op->flags & Ob)
1340 {
1341 switch (type)
1342 {
1343 case '^':
1344 *((unsigned char*)op->value) ^= n;
1345 break;
1346 default:
1347 *((unsigned char*)op->value) = n != 0;
1348 break;
1349 }
1350 }
1351 else if (op->flags & On)
1352 {
1353 switch (type)
1354 {
1355 case '+':
1356 *((int*)op->value) += n;
1357 break;
1358 case '-':
1359 *((int*)op->value) -= n;
1360 break;
1361 case '|':
1362 *((int*)op->value) |= n;
1363 break;
1364 case '&':
1365 *((int*)op->value) &= n;
1366 break;
1367 case '^':
1368 *((int*)op->value) ^= n;
1369 break;
1370 default:
1371 *((int*)op->value) = n;
1372 break;
1373 }
1374 }
1375 else if (op->flags & Os)
1376 {
1377 if (op->flags & Oa)
1378 {
1379 if (s)
1380 {
1381 /*
1382 * s is a ':' list
1383 */
1384
1385 for (;;)
1386 {
1387 if (t = strchr(s, ':'))
1388 *t = 0;
1389 addprereq((*(Rule_t**)op->value), makerule(s), PREREQ_APPEND);
1390 if (!t)
1391 break;
1392 *t++ = ':';
1393 s = t;
1394 }
1395 }
1396 else
1397 {
1398 freelist((*(Rule_t**)op->value)->prereqs);
1399 (*(Rule_t**)op->value)->prereqs = 0;
1400 }
1401 }
1402 else
1403 *((char**)op->value) = s ? strdup(s) : 0;
1404 }
1405 }
1406 if (op->set)
1407 setcall(op, readonly);
1408 if ((op->flags & Op) && !state.reading && !state.user)
1409 {
1410 if ((op->flags & Os) && s)
1411 {
1412 sfprintf(internal.tmp, "--%s=%s", op->name, s);
1413 if (!(r = getrule(sfstruse(internal.tmp))))
1414 r = makerule(NiL);
1415 addprereq(internal.preprocess, r, PREREQ_APPEND);
1416 }
1417 else if ((op->flags & (Ob|On)) && n)
1418 {
1419 if (!n)
1420 sfprintf(internal.tmp, "--no%s", op->name);
1421 else if (n != 1)
1422 sfprintf(internal.tmp, "--%s=%d", op->name, n);
1423 else
1424 sfprintf(internal.tmp, "--%s", op->name);
1425 if (!(r = getrule(sfstruse(internal.tmp))))
1426 r = makerule(NiL);
1427 addprereq(internal.preprocess, r, PREREQ_APPEND);
1428 }
1429 }
1430 }
1431
1432 /*
1433 * generate a single option setting in sp given the option pointer
1434 * setting:
1435 * 0 only the value is generated, "" if option not set
1436 * '+' only the value is generated, "" if Os option not set, 0 for Ob|Oi not set
1437 * '-' option name and value suitable for set()
1438 * '?' 1 if OPT_SET, "" otherwise
1439 * '#' table attributes with the current value
1440 * flag!=0 if relative to option flag rather than option name
1441 */
1442
1443 static void
genop(register Sfio_t * sp,register Option_t * op,int setting,int flag)1444 genop(register Sfio_t* sp, register Option_t* op, int setting, int flag)
1445 {
1446 register long n;
1447 char* v;
1448 List_t* p;
1449
1450 switch (setting)
1451 {
1452 case '?':
1453 if (op->flags & OPT_SET)
1454 sfprintf(sp, "1");
1455 return;
1456 case '#':
1457 sfprintf(sp, "%s=", showop(op));
1458 break;
1459 case 0:
1460 break;
1461 default:
1462 flag &= ~Oi;
1463 break;
1464 }
1465 switch (op->flags & (Ob|On|Os))
1466 {
1467 case Ob:
1468 if (op->value)
1469 n = *((unsigned char*)op->value);
1470 else
1471 n = 0;
1472 if ((op->flags & Oi) ^ (flag & Oi))
1473 n = !n;
1474 if ((flag & Of) && (op->flags & Oo))
1475 n = !n;
1476 switch (setting)
1477 {
1478 case 0:
1479 if (!n)
1480 return;
1481 /*FALLTHROUGH*/
1482 case '+':
1483 case '#':
1484 break;
1485 default:
1486 if (op->flags & OPT_DEFAULT)
1487 sfprintf(sp, "--%s:=", op->name);
1488 else if (n)
1489 {
1490 sfprintf(sp, "--%s", op->name);
1491 return;
1492 }
1493 else
1494 {
1495 sfprintf(sp, "--no%s", op->name);
1496 return;
1497 }
1498 break;
1499 }
1500 sfputc(sp, n ? '1' : '0');
1501 break;
1502 case On:
1503 if (op->value)
1504 n = *((int*)op->value);
1505 else
1506 n = 0;
1507 if (op->flags & Oi)
1508 n = -n;
1509 if (flag & Oi)
1510 n = !n;
1511 switch (setting)
1512 {
1513 case 0:
1514 if (!n)
1515 return;
1516 /*FALLTHROUGH*/
1517 case '+':
1518 case '#':
1519 break;
1520 default:
1521 if (op->flags & OPT_DEFAULT)
1522 sfprintf(sp, "--%s:=", op->name);
1523 else if (n)
1524 sfprintf(sp, "--%s=", op->name);
1525 else
1526 {
1527 sfprintf(sp, "--no%s", op->name);
1528 return;
1529 }
1530 break;
1531 }
1532 sfprintf(sp, (op->flags & Oa) ? "0x%08lx" : "%ld", n);
1533 break;
1534 case Os:
1535 if ((op->flags & Oa) && op->value && !(flag & Oi))
1536 {
1537 p = (*(Rule_t**)op->value)->prereqs;
1538 switch (setting)
1539 {
1540 case 0:
1541 if (!p)
1542 return;
1543 /*FALLTHROUGH*/
1544 case '+':
1545 case '#':
1546 break;
1547 default:
1548 if (op->flags & OPT_DEFAULT)
1549 sfprintf(sp, "--%s:=", op->name);
1550 else if (p)
1551 sfprintf(sp, "--%s=", op->name);
1552 else
1553 {
1554 sfprintf(sp, "--no%s", op->name);
1555 return;
1556 }
1557 break;
1558 }
1559 if (p)
1560 for (;;)
1561 {
1562 shquote(sp, fmtesc(p->rule->name));
1563 if (!(p = p->next))
1564 break;
1565 sfputc(sp, ':');
1566 }
1567 }
1568 else
1569 {
1570 if (op->value && !(flag & Oi))
1571 v = *((char**)op->value);
1572 else
1573 v = 0;
1574 switch (setting)
1575 {
1576 case 0:
1577 case '#':
1578 if (!v)
1579 return;
1580 setting = 0;
1581 break;
1582 case '+':
1583 break;
1584 default:
1585 if (op->flags & OPT_DEFAULT)
1586 sfprintf(sp, "--%s:=", op->name);
1587 else if (v)
1588 sfprintf(sp, "--%s=", op->name);
1589 else
1590 {
1591 sfprintf(sp, "--no%s", op->name);
1592 return;
1593 }
1594 break;
1595 }
1596 if (v)
1597 {
1598 if (setting)
1599 shquote(sp, fmtesc(v));
1600 else
1601 sfputr(sp, v, -1);
1602 }
1603 }
1604 break;
1605 #if DEBUG
1606 default:
1607 error(PANIC, "%s: option has%s%s%s", op->name, (op->flags & Ob) ? " Ob" : null, (op->flags & On) ? " On" : null, (op->flags & Os) ? " Os" : null);
1608 break;
1609 #endif
1610 }
1611 }
1612
1613 /*
1614 * check and set delayed options
1615 * this gives base, global and local makefiles a chance to
1616 * define the options via --option=definition
1617 */
1618
1619 void
optcheck(int must)1620 optcheck(int must)
1621 {
1622 Oplist_t* x;
1623 int errors;
1624
1625 if (must)
1626 {
1627 errors = error_info.errors;
1628 while (x = opt.delayed)
1629 {
1630 opt.delayed = x->next;
1631 if (*x->option)
1632 set(x->option, 1, NiL);
1633 free(x);
1634 }
1635 if (error_info.errors != errors)
1636 finish(2);
1637 }
1638 else
1639 for (x = opt.delayed; x; x = x->next)
1640 if (!set(x->option, 0, NiL))
1641 *x->option = 0;
1642 }
1643
1644 /*
1645 * generate option setting list in sp suitable for set()
1646 * setting:
1647 * 0 non-Ox OPT_SET options
1648 * '+' non-Om non-Ox OPT_SET options
1649 * '-' all
1650 * '?' non-Ox OPT_DEFAULT
1651 * '#' table attributes with the current value
1652 * '@' internal object file dump
1653 */
1654
1655 void
listops(register Sfio_t * sp,int setting)1656 listops(register Sfio_t* sp, int setting)
1657 {
1658 register Option_t* op;
1659 register Oplist_t* x;
1660 int sc;
1661 int sep;
1662 long mask;
1663 long test;
1664 long clear;
1665
1666 sep = 0;
1667 sc = ' ';
1668 clear = 0;
1669 switch (setting)
1670 {
1671 case '-':
1672 mask = 0;
1673 test = 0;
1674 break;
1675 case '+':
1676 setting = '-';
1677 mask = Om|Ox|OPT_SET;
1678 test = Om|OPT_SET;
1679 break;
1680 case '?':
1681 setting = '-';
1682 mask = Ox|OPT_DEFAULT;
1683 test = OPT_DEFAULT;
1684 break;
1685 case '#':
1686 sc = '\n';
1687 mask = 0;
1688 test = 0;
1689 break;
1690 case '@':
1691 setting = '-';
1692 mask = Ox|OPT_COMPILE;
1693 test = OPT_COMPILE;
1694 clear = ~OPT_COMPILE;
1695 for (op = opt.head; op; op = op->next)
1696 if (op->flags & OPT_DECLARE)
1697 {
1698 op->flags &= ~OPT_DECLARE;
1699 if (sep)
1700 sfputc(sp, ':');
1701 else
1702 {
1703 sep = 1;
1704 sfprintf(sp, "--%s=", optflag(OPT_option)->name);
1705 }
1706 declare(sp, op);
1707 }
1708 while (x = opt.hidden)
1709 {
1710 opt.hidden = x->next;
1711 if (sep)
1712 sfputc(sp, sc);
1713 else
1714 sep = 1;
1715 sfputr(sp, x->option, -1);
1716 free(x);
1717 }
1718 break;
1719 default:
1720 setting = '-';
1721 mask = Ox|OPT_SET;
1722 test = OPT_SET;
1723 break;
1724 }
1725 for (op = opt.head; op; op = op->next)
1726 if ((op->flags & mask) == test)
1727 {
1728 if (sep)
1729 sfputc(sp, sc);
1730 else
1731 sep = 1;
1732 genop(sp, op, setting, 0);
1733 if (clear)
1734 op->flags &= clear;
1735 }
1736 }
1737
1738 /*
1739 * generate a single option setting in sp given the option name
1740 * setting passed to genop()
1741 * end of s is returned
1742 */
1743
1744 void
getop(register Sfio_t * sp,char * name,int setting)1745 getop(register Sfio_t* sp, char* name, int setting)
1746 {
1747 register Option_t* op;
1748 int flag;
1749
1750 if ((op = getoption(name)) && !(flag = 0) || name[0] == 'n' && name[1] == 'o' && (op = getoption(&name[2])) && (flag = Oi) || name[0] && !name[1] && (op = optflag(name[0])) && (flag = Of))
1751 genop(sp, op, setting, flag);
1752 }
1753
1754 /*
1755 * set an option by its optget()/optstr() index
1756 */
1757
1758 static void
optset(int i,char * v,Sfio_t * scope)1759 optset(int i, char* v, Sfio_t* scope)
1760 {
1761 register char* s;
1762 int n;
1763 Option_t* op;
1764 Oplist_t* x;
1765 char buf[16];
1766
1767 if (i > 0)
1768 {
1769 if (state.readonly && !state.interpreter)
1770 {
1771 if (strchr(v, ' ') || strchr(v, '\t'))
1772 {
1773 while (*v && *v != '=')
1774 sfputc(internal.tmp, *v++);
1775 if (*v)
1776 {
1777 sfputc(internal.tmp, *v++);
1778 if (!strchr(v, '\"'))
1779 sfprintf(internal.tmp, "\"%s\"", v);
1780 else
1781 sfprintf(internal.tmp, "'%s'", v);
1782 }
1783 n = sfstrtell(internal.tmp);
1784 v = sfstruse(internal.tmp);
1785 }
1786 else
1787 n = strlen(v);
1788 x = newof(0, Oplist_t, 1, n + 1);
1789 x->option = strcpy((char*)(x + 1), v);
1790 if (opt.lastdelayed)
1791 opt.lastdelayed = opt.lastdelayed->next = x;
1792 else
1793 opt.delayed = opt.lastdelayed = x;
1794 }
1795 else
1796 error((i == '?' && opt_info.option[0] == '-' && opt_info.option[1] != '?') ? (ERROR_USAGE|(state.interpreter ? 2 : 4)) : 2, "%s", opt_info.arg);
1797 }
1798 else
1799 {
1800 if ((i = -OPT_OFFSET - i) < elementsof(options))
1801 op = &options[i];
1802 else
1803 {
1804 sfsprintf(buf, sizeof(buf), "-%d", i);
1805 op = getoption(buf);
1806 }
1807 n = (op->flags & On) ? (opt_info.arg ? opt_info.num : 0) : (op->flags & Ob) ? (opt_info.num != 0) : (opt_info.num != 0) == !(op->flags & Oi);
1808 if (*opt_info.option == '+')
1809 n = !n;
1810 if ((s = opt_info.arg) && (!*s || !(op->flags & Os)))
1811 s = 0;
1812 if (scope)
1813 genop(scope, op, '-', 0);
1814 setop(op, n, s, opt_info.assignment);
1815 }
1816 }
1817
1818 /*
1819 * set options by name
1820 */
1821
1822 int
set(char * s,int must,Sfio_t * scope)1823 set(char* s, int must, Sfio_t* scope)
1824 {
1825 register int i;
1826 int r;
1827 int oreadonly;
1828 Opt_t info;
1829
1830 r = 0;
1831 info = opt_info;
1832 while (i = optstr(s, sfstrbase(opt.usage)))
1833 {
1834 if (i > 0 && !must)
1835 {
1836 r = -1;
1837 break;
1838 }
1839 s += opt_info.offset;
1840 if (!must)
1841 {
1842 oreadonly = state.readonly;
1843 state.readonly = 1;
1844 }
1845 optset(i, opt_info.argv[1], scope);
1846 if (!must)
1847 state.readonly = oreadonly;
1848 }
1849 opt_info = info;
1850 return r;
1851 }
1852
1853 /*
1854 * set command line options with optget(3)
1855 * options may appear in any position before --
1856 * read command line assignments
1857 * mark the command line scripts and targets
1858 * index of the first command line script or target is returned
1859 */
1860
1861 int
scanargs(int argc,char ** argv,int * argf)1862 scanargs(int argc, char** argv, int* argf)
1863 {
1864 register int i;
1865 register char* s;
1866 register int c;
1867 int args;
1868 int done;
1869 char* e;
1870
1871 /*
1872 * generate the optget() usage string from options[]
1873 */
1874
1875 if (!(opt.usage = sfstropen()))
1876 error(ERROR_SYSTEM|3, "out of space [usage]");
1877 sfprintf(opt.usage, usage1, version);
1878 for (i = 0; i < elementsof(options); i++)
1879 genusage(options + i, i, 0);
1880 genusage(NiL, 0, 1);
1881 opt.usageindex = i;
1882 args = 0;
1883 done = 0;
1884 again:
1885 while (i = optget(argv, sfstrbase(opt.usage)))
1886 optset(i, argv[opt_info.index - (opt_info.offset == 0)], NiL);
1887 if (!done && streq(argv[opt_info.index - 1], "--"))
1888 done = 1;
1889 for (i = opt_info.index; i < argc; i++)
1890 {
1891 s = argv[i];
1892 while (isspace(*s))
1893 s++;
1894 if (!done && (*s == '-' || *s == '+') && *(s + 1))
1895 {
1896 opt_info.index = i;
1897 opt_info.offset = 0;
1898 goto again;
1899 }
1900 if (*s)
1901 {
1902 for (e = s; c = *s; s++)
1903 if (c == ',')
1904 {
1905 s = null;
1906 break;
1907 }
1908 else if (istype(c, C_TERMINAL) && c != '+' && c != '&')
1909 {
1910 while (isspace(*s))
1911 s++;
1912 if (*s == '=' || *(s + 1) == '=')
1913 {
1914 argf[i] |= ARG_ASSIGN;
1915 state.reading = 1;
1916 parse(NiL, e, "command line assignment", NiL);
1917 state.reading = 0;
1918 }
1919 else
1920 {
1921 argf[i] |= ARG_SCRIPT;
1922 if (!args)
1923 args = i;
1924 }
1925 break;
1926 }
1927 if (!*s)
1928 {
1929 argf[i] |= ARG_TARGET;
1930 if (!args)
1931 args = i;
1932 }
1933 }
1934 }
1935 return error_info.errors ? -1 : args ? args : argc;
1936 }
1937
1938 /*
1939 * please reboot your program to finish setup ...
1940 *
1941 * old!=0 execs external.old for backwards compatibility
1942 * otherwise re-exec forcing input files to be read
1943 */
1944
1945 void
punt(int old)1946 punt(int old)
1947 {
1948 register char* s;
1949 register char** av;
1950 int i;
1951 List_t* p;
1952 Oplist_t* x;
1953 Sfio_t* vec;
1954
1955 if (state.reread > 1)
1956 error(PANIC, "makefile prerequisites cause unbounded make exec recursion");
1957 vec = sfstropen();
1958 if (old)
1959 {
1960 expand(internal.tmp, getval(external.old, VAL_PRIMARY));
1961 putptr(vec, strdup(sfstruse(internal.tmp)));
1962
1963 /*
1964 * this chunk must track external.old options
1965 */
1966
1967 sfputc(internal.tmp, '-');
1968
1969 /*
1970 * options with same flag and meaning
1971 */
1972
1973 if (error_info.trace < -3)
1974 sfputc(internal.tmp, 'd');
1975 if (state.ignore)
1976 sfputc(internal.tmp, 'i');
1977 if (state.keepgoing)
1978 sfputc(internal.tmp, 'k');
1979 if (!state.mam.options && !state.exec)
1980 sfputc(internal.tmp, 'n');
1981 if (state.silent)
1982 sfputc(internal.tmp, 's');
1983 if (state.touch)
1984 sfputc(internal.tmp, 't');
1985
1986 /*
1987 * options with different flag but same meaning
1988 */
1989
1990 if (state.vardump)
1991 sfputc(internal.tmp, 'p');
1992 if (!state.mam.options && state.force)
1993 sfputc(internal.tmp, 'u');
1994
1995 /*
1996 * options with different flag and meaning
1997 * the external.old meaning prevails
1998 */
1999
2000 if (state.base)
2001 sfputc(internal.tmp, 'b');
2002 if (state.explain)
2003 sfputc(internal.tmp, 'e');
2004 if (state.ruledump)
2005 sfputc(internal.tmp, 'r');
2006 s = sfstruse(internal.tmp);
2007 if (s[1])
2008 putptr(vec, strdup(s));
2009
2010 /*
2011 * mam arguments -- assume oldmake knows mam
2012 */
2013
2014 if (state.mam.options)
2015 {
2016 sfputc(internal.tmp, '-');
2017 if (state.never)
2018 sfputc(internal.tmp, 'N');
2019 else if (!state.exec)
2020 sfputc(internal.tmp, 'n');
2021 if (state.force)
2022 sfputc(internal.tmp, 'F');
2023 sfputc(internal.tmp, 'M');
2024 sfputr(internal.tmp, state.mam.options, -1);
2025 putptr(vec, strdup(sfstruse(internal.tmp)));
2026 }
2027
2028 /*
2029 * delayed (unknown) options
2030 */
2031
2032 for (x = opt.delayed; x; x = x->next)
2033 putptr(vec, x->option);
2034
2035 /*
2036 * makefile arguments
2037 */
2038
2039 if (!(p = internal.makefiles->prereqs))
2040 {
2041 putptr(vec, "-f");
2042 putptr(vec, state.makefile);
2043 }
2044 else for (; p; p = p->next)
2045 {
2046 putptr(vec, "-f");
2047 putptr(vec, p->rule->name);
2048 }
2049
2050 /*
2051 * variable assignment arguments
2052 */
2053
2054 for (i = 1; i < state.argc; i++)
2055 if (state.argf[i] & (ARG_ASSIGN|ARG_TARGET))
2056 putptr(vec, state.argv[i]);
2057 if (!state.silent)
2058 {
2059 /*
2060 * echo the exec action external.old style
2061 */
2062
2063 #if !__sun__ && !sun
2064 sfprintf(sfstderr, "\t");
2065 #endif
2066 putptr(vec, 0);
2067 av = (char**)sfstrbase(vec);
2068 while (*av)
2069 sfprintf(sfstderr, "%s ", *av++);
2070 sfprintf(sfstderr, "\n");
2071 }
2072 }
2073 else
2074 {
2075 /*
2076 * copy the original argv adding OPT_reread
2077 * and possibly OPT_preprocess
2078 */
2079
2080 for (av = state.argv; *av; putptr(vec, *av++));
2081 sfprintf(internal.tmp, "--%s=%d", optflag(OPT_reread)->name, state.reread + 1);
2082 if (state.preprocess)
2083 sfprintf(internal.tmp, "--%s", optflag(OPT_preprocess)->name);
2084 putptr(vec, sfstruse(internal.tmp));
2085 }
2086 putptr(vec, 0);
2087
2088 /*
2089 * tidy up
2090 */
2091
2092 sfsync(sfstdout);
2093 sfsync(sfstderr);
2094 if (internal.openfile)
2095 close(internal.openfd);
2096
2097 /*
2098 * start fresh
2099 */
2100
2101 av = (char**)sfstrbase(vec);
2102 execvp(av[0], av);
2103 error(3, "cannot exec %s", av[0]);
2104 }
2105
2106 /*
2107 * return 1 if name is an option
2108 */
2109
2110 int
isoption(const char * name)2111 isoption(const char* name)
2112 {
2113 return getoption(name) != 0 || name[0] == 'n' && name[1] == 'o' && getoption(&name[2]) != 0;
2114 }
2115