xref: /illumos-gate/usr/src/tools/cw/cw.c (revision b3cd65e66b4d22c513629005636c0e232d1e1d26)
1 
2 /*
3  * CDDL HEADER START
4  *
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License (the "License").
7  * You may not use this file except in compliance with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  *
27  * Copyright 2018 Richard Lowe.
28  * Copyright 2019 Joyent, Inc.
29  * Copyright 2025 Oxide Computer Company
30  */
31 
32 /*
33  * Wrapper for the GNU C compiler to make it accept the Sun C compiler
34  * arguments where possible.
35  *
36  * Since the translation is inexact, this is something of a work-in-progress.
37  *
38  */
39 
40 /*
41  * If you modify this file, you must increment CW_VERSION.  This is a semver,
42  * incompatible changes should bump the major, anything else the minor.
43  */
44 #define	CW_VERSION	"10.0"
45 
46 /*
47  * -#		Verbose mode
48  * -###		Show compiler commands built by driver, no compilation
49  * -A<name[(tokens)]>	Preprocessor predicate assertion
50  * -C		Prevent preprocessor from removing comments
51  * -c		Compile only - produce .o files, suppress linking
52  * -D<name[=token]>	Associate name with token as if by #define
53  * -d[y|n]	dynamic [-dy] or static [-dn] option to linker
54  * -E		Compile source through preprocessor only, output to stdout
55  * -erroff=<t>	Suppress warnings specified by tags t(%none, %all, <tag list>)
56  * -errtags=<a>	Display messages with tags a(no, yes)
57  * -errwarn=<t>	Treats warnings specified by tags t(%none, %all, <tag list>)
58  *		as errors
59  * -g		Compile for debugging
60  * -H		Print path name of each file included during compilation
61  * -h <name>	Assign <name> to generated dynamic shared library
62  * -I<dir>	Add <dir> to preprocessor #include file search path
63  * -i		Passed to linker to ignore any LD_LIBRARY_PATH setting
64  * -keeptmp	Keep temporary files created during compilation
65  * -L<dir>	Pass to linker to add <dir> to the library search path
66  * -l<name>	Link with library lib<name>.a or lib<name>.so
67  * -mt		Specify options needed when compiling multi-threaded code
68  * -O		Use default optimization level (-xO2 or -xO3. Check man page.)
69  * -o <outputfile> Set name of output file to <outputfile>
70  * -P		Compile source through preprocessor only, output to .i  file
71  * -p		Compile for profiling with prof
72  * -R<dir[:dir]> Build runtime search path list into executable
73  * -S		Compile and only generate assembly code (.s)
74  * -s		Strip symbol table from the executable file
75  * -std=	Set the C standard (note this is in the GNU syntax)
76  * -t		Turn off duplicate symbol warnings when linking
77  * -U<name>	Delete initial definition of preprocessor symbol <name>
78  * -V		Report version number of each compilation phase
79  * -v		Do stricter semantic checking
80  * -W<c>,<arg>	Pass <arg> to specified component <c> (a,l,m,p,0,2,h,i,u)
81  * -w		Suppress compiler warning messages
82  * -Xa		Compile assuming ANSI C conformance, allow K & R extensions
83  *		(default mode)
84  * -Xs		Compile assuming (pre-ANSI) K & R C style code
85  * -xarch=<a>	Specify target architecture instruction set
86  * -xbuiltin[=<b>] When profitable inline, or substitute intrinisic functions
87  *		for system functions, b={%all,%none}
88  * -xchip=<c>	Specify the target processor for use by the optimizer
89  * -xO<n>	Generate optimized code (n={1|2|3|4|5})
90  * -xtarget=<t>	Specify target system for optimization
91  * -YI,<dir>	Specify <dir> for location of headers
92  */
93 
94 /*
95  * Translation table:
96  */
97 /*
98  * -#				-v
99  * -###				error
100  * -A<name[(tokens)]>		pass-thru
101  * -B<[static|dynamic]>		pass-thru (syntax error for anything else)
102  * -C				pass-thru
103  * -c				pass-thru
104  * -D<name[=token]>		pass-thru
105  * -E				pass-thru
106  * -errtags=%all		-Wall
107  * -errwarn=%all		-Werror else -Wno-error
108  * -g				pass-thru
109  * -H				pass-thru
110  * -I<dir>			pass-thru
111  * -i				pass-thru
112  * -keeptmp			-save-temps
113  * -L<dir>			pass-thru
114  * -l<name>			pass-thru
115  * -mt				-D_REENTRANT
116  * -nolib			-nodefaultlibs
117  * -O				-O1 (Check the man page to be certain)
118  * -o <outputfile>		pass-thru
119  * -P				-E -o filename.i (or error)
120  * -p				pass-thru
121  * -R<dir[:dir]>		pass-thru
122  * -S				pass-thru
123  * -std=			pass-thru
124  * -U<name>			pass-thru
125  * -V				--version
126  * -v				-Wall
127  * -Wa,<arg>			pass-thru
128  * -Wp,<arg>			pass-thru
129  * -Wl,<arg>			pass-thru
130  * -xmodel=kernel		-ffreestanding -mcmodel=kernel -mno-red-zone
131  * -Wu,-save_args		-msave-args
132  * -w				pass-thru
133  * -Xa				-std=iso9899:199409 or -ansi
134  * -Xs				-traditional -std=c89
135  * -xarch=<a>			table
136  * -xbuiltin[=<b>]		-fbuiltin (-fno-builtin otherwise)
137  * -xchip=<c>			table
138  * -xO<n>			-O<n>
139  * -xtarget=<t>			table
140  * -xtransition			-Wtransition
141  * -YI,<dir>			-nostdinc -I<dir>
142  */
143 
144 #include <ctype.h>
145 #include <err.h>
146 #include <errno.h>
147 #include <fcntl.h>
148 #include <getopt.h>
149 #include <stdio.h>
150 #include <stdlib.h>
151 #include <stdbool.h>
152 #include <string.h>
153 #include <unistd.h>
154 #include <dirent.h>
155 
156 #include <sys/param.h>
157 #include <sys/stat.h>
158 #include <sys/types.h>
159 #include <sys/utsname.h>
160 #include <sys/wait.h>
161 
162 #define	CW_F_CXX	0x01
163 #define	CW_F_SHADOW	0x02
164 #define	CW_F_EXEC	0x04
165 #define	CW_F_ECHO	0x08
166 #define	CW_F_XLATE	0x10
167 #define	CW_F_PROG	0x20
168 #define	CW_F_TIME	0x40
169 
170 typedef enum cw_op {
171 	CW_O_NONE = 0,
172 	CW_O_PREPROCESS,
173 	CW_O_COMPILE,
174 	CW_O_LINK
175 } cw_op_t;
176 
177 struct aelist {
178 	struct ae {
179 		struct ae *ae_next;
180 		char *ae_arg;
181 	} *ael_head, *ael_tail;
182 	int ael_argc;
183 };
184 
185 typedef enum {
186 	GNU,
187 	SUN,
188 	SMATCH
189 } compiler_style_t;
190 
191 typedef enum {
192 	SOURCE_FILE_T_NONE,
193 	SOURCE_FILE_T_C,
194 	SOURCE_FILE_T_S,
195 	SOURCE_FILE_T_CPP,
196 	SOURCE_FILE_T_CCX,
197 	SOURCE_FILE_T_I
198 } source_file_type_t;
199 
200 typedef struct {
201 	char *c_name;
202 	char *c_path;
203 	compiler_style_t c_style;
204 } cw_compiler_t;
205 
206 typedef struct cw_ictx {
207 	struct cw_ictx	*i_next;
208 	cw_compiler_t	*i_compiler;
209 	char		*i_linker;
210 	struct aelist	*i_ae;
211 	uint32_t	i_flags;
212 	int		i_oldargc;
213 	char		**i_oldargv;
214 	pid_t		i_pid;
215 	char		*i_tmpdir;
216 	char		*i_stderr;
217 } cw_ictx_t;
218 
219 /*
220  * Status values to indicate which Studio compiler and associated
221  * flags are being used.
222  */
223 #define	M32		0x01	/* -m32 - only on Studio 12 */
224 #define	M64		0x02	/* -m64 - only on Studio 12 */
225 #define	SS11		0x100	/* Studio 11 */
226 #define	SS12		0x200	/* Studio 12 */
227 
228 #define	TRANS_ENTRY	5
229 /*
230  * Translation table definition for the -xarch= flag. The "x_arg"
231  * value is translated into the appropriate gcc flags according
232  * to the values in x_trans[n]. The x_flags indicates what compiler
233  * is being used and what flags have been set via the use of
234  * "x_arg".
235  */
236 typedef struct xarch_table {
237 	char	*x_arg;
238 	int	x_flags;
239 	char	*x_trans[TRANS_ENTRY];
240 } xarch_table_t;
241 
242 /*
243  * The translation table for the -xarch= flag used in the Studio compilers.
244  */
245 static const xarch_table_t xtbl[] = {
246 #if defined(__x86)
247 	{ "generic",	SS11, {NULL} },
248 	{ "generic64",	(SS11|M64), { "-m64", "-mtune=opteron" } },
249 	{ "amd64",	(SS11|M64), { "-m64", "-mtune=opteron" } },
250 	{ "386",	SS11,	{ "-march=i386" } },
251 	{ "pentium_pro", SS11,	{ "-march=pentiumpro" } },
252 	{ "sse",	SS11, { "-msse", "-mfpmath=sse" } },
253 	{ "sse2",	SS11, { "-msse2", "-mfpmath=sse" } },
254 #endif
255 };
256 
257 static int xtbl_size = sizeof (xtbl) / sizeof (xarch_table_t);
258 
259 static const char *xchip_tbl[] = {
260 #if defined(__x86)
261 	"386",		"-mtune=i386", NULL,
262 	"486",		"-mtune=i486", NULL,
263 	"pentium",	"-mtune=pentium", NULL,
264 	"pentium_pro",  "-mtune=pentiumpro", NULL,
265 #endif
266 	NULL,		NULL
267 };
268 
269 static const char *xtarget_tbl[] = {
270 #if defined(__x86)
271 	"pentium_pro",	"-march=pentiumpro", NULL,
272 #endif	/* __x86 */
273 	NULL,		NULL
274 };
275 
276 static void
nomem(void)277 nomem(void)
278 {
279 	errx(1, "out of memory");
280 }
281 
282 static void
newae(struct aelist * ael,const char * arg)283 newae(struct aelist *ael, const char *arg)
284 {
285 	struct ae *ae;
286 
287 	if ((ae = calloc(1, sizeof (*ae))) == NULL)
288 		nomem();
289 	ae->ae_arg = strdup(arg);
290 	if (ael->ael_tail == NULL)
291 		ael->ael_head = ae;
292 	else
293 		ael->ael_tail->ae_next = ae;
294 	ael->ael_tail = ae;
295 	ael->ael_argc++;
296 }
297 
298 static cw_ictx_t *
newictx(void)299 newictx(void)
300 {
301 	cw_ictx_t *ctx = calloc(1, sizeof (cw_ictx_t));
302 	if (ctx)
303 		if ((ctx->i_ae = calloc(1, sizeof (struct aelist))) == NULL) {
304 			free(ctx);
305 			return (NULL);
306 		}
307 
308 	return (ctx);
309 }
310 
311 static void
error(const char * arg)312 error(const char *arg)
313 {
314 	errx(2, "error: mapping failed at or near arg '%s'", arg);
315 }
316 
317 /*
318  * Add the current favourite set of warnings to the gcc invocation.
319  */
320 static void
warnings(struct aelist * h)321 warnings(struct aelist *h)
322 {
323 	static int warningsonce;
324 
325 	if (warningsonce++)
326 		return;
327 
328 	/*
329 	 * Enable as many warnings as exist, then disable those that we never
330 	 * ever want.
331 	 */
332 	newae(h, "-Wall");
333 	newae(h, "-Wextra");
334 }
335 
336 static void
optim_disable(struct aelist * h,int level)337 optim_disable(struct aelist *h, int level)
338 {
339 	if (level >= 2) {
340 		newae(h, "-fno-strict-aliasing");
341 		newae(h, "-fno-unit-at-a-time");
342 		newae(h, "-fno-optimize-sibling-calls");
343 	}
344 }
345 
346 static void
Xsmode(struct aelist * h)347 Xsmode(struct aelist *h)
348 {
349 	static int xsonce;
350 
351 	if (xsonce++)
352 		return;
353 
354 	newae(h, "-traditional");
355 	newae(h, "-traditional-cpp");
356 }
357 
358 static void
usage(void)359 usage(void)
360 {
361 	extern char *__progname;
362 	(void) fprintf(stderr,
363 	    "usage: %s [-C] [--tag arg] [--versions] --primary <compiler> "
364 	    "[--shadow <compiler>]... -- cflags...\n",
365 	    __progname);
366 	(void) fprintf(stderr, "compilers take the form: name,path,style\n"
367 	    " - name: a unique name usable in flag specifiers\n"
368 	    " - path: path to the compiler binary\n"
369 	    " - style: the style of flags expected: either sun or gnu\n");
370 	exit(2);
371 }
372 
373 static int
xlate_xtb(struct aelist * h,const char * xarg)374 xlate_xtb(struct aelist *h, const char *xarg)
375 {
376 	int	i, j;
377 
378 	for (i = 0; i < xtbl_size; i++) {
379 		if (strcmp(xtbl[i].x_arg, xarg) == 0)
380 			break;
381 	}
382 
383 	/*
384 	 * At the end of the table and so no matching "arg" entry
385 	 * found and so this must be a bad -xarch= flag.
386 	 */
387 	if (i == xtbl_size)
388 		error(xarg);
389 
390 	for (j = 0; j < TRANS_ENTRY; j++) {
391 		if (xtbl[i].x_trans[j] != NULL)
392 			newae(h, xtbl[i].x_trans[j]);
393 		else
394 			break;
395 	}
396 	return (xtbl[i].x_flags);
397 
398 }
399 
400 static void
xlate(struct aelist * h,const char * xarg,const char ** table)401 xlate(struct aelist *h, const char *xarg, const char **table)
402 {
403 	while (*table != NULL && strcmp(xarg, *table) != 0) {
404 		while (*table != NULL)
405 			table++;
406 		table++;
407 	}
408 
409 	if (*table == NULL)
410 		error(xarg);
411 
412 	table++;
413 
414 	while (*table != NULL) {
415 		newae(h, *table);
416 		table++;
417 	}
418 }
419 
420 /*
421  * The compiler wants the output file to end in appropriate extension.  If
422  * we're generating a name from whole cloth (path == NULL), we assume that
423  * extension to be .o, otherwise we match the extension of the caller.
424  */
425 static char *
discard_file_name(cw_ictx_t * ctx,const char * path)426 discard_file_name(cw_ictx_t *ctx, const char *path)
427 {
428 	char *ret, *ext;
429 	char tmpl[] = "cwXXXXXX";
430 
431 	if (path == NULL) {
432 		ext = ".o";
433 	} else {
434 		ext = strrchr(path, '.');
435 	}
436 
437 	/*
438 	 * We need absolute control over where the temporary file goes, since
439 	 * we rely on it for cleanup so tempnam(3C) and tmpnam(3C) are
440 	 * inappropriate (they use TMPDIR, preferentially).
441 	 *
442 	 * mkstemp(3C) doesn't actually help us, since the temporary file
443 	 * isn't used by us, only its name.
444 	 */
445 	if (mktemp(tmpl) == NULL)
446 		nomem();
447 
448 	(void) asprintf(&ret, "%s/%s%s", ctx->i_tmpdir, tmpl,
449 	    (ext != NULL) ? ext : "");
450 
451 	if (ret == NULL)
452 		nomem();
453 
454 	return (ret);
455 }
456 
457 static source_file_type_t
id_source_file(const char * path)458 id_source_file(const char *path)
459 {
460 	const char *ext = strrchr(path, '.');
461 
462 	if ((ext == NULL) || (*(ext + 1) == '\0'))
463 		return (SOURCE_FILE_T_NONE);
464 
465 	ext += 1;
466 
467 	if (strcasecmp(ext, "c") == 0) {
468 		return (SOURCE_FILE_T_C);
469 	} else if (strcmp(ext, "cc") == 0) {
470 		return (SOURCE_FILE_T_CCX);
471 	} else if (strcmp(ext, "i") == 0) {
472 		return (SOURCE_FILE_T_I);
473 	} else if (strcasecmp(ext, "s") == 0) {
474 		return (SOURCE_FILE_T_S);
475 	} else if (strcmp(ext, "cpp") == 0) {
476 		return (SOURCE_FILE_T_CPP);
477 	}
478 
479 	return (SOURCE_FILE_T_NONE);
480 
481 }
482 
483 static bool
is_asm_file(const char * path)484 is_asm_file(const char *path)
485 {
486 	return (id_source_file(path) == SOURCE_FILE_T_S);
487 }
488 
489 static void
do_gcc(cw_ictx_t * ctx)490 do_gcc(cw_ictx_t *ctx)
491 {
492 	int c;
493 	int nolibc = 0;
494 	int in_output = 0, seen_o = 0, src_files = 0;
495 	cw_op_t op = CW_O_LINK;
496 	char *model = NULL;
497 	char *nameflag;
498 	int mflag = 0;
499 	bool seen_cstd = false, check_cstd = false;
500 
501 	if (ctx->i_flags & CW_F_PROG) {
502 		newae(ctx->i_ae, "--version");
503 		return;
504 	}
505 
506 	newae(ctx->i_ae, "-fident");
507 	newae(ctx->i_ae, "-finline");
508 	newae(ctx->i_ae, "-fno-inline-functions");
509 	newae(ctx->i_ae, "-fno-builtin");
510 	newae(ctx->i_ae, "-fno-asm");
511 	newae(ctx->i_ae, "-fdiagnostics-show-option");
512 	newae(ctx->i_ae, "-nodefaultlibs");
513 
514 	/*
515 	 * This is needed because 'u' is defined
516 	 * under a conditional on 'sun'.  Should
517 	 * probably just remove the conditional,
518 	 * or make it be dependent on '__sun'.
519 	 *
520 	 * -Dunix is also missing in enhanced ANSI mode
521 	 */
522 	newae(ctx->i_ae, "-D__sun");
523 
524 	if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1)
525 		nomem();
526 
527 	/*
528 	 * Walk the argument list, translating as we go ..
529 	 */
530 	while (--ctx->i_oldargc > 0) {
531 		char *arg = *++ctx->i_oldargv;
532 		size_t arglen = strlen(arg);
533 
534 		if (*arg == '-') {
535 			arglen--;
536 		} else {
537 			if (!in_output) {
538 				source_file_type_t type = id_source_file(arg);
539 				if (type != SOURCE_FILE_T_NONE)
540 					src_files++;
541 				if (type == SOURCE_FILE_T_C)
542 					check_cstd = true;
543 			}
544 
545 			/*
546 			 * Otherwise, filenames and partial arguments
547 			 * are passed through for gcc to chew on.  However,
548 			 * output is always discarded for the secondary
549 			 * compiler.
550 			 */
551 			if ((ctx->i_flags & CW_F_SHADOW) && in_output) {
552 				newae(ctx->i_ae, discard_file_name(ctx, arg));
553 			} else {
554 				newae(ctx->i_ae, arg);
555 			}
556 			in_output = 0;
557 			continue;
558 		}
559 
560 		if (ctx->i_flags & CW_F_CXX) {
561 			if (strncmp(arg, "-_g++=", 6) == 0) {
562 				newae(ctx->i_ae, strchr(arg, '=') + 1);
563 				continue;
564 			}
565 
566 			if (strcmp(arg, "-xwe") == 0) {
567 				/* turn warnings into errors */
568 				newae(ctx->i_ae, "-Werror");
569 				continue;
570 			}
571 			if (strcmp(arg, "-nolib") == 0) {
572 				/* -nodefaultlibs is on by default */
573 				nolibc = 1;
574 				continue;
575 			}
576 		}
577 
578 		switch ((c = arg[1])) {
579 		case '_':
580 			if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) ||
581 			    (strncmp(arg, "-_gcc=", 6) == 0) ||
582 			    (strncmp(arg, "-_gnu=", 6) == 0)) {
583 				newae(ctx->i_ae, strchr(arg, '=') + 1);
584 			}
585 			break;
586 		case '#':
587 			if (arglen == 1) {
588 				newae(ctx->i_ae, "-v");
589 				break;
590 			}
591 			error(arg);
592 			break;
593 		case 'f':
594 			if ((strcmp(arg, "-fpic") == 0) ||
595 			    (strcmp(arg, "-fPIC") == 0)) {
596 				newae(ctx->i_ae, arg);
597 				break;
598 			}
599 			error(arg);
600 			break;
601 		case 'E':
602 			if (arglen == 1) {
603 				newae(ctx->i_ae, "-xc");
604 				newae(ctx->i_ae, arg);
605 				op = CW_O_PREPROCESS;
606 				nolibc = 1;
607 				break;
608 			}
609 			error(arg);
610 			break;
611 		case 'c':
612 		case 'S':
613 			if (arglen == 1) {
614 				op = CW_O_COMPILE;
615 				nolibc = 1;
616 			}
617 			/* FALLTHROUGH */
618 		case 'C':
619 		case 'H':
620 		case 'p':
621 			if (arglen == 1) {
622 				newae(ctx->i_ae, arg);
623 				break;
624 			}
625 			error(arg);
626 			break;
627 		case 'A':
628 		case 'g':
629 		case 'I':
630 		case 'i':
631 		case 'L':
632 		case 'l':
633 		case 'R':
634 		case 'U':
635 		case 'u':
636 		case 'w':
637 			newae(ctx->i_ae, arg);
638 			break;
639 		case 'o':
640 			seen_o = 1;
641 			if (arglen == 1) {
642 				in_output = 1;
643 				newae(ctx->i_ae, arg);
644 			} else if (ctx->i_flags & CW_F_SHADOW) {
645 				newae(ctx->i_ae, "-o");
646 				newae(ctx->i_ae, discard_file_name(ctx, arg));
647 			} else {
648 				newae(ctx->i_ae, arg);
649 			}
650 			break;
651 		case 'D':
652 			newae(ctx->i_ae, arg);
653 			/*
654 			 * XXX	Clearly a hack ... do we need _KADB too?
655 			 */
656 			if (strcmp(arg, "-D_KERNEL") == 0 ||
657 			    strcmp(arg, "-D_BOOT") == 0)
658 				newae(ctx->i_ae, "-ffreestanding");
659 			break;
660 		case 'e':
661 			if (strcmp(arg, "-errtags=yes") == 0) {
662 				warnings(ctx->i_ae);
663 				break;
664 			}
665 			if (strcmp(arg, "-errwarn=%all") == 0) {
666 				newae(ctx->i_ae, "-Werror");
667 				break;
668 			}
669 			error(arg);
670 			break;
671 		case 'k':
672 			if (strcmp(arg, "-keeptmp") == 0) {
673 				newae(ctx->i_ae, "-save-temps");
674 				break;
675 			}
676 			error(arg);
677 			break;
678 		case 'm':
679 			if (strcmp(arg, "-mt") == 0) {
680 				newae(ctx->i_ae, "-D_REENTRANT");
681 				break;
682 			}
683 			if (strcmp(arg, "-m64") == 0) {
684 				newae(ctx->i_ae, "-m64");
685 #if defined(__x86)
686 				newae(ctx->i_ae, "-mtune=opteron");
687 #endif
688 				mflag |= M64;
689 				break;
690 			}
691 			if (strcmp(arg, "-m32") == 0) {
692 				newae(ctx->i_ae, "-m32");
693 				mflag |= M32;
694 				break;
695 			}
696 			error(arg);
697 			break;
698 		case 'O':
699 			if (arglen == 1) {
700 				newae(ctx->i_ae, "-O");
701 				break;
702 			}
703 			error(arg);
704 			break;
705 		case 'P':
706 			/*
707 			 * We could do '-E -o filename.i', but that's hard,
708 			 * and we don't need it for the case that's triggering
709 			 * this addition.  We'll require the user to specify
710 			 * -o in the Makefile.  If they don't they'll find out
711 			 * in a hurry.
712 			 */
713 			newae(ctx->i_ae, "-E");
714 			op = CW_O_PREPROCESS;
715 			nolibc = 1;
716 			break;
717 		case 's':
718 			if (strcmp(arg, "-shared") == 0) {
719 				newae(ctx->i_ae, "-shared");
720 				nolibc = 1;
721 				break;
722 			}
723 
724 			if (strncmp(arg, "-std=", 4) == 0) {
725 				seen_cstd = true;
726 				newae(ctx->i_ae, arg);
727 				break;
728 			}
729 			error(arg);
730 			break;
731 
732 		case 'V':
733 			if (arglen == 1) {
734 				ctx->i_flags &= ~CW_F_ECHO;
735 				newae(ctx->i_ae, "--version");
736 				break;
737 			}
738 			error(arg);
739 			break;
740 		case 'v':
741 			if (arglen == 1) {
742 				warnings(ctx->i_ae);
743 				break;
744 			}
745 			error(arg);
746 			break;
747 		case 'W':
748 			if (strncmp(arg, "-Wa,", 4) == 0 ||
749 			    strncmp(arg, "-Wp,", 4) == 0 ||
750 			    strncmp(arg, "-Wl,", 4) == 0) {
751 				newae(ctx->i_ae, arg);
752 				break;
753 			}
754 
755 #if defined(__x86)
756 			if (strcmp(arg, "-Wu,-save_args") == 0) {
757 				newae(ctx->i_ae, "-msave-args");
758 				break;
759 			}
760 #endif	/* __x86 */
761 			error(arg);
762 			break;
763 		case 'X':
764 			if (strcmp(arg, "-Xs") == 0) {
765 				Xsmode(ctx->i_ae);
766 				break;
767 			}
768 			error(arg);
769 			break;
770 		case 'x':
771 			if (arglen == 1)
772 				error(arg);
773 			switch (arg[2]) {
774 			case 'a':
775 				if (strncmp(arg, "-xarch=", 7) == 0) {
776 					mflag |= xlate_xtb(ctx->i_ae, arg + 7);
777 					break;
778 				}
779 				error(arg);
780 				break;
781 			case 'b':
782 				if (strncmp(arg, "-xbuiltin=", 10) == 0) {
783 					if (strcmp(arg + 10, "%all"))
784 						newae(ctx->i_ae, "-fbuiltin");
785 					break;
786 				}
787 				error(arg);
788 				break;
789 			case 'c':
790 				if (strncmp(arg, "-xc99=%all", 10) == 0) {
791 					seen_cstd = true;
792 					newae(ctx->i_ae, "-std=gnu99");
793 					break;
794 				}
795 				if (strncmp(arg, "-xc99=%none", 11) == 0) {
796 					seen_cstd = true;
797 					newae(ctx->i_ae, "-std=gnu89");
798 					break;
799 				}
800 				if (strncmp(arg, "-xchip=", 7) == 0) {
801 					xlate(ctx->i_ae, arg + 7, xchip_tbl);
802 					break;
803 				}
804 
805 				error(arg);
806 				break;
807 #if defined(__x86)
808 			case 'm':
809 				if (strcmp(arg, "-xmodel=kernel") == 0) {
810 					newae(ctx->i_ae, "-ffreestanding");
811 					newae(ctx->i_ae, "-mno-red-zone");
812 					model = "-mcmodel=kernel";
813 					nolibc = 1;
814 					break;
815 				}
816 				error(arg);
817 				break;
818 #endif	/* __x86 */
819 			case 'O':
820 				if (strncmp(arg, "-xO", 3) == 0) {
821 					size_t len = strlen(arg);
822 					char *s = NULL;
823 					int c = *(arg + 3);
824 					int level;
825 
826 					if (len != 4 || !isdigit(c))
827 						error(arg);
828 
829 					level = atoi(arg + 3);
830 					if (level > 5)
831 						error(arg);
832 					if (level >= 2) {
833 						/*
834 						 * For gcc-3.4.x at -O2 we
835 						 * need to disable optimizations
836 						 * that break ON.
837 						 */
838 						optim_disable(ctx->i_ae, level);
839 						/*
840 						 * limit -xO3 to -O2 as well.
841 						 */
842 						level = 2;
843 					}
844 					if (asprintf(&s, "-O%d", level) == -1)
845 						nomem();
846 					newae(ctx->i_ae, s);
847 					free(s);
848 					break;
849 				}
850 				error(arg);
851 				break;
852 			case 't':
853 				if (strncmp(arg, "-xtarget=", 9) == 0) {
854 					xlate(ctx->i_ae, arg + 9, xtarget_tbl);
855 					break;
856 				}
857 				error(arg);
858 				break;
859 			default:
860 				error(arg);
861 				break;
862 			}
863 			break;
864 		case 'Y':
865 			if (arglen == 1) {
866 				if ((arg = *++ctx->i_oldargv) == NULL ||
867 				    *arg == '\0')
868 					error("-Y");
869 				ctx->i_oldargc--;
870 				arglen = strlen(arg + 1);
871 			} else {
872 				arg += 2;
873 			}
874 			if (strncmp(arg, "I,", 2) == 0) {
875 				char *s = strdup(arg);
876 				s[0] = '-';
877 				s[1] = 'I';
878 				newae(ctx->i_ae, "-nostdinc");
879 				newae(ctx->i_ae, s);
880 				free(s);
881 				break;
882 			}
883 			error(arg);
884 			break;
885 		default:
886 			error(arg);
887 			break;
888 		}
889 	}
890 
891 	free(nameflag);
892 
893 	/*
894 	 * We would like to catch occurrences where we have left out a -std
895 	 * argument so we don't end up with the compiler's default. The use of
896 	 * check_cstd keys on seeing a .c file, which ensures that we don't
897 	 * check C++, assembly, or other files, though we should check C++ at
898 	 * some point. In addition, if we do a link in a single action, it will
899 	 * actually catch it as well because we will see a .c file. However, if
900 	 * we're doing a normal link of multiple files then it won't misfire.
901 	 */
902 	if (check_cstd && !seen_cstd) {
903 		errx(2, "missing required -std= specification");
904 	}
905 
906 	/*
907 	 * When compiling multiple source files in a single invocation some
908 	 * compilers output objects into the current directory with
909 	 * predictable and conventional names.
910 	 *
911 	 * We prevent any attempt to compile multiple files at once so that
912 	 * any such objects created by a shadow can't escape into a later
913 	 * link-edit.
914 	 */
915 	if (src_files > 1 && op != CW_O_PREPROCESS) {
916 		errx(2, "multiple source files are "
917 		    "allowed only with -E or -P");
918 	}
919 
920 	/*
921 	 * Make sure that we do not have any unintended interactions between
922 	 * the xarch options passed in and the version of the Studio compiler
923 	 * used.
924 	 */
925 	if ((mflag & (SS11|SS12)) == (SS11|SS12)) {
926 		errx(2,
927 		    "Conflicting \"-xarch=\" flags (both Studio 11 and 12)\n");
928 	}
929 
930 	switch (mflag) {
931 	case 0:
932 	case M32:
933 	case M64:
934 	case SS12:
935 	case SS11:
936 	case (SS11|M32):
937 	case (SS11|M64):
938 	case (SS12|M32):
939 	case (SS12|M64):
940 		break;
941 	default:
942 		(void) fprintf(stderr,
943 		    "Incompatible -xarch= and/or -m32/-m64 options used.\n");
944 		exit(2);
945 	}
946 
947 	if (ctx->i_flags & CW_F_SHADOW) {
948 		if (op == CW_O_PREPROCESS)
949 			exit(0);
950 		else if (op == CW_O_LINK && src_files == 0)
951 			exit(0);
952 	}
953 
954 	if (model != NULL)
955 		newae(ctx->i_ae, model);
956 	if (!nolibc)
957 		newae(ctx->i_ae, "-lc");
958 	if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) {
959 		newae(ctx->i_ae, "-o");
960 		newae(ctx->i_ae, discard_file_name(ctx, NULL));
961 	}
962 }
963 
964 static void
do_smatch(cw_ictx_t * ctx)965 do_smatch(cw_ictx_t *ctx)
966 {
967 	if (ctx->i_flags & CW_F_PROG) {
968 		newae(ctx->i_ae, "--version");
969 		return;
970 	}
971 
972 	/*
973 	 * Some sources shouldn't run smatch at all.
974 	 */
975 	for (int i = 0; i < ctx->i_oldargc; i++) {
976 		char *arg = ctx->i_oldargv[i];
977 
978 		if (strcmp(arg, "-_smatch=off") == 0) {
979 			ctx->i_flags &= ~(CW_F_EXEC | CW_F_ECHO);
980 			return;
981 		}
982 
983 		/* smatch can't handle asm */
984 		if ((arg[0] != '-') && is_asm_file(arg)) {
985 			ctx->i_flags &= ~(CW_F_EXEC | CW_F_ECHO);
986 			return;
987 		}
988 	}
989 
990 	/*
991 	 * smatch can handle gcc's options.
992 	 */
993 	do_gcc(ctx);
994 }
995 
996 static void
do_cc(cw_ictx_t * ctx)997 do_cc(cw_ictx_t *ctx)
998 {
999 	int in_output = 0, seen_o = 0, c_files = 0;
1000 	cw_op_t op = CW_O_LINK;
1001 	char *nameflag;
1002 
1003 	if (ctx->i_flags & CW_F_PROG) {
1004 		newae(ctx->i_ae, "-V");
1005 		return;
1006 	}
1007 
1008 	if (asprintf(&nameflag, "-_%s=", ctx->i_compiler->c_name) == -1)
1009 		nomem();
1010 
1011 	while (--ctx->i_oldargc > 0) {
1012 		char *arg = *++ctx->i_oldargv;
1013 
1014 		if (strncmp(arg, "-_CC=", 5) == 0) {
1015 			newae(ctx->i_ae, strchr(arg, '=') + 1);
1016 			continue;
1017 		}
1018 
1019 		if (*arg != '-') {
1020 			if (!in_output && id_source_file(arg) !=
1021 			    SOURCE_FILE_T_NONE)
1022 				c_files++;
1023 
1024 			if (in_output == 0 || !(ctx->i_flags & CW_F_SHADOW)) {
1025 				newae(ctx->i_ae, arg);
1026 			} else {
1027 				in_output = 0;
1028 				newae(ctx->i_ae, discard_file_name(ctx, arg));
1029 			}
1030 			continue;
1031 		}
1032 		switch (*(arg + 1)) {
1033 		case '_':
1034 			if ((strncmp(arg, nameflag, strlen(nameflag)) == 0) ||
1035 			    (strncmp(arg, "-_cc=", 5) == 0) ||
1036 			    (strncmp(arg, "-_sun=", 6) == 0)) {
1037 				newae(ctx->i_ae, strchr(arg, '=') + 1);
1038 			}
1039 			break;
1040 
1041 		case 'V':
1042 			ctx->i_flags &= ~CW_F_ECHO;
1043 			newae(ctx->i_ae, arg);
1044 			break;
1045 		case 'o':
1046 			seen_o = 1;
1047 			if (strlen(arg) == 2) {
1048 				in_output = 1;
1049 				newae(ctx->i_ae, arg);
1050 			} else if (ctx->i_flags & CW_F_SHADOW) {
1051 				newae(ctx->i_ae, "-o");
1052 				newae(ctx->i_ae, discard_file_name(ctx, arg));
1053 			} else {
1054 				newae(ctx->i_ae, arg);
1055 			}
1056 			break;
1057 		case 'c':
1058 		case 'S':
1059 			if (strlen(arg) == 2)
1060 				op = CW_O_COMPILE;
1061 			newae(ctx->i_ae, arg);
1062 			break;
1063 		case 'E':
1064 		case 'P':
1065 			if (strlen(arg) == 2)
1066 				op = CW_O_PREPROCESS;
1067 		/*FALLTHROUGH*/
1068 		default:
1069 			newae(ctx->i_ae, arg);
1070 		}
1071 	}
1072 
1073 	free(nameflag);
1074 
1075 	/* See the comment on this same code in do_gcc() */
1076 	if (c_files > 1 && op != CW_O_PREPROCESS) {
1077 		errx(2, "multiple source files are "
1078 		    "allowed only with -E or -P");
1079 	}
1080 
1081 	if (ctx->i_flags & CW_F_SHADOW) {
1082 		if (op == CW_O_PREPROCESS)
1083 			exit(0);
1084 		else if (op == CW_O_LINK && c_files == 0)
1085 			exit(0);
1086 	}
1087 
1088 	if (!seen_o && (ctx->i_flags & CW_F_SHADOW)) {
1089 		newae(ctx->i_ae, "-o");
1090 		newae(ctx->i_ae, discard_file_name(ctx, NULL));
1091 	}
1092 }
1093 
1094 static void
prepctx(cw_ictx_t * ctx)1095 prepctx(cw_ictx_t *ctx)
1096 {
1097 	newae(ctx->i_ae, ctx->i_compiler->c_path);
1098 
1099 	if (ctx->i_flags & CW_F_PROG) {
1100 		(void) printf("%s: %s\n", (ctx->i_flags & CW_F_SHADOW) ?
1101 		    "shadow" : "primary", ctx->i_compiler->c_path);
1102 		(void) fflush(stdout);
1103 	}
1104 
1105 	/*
1106 	 * If LD_ALTEXEC is already set, the expectation would be that that
1107 	 * link-editor is run, as such we need to leave it the environment
1108 	 * alone and let that happen.
1109 	 */
1110 	if ((ctx->i_linker != NULL) && (getenv("LD_ALTEXEC") == NULL))
1111 		setenv("LD_ALTEXEC", ctx->i_linker, 1);
1112 
1113 	if (!(ctx->i_flags & CW_F_XLATE))
1114 		return;
1115 
1116 	switch (ctx->i_compiler->c_style) {
1117 	case SUN:
1118 		do_cc(ctx);
1119 		break;
1120 	case GNU:
1121 		do_gcc(ctx);
1122 		break;
1123 	case SMATCH:
1124 		do_smatch(ctx);
1125 		break;
1126 	}
1127 }
1128 
1129 static int
invoke(cw_ictx_t * ctx)1130 invoke(cw_ictx_t *ctx)
1131 {
1132 	char **newargv, *makeflags;
1133 	int ac;
1134 	struct ae *a;
1135 
1136 	newargv = calloc(ctx->i_ae->ael_argc + 1, sizeof (*newargv));
1137 	if (newargv == NULL)
1138 		nomem();
1139 
1140 	/*
1141 	 * Check to see if the silent make flag is present (-s), if so, do not
1142 	 * echo. The MAKEFLAGS environment variable is set by dmake. By
1143 	 * observation it appears to place short flags without any arguments
1144 	 * first followed by any long form flags or flags with arguments.
1145 	 */
1146 	makeflags = getenv("MAKEFLAGS");
1147 	if (makeflags != NULL) {
1148 		size_t makeflags_len = strlen(makeflags);
1149 		for (size_t i = 0; i < makeflags_len; i++) {
1150 			if (makeflags[i] == 's') {
1151 				ctx->i_flags &= ~CW_F_ECHO;
1152 				break;
1153 			}
1154 			/* end of short flags */
1155 			if (makeflags[i] == ' ')
1156 				break;
1157 		}
1158 	}
1159 
1160 	if (ctx->i_flags & CW_F_ECHO)
1161 		(void) fprintf(stderr, "+ ");
1162 
1163 	for (ac = 0, a = ctx->i_ae->ael_head; a; a = a->ae_next, ac++) {
1164 		newargv[ac] = a->ae_arg;
1165 		if (ctx->i_flags & CW_F_ECHO)
1166 			(void) fprintf(stderr, "%s ", a->ae_arg);
1167 		if (a == ctx->i_ae->ael_tail)
1168 			break;
1169 	}
1170 
1171 	if (ctx->i_flags & CW_F_ECHO) {
1172 		(void) fprintf(stderr, "\n");
1173 		(void) fflush(stderr);
1174 	}
1175 
1176 	if (!(ctx->i_flags & CW_F_EXEC))
1177 		return (0);
1178 
1179 	/*
1180 	 * We must fix up the environment here so that the dependency files are
1181 	 * not trampled by the shadow compiler. Also take care of GCC
1182 	 * environment variables that will throw off gcc. This assumes a primary
1183 	 * gcc.
1184 	 */
1185 	if ((ctx->i_flags & CW_F_SHADOW) &&
1186 	    (unsetenv("SUNPRO_DEPENDENCIES") != 0 ||
1187 	    unsetenv("DEPENDENCIES_OUTPUT") != 0 ||
1188 	    unsetenv("GCC_ROOT") != 0)) {
1189 		(void) fprintf(stderr, "error: environment setup failed: %s\n",
1190 		    strerror(errno));
1191 		return (-1);
1192 	}
1193 
1194 	(void) execv(newargv[0], newargv);
1195 	warn("couldn't run %s", newargv[0]);
1196 
1197 	return (-1);
1198 }
1199 
1200 static int
reap(cw_ictx_t * ctx)1201 reap(cw_ictx_t *ctx)
1202 {
1203 	int status, ret = 0;
1204 	char buf[1024];
1205 	struct stat s;
1206 	struct rusage res;
1207 
1208 	/*
1209 	 * Only wait for one specific child.
1210 	 */
1211 	if (ctx->i_pid <= 0)
1212 		return (-1);
1213 
1214 	do {
1215 		if (wait4(ctx->i_pid, &status, 0, &res) < 0) {
1216 			warn("cannot reap child");
1217 			return (-1);
1218 		}
1219 		if (status != 0) {
1220 			if (WIFSIGNALED(status)) {
1221 				ret = -WTERMSIG(status);
1222 				break;
1223 			} else if (WIFEXITED(status)) {
1224 				ret = WEXITSTATUS(status);
1225 				break;
1226 			}
1227 		}
1228 	} while (!WIFEXITED(status) && !WIFSIGNALED(status));
1229 	if (ctx->i_flags & CW_F_TIME) {
1230 		(void) fprintf(stderr, "+ %s: user time: %ld.%6.6ld "
1231 		    "system time: %ld.%6.6ld\n",
1232 		    ctx->i_compiler->c_name,
1233 		    res.ru_utime.tv_sec,
1234 		    res.ru_utime.tv_usec,
1235 		    res.ru_stime.tv_sec,
1236 		    res.ru_stime.tv_usec);
1237 	}
1238 
1239 	if (stat(ctx->i_stderr, &s) < 0) {
1240 		warn("stat failed on child cleanup");
1241 		return (-1);
1242 	}
1243 	if (s.st_size != 0) {
1244 		FILE *f;
1245 
1246 		if ((f = fopen(ctx->i_stderr, "r")) != NULL) {
1247 			while (fgets(buf, sizeof (buf), f))
1248 				(void) fprintf(stderr, "%s", buf);
1249 			(void) fflush(stderr);
1250 			(void) fclose(f);
1251 		}
1252 	}
1253 	(void) unlink(ctx->i_stderr);
1254 	free(ctx->i_stderr);
1255 
1256 	/*
1257 	 * cc returns an error code when given -V; we want that to succeed.
1258 	 */
1259 	if (ctx->i_flags & CW_F_PROG)
1260 		return (0);
1261 
1262 	return (ret);
1263 }
1264 
1265 static int
exec_ctx(cw_ictx_t * ctx,int block)1266 exec_ctx(cw_ictx_t *ctx, int block)
1267 {
1268 	if ((ctx->i_stderr = tempnam(ctx->i_tmpdir, "cw")) == NULL) {
1269 		nomem();
1270 		return (-1);
1271 	}
1272 
1273 	if ((ctx->i_pid = fork()) == 0) {
1274 		int fd;
1275 
1276 		(void) fclose(stderr);
1277 		if ((fd = open(ctx->i_stderr, O_WRONLY | O_CREAT | O_EXCL,
1278 		    0666)) < 0) {
1279 			err(1, "open failed for standard error");
1280 		}
1281 		if (dup2(fd, 2) < 0) {
1282 			err(1, "dup2 failed for standard error");
1283 		}
1284 		if (fd != 2)
1285 			(void) close(fd);
1286 		if (freopen("/dev/fd/2", "w", stderr) == NULL) {
1287 			err(1, "freopen failed for /dev/fd/2");
1288 		}
1289 
1290 		prepctx(ctx);
1291 		exit(invoke(ctx));
1292 	}
1293 
1294 	if (ctx->i_pid < 0) {
1295 		err(1, "fork failed");
1296 	}
1297 
1298 	if (block)
1299 		return (reap(ctx));
1300 
1301 	return (0);
1302 }
1303 
1304 static void
parse_compiler(const char * spec,cw_compiler_t * compiler)1305 parse_compiler(const char *spec, cw_compiler_t *compiler)
1306 {
1307 	char *tspec, *token;
1308 
1309 	if ((tspec = strdup(spec)) == NULL)
1310 		nomem();
1311 
1312 	if ((token = strsep(&tspec, ",")) == NULL)
1313 		errx(1, "Compiler is missing a name: %s", spec);
1314 	compiler->c_name = token;
1315 
1316 	if ((token = strsep(&tspec, ",")) == NULL)
1317 		errx(1, "Compiler is missing a path: %s", spec);
1318 	compiler->c_path = token;
1319 
1320 	if ((token = strsep(&tspec, ",")) == NULL)
1321 		errx(1, "Compiler is missing a style: %s", spec);
1322 
1323 	if ((strcasecmp(token, "gnu") == 0) ||
1324 	    (strcasecmp(token, "gcc") == 0)) {
1325 		compiler->c_style = GNU;
1326 	} else if ((strcasecmp(token, "sun") == 0) ||
1327 	    (strcasecmp(token, "cc") == 0)) {
1328 		compiler->c_style = SUN;
1329 	} else if ((strcasecmp(token, "smatch") == 0)) {
1330 		compiler->c_style = SMATCH;
1331 	} else {
1332 		errx(1, "unknown compiler style: %s", token);
1333 	}
1334 
1335 	if (tspec != NULL)
1336 		errx(1, "Excess tokens in compiler: %s", spec);
1337 }
1338 
1339 static void
cleanup(cw_ictx_t * ctx)1340 cleanup(cw_ictx_t *ctx)
1341 {
1342 	DIR *dirp;
1343 	struct dirent *dp;
1344 	char buf[MAXPATHLEN];
1345 
1346 	if ((dirp = opendir(ctx->i_tmpdir)) == NULL) {
1347 		if (errno != ENOENT) {
1348 			err(1, "couldn't open temp directory: %s",
1349 			    ctx->i_tmpdir);
1350 		} else {
1351 			return;
1352 		}
1353 	}
1354 
1355 	errno = 0;
1356 	while ((dp = readdir(dirp)) != NULL) {
1357 		(void) snprintf(buf, MAXPATHLEN, "%s/%s", ctx->i_tmpdir,
1358 		    dp->d_name);
1359 
1360 		if (strcmp(dp->d_name, ".") == 0 ||
1361 		    strcmp(dp->d_name, "..") == 0) {
1362 			continue;
1363 		}
1364 
1365 		if (unlink(buf) == -1)
1366 			err(1, "failed to unlink temp file: %s", dp->d_name);
1367 		errno = 0;
1368 	}
1369 
1370 	if (errno != 0) {
1371 		err(1, "failed to read temporary directory: %s",
1372 		    ctx->i_tmpdir);
1373 	}
1374 
1375 	(void) closedir(dirp);
1376 	if (rmdir(ctx->i_tmpdir) != 0) {
1377 		err(1, "failed to unlink temporary directory: %s",
1378 		    ctx->i_tmpdir);
1379 	}
1380 }
1381 
1382 int
main(int argc,char ** argv)1383 main(int argc, char **argv)
1384 {
1385 	int ch;
1386 	cw_compiler_t primary = { NULL, NULL, 0 };
1387 	cw_compiler_t shadows[10];
1388 	int nshadows = 0;
1389 	int ret = 0;
1390 	bool do_serial;
1391 	bool do_exec;
1392 	bool vflg = false;
1393 	bool Cflg = false;
1394 	bool cflg = false;
1395 	bool nflg = false;
1396 	bool Tflg = false;
1397 	char *tmpdir;
1398 
1399 	cw_ictx_t *main_ctx;
1400 
1401 	static struct option longopts[] = {
1402 		{ "compiler", no_argument, NULL, 'c' },
1403 		{ "linker", required_argument, NULL, 'l' },
1404 		{ "noecho", no_argument, NULL, 'n' },
1405 		{ "primary", required_argument, NULL, 'p' },
1406 		{ "shadow", required_argument, NULL, 's' },
1407 		{ "tag", required_argument, NULL, 't' },
1408 		{ "time", no_argument, NULL, 'T' },
1409 		{ "versions", no_argument, NULL, 'v' },
1410 		{ NULL, 0, NULL, 0 },
1411 	};
1412 
1413 
1414 	if ((main_ctx = newictx()) == NULL)
1415 		nomem();
1416 
1417 	while ((ch = getopt_long(argc, argv, "C", longopts, NULL)) != -1) {
1418 		switch (ch) {
1419 		case 'c':
1420 			cflg = true;
1421 			break;
1422 		case 'C':
1423 			Cflg = true;
1424 			break;
1425 		case 'l':
1426 			if ((main_ctx->i_linker = strdup(optarg)) == NULL)
1427 				nomem();
1428 			break;
1429 		case 'n':
1430 			nflg = true;
1431 			break;
1432 		case 'p':
1433 			if (primary.c_path != NULL) {
1434 				warnx("Only one primary compiler may "
1435 				    "be specified");
1436 				usage();
1437 			}
1438 
1439 			parse_compiler(optarg, &primary);
1440 			break;
1441 		case 's':
1442 			if (nshadows >= 10)
1443 				errx(1, "May only use 10 shadows at "
1444 				    "the moment");
1445 			parse_compiler(optarg, &shadows[nshadows]);
1446 			nshadows++;
1447 			break;
1448 		case 't':	/* Ignored, a diagnostic in build logs only */
1449 			break;
1450 		case 'T':
1451 			Tflg = true;
1452 			break;
1453 		case 'v':
1454 			vflg = true;
1455 			break;
1456 		default:
1457 			(void) fprintf(stderr, "Did you forget '--'?\n");
1458 			usage();
1459 		}
1460 	}
1461 
1462 	if (primary.c_path == NULL) {
1463 		warnx("A primary compiler must be specified");
1464 		usage();
1465 	}
1466 
1467 	do_serial = getenv("CW_SHADOW_SERIAL") != NULL;
1468 	do_exec = getenv("CW_NO_EXEC") == NULL;
1469 
1470 	/* Leave room for argv[0] */
1471 	argc -= (optind - 1);
1472 	argv += (optind - 1);
1473 
1474 	main_ctx->i_oldargc = argc;
1475 	main_ctx->i_oldargv = argv;
1476 	main_ctx->i_flags = CW_F_XLATE;
1477 	if (nflg == 0)
1478 		main_ctx->i_flags |= CW_F_ECHO;
1479 	if (do_exec)
1480 		main_ctx->i_flags |= CW_F_EXEC;
1481 	if (Cflg)
1482 		main_ctx->i_flags |= CW_F_CXX;
1483 	if (Tflg)
1484 		main_ctx->i_flags |= CW_F_TIME;
1485 	main_ctx->i_compiler = &primary;
1486 
1487 	if (cflg) {
1488 		(void) fputs(primary.c_path, stdout);
1489 	}
1490 
1491 	if (vflg) {
1492 		(void) printf("cw version %s\n", CW_VERSION);
1493 		(void) fflush(stdout);
1494 		main_ctx->i_flags &= ~CW_F_ECHO;
1495 		main_ctx->i_flags |= CW_F_PROG | CW_F_EXEC;
1496 		do_serial = 1;
1497 	}
1498 
1499 	tmpdir = getenv("TMPDIR");
1500 	if (tmpdir == NULL)
1501 		tmpdir = "/tmp";
1502 
1503 	if (asprintf(&main_ctx->i_tmpdir, "%s/cw.XXXXXX", tmpdir) == -1)
1504 		nomem();
1505 
1506 	if ((main_ctx->i_tmpdir = mkdtemp(main_ctx->i_tmpdir)) == NULL)
1507 		errx(1, "failed to create temporary directory");
1508 
1509 	ret |= exec_ctx(main_ctx, do_serial);
1510 
1511 	for (int i = 0; i < nshadows; i++) {
1512 		int r;
1513 		cw_ictx_t *shadow_ctx;
1514 
1515 		if ((shadow_ctx = newictx()) == NULL)
1516 			nomem();
1517 
1518 		(void) memcpy(shadow_ctx, main_ctx, sizeof (cw_ictx_t));
1519 
1520 		shadow_ctx->i_flags |= CW_F_SHADOW;
1521 		shadow_ctx->i_compiler = &shadows[i];
1522 
1523 		r = exec_ctx(shadow_ctx, do_serial);
1524 		if (r == 0) {
1525 			shadow_ctx->i_next = main_ctx->i_next;
1526 			main_ctx->i_next = shadow_ctx;
1527 		}
1528 		ret |= r;
1529 	}
1530 
1531 	if (!do_serial) {
1532 		cw_ictx_t *next = main_ctx;
1533 		while (next != NULL) {
1534 			cw_ictx_t *toreap = next;
1535 			next = next->i_next;
1536 			ret |= reap(toreap);
1537 		}
1538 	}
1539 
1540 	cleanup(main_ctx);
1541 	return (ret);
1542 }
1543