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