xref: /netbsd-src/external/bsd/pcc/dist/pcc/cc/driver/driver.c (revision 1641c82fac67e97be8d0717d473003b5990c2fe3)
1 /*	Id: driver.c,v 1.7 2011/06/03 15:34:01 plunky Exp 	*/
2 /*	$NetBSD: driver.c,v 1.1.1.1 2011/09/01 12:47:04 plunky Exp $	*/
3 
4 /*-
5  * Copyright (c) 2011 Joerg Sonnenberger <joerg@NetBSD.org>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/wait.h>
34 #include <assert.h>
35 #include <errno.h>
36 #include <signal.h>
37 #include <stdarg.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include "driver.h"
44 #include "xalloc.h"
45 
46 #include "config.h"
47 
48 static volatile sig_atomic_t exit_now;
49 static volatile sig_atomic_t child;
50 
51 static void
sigterm_handler(int signum)52 sigterm_handler(int signum)
53 {
54 	exit_now = 1;
55 	if (child)
56 		kill(child, SIGTERM);
57 }
58 
59 static const char versionstr[] = VERSSTR;
60 
61 enum phases { DEFAULT, PREPROCESS, COMPILE, ASSEMBLE, LINK } last_phase =
62     DEFAULT;
63 
64 const char *isysroot = NULL;
65 const char *sysroot = "";
66 const char *preprocessor;
67 const char *compiler;
68 const char *assembler;
69 const char *linker;
70 
71 struct strlist crtdirs;
72 static struct strlist user_sysincdirs;
73 struct strlist sysincdirs;
74 struct strlist includes;
75 struct strlist incdirs;
76 struct strlist libdirs;
77 struct strlist progdirs;
78 struct strlist preprocessor_flags;
79 struct strlist compiler_flags;
80 struct strlist assembler_flags;
81 struct strlist early_linker_flags;
82 struct strlist middle_linker_flags;
83 struct strlist late_linker_flags;
84 struct strlist stdlib_flags;
85 struct strlist early_program_csu_files;
86 struct strlist late_program_csu_files;
87 struct strlist early_dso_csu_files;
88 struct strlist late_dso_csu_files;
89 struct strlist temp_outputs;
90 
91 const char *final_output;
92 static char *temp_directory;
93 static struct strlist inputs;
94 
95 int pic_mode; /* 0: no PIC, 1: -fpic, 2: -fPIC */
96 int save_temps;
97 int debug_mode;
98 int profile_mode;
99 int nostdinc;
100 int nostdlib;
101 int nostartfiles;
102 int static_mode;
103 int shared_mode;
104 int use_pthread;
105 int verbose_mode;
106 
107 void
error(const char * fmt,...)108 error(const char *fmt, ...)
109 {
110 	va_list arg;
111 	va_start(arg, fmt);
112 	vfprintf(stderr, fmt, arg);
113 	putc('\n', stderr);
114 	va_end(arg);
115 	exit(1);
116 }
117 
118 static void
warning(const char * fmt,...)119 warning(const char *fmt, ...)
120 {
121 	va_list arg;
122 	va_start(arg, fmt);
123 	vfprintf(stderr, fmt, arg);
124 	putc('\n', stderr);
125 	va_end(arg);
126 }
127 
128 static void
set_last_phase(enum phases phase)129 set_last_phase(enum phases phase)
130 {
131 	assert(phase != DEFAULT);
132 	if (last_phase != DEFAULT && phase != last_phase)
133 		error("conflicting compiler options specified");
134 	last_phase = phase;
135 }
136 
137 static void
expand_sysroot(void)138 expand_sysroot(void)
139 {
140 	struct string *s;
141 	struct strlist *lists[] = { &crtdirs, &sysincdirs, &incdirs,
142 	    &user_sysincdirs, &libdirs, &progdirs, NULL };
143 	const char *sysroots[] = { sysroot, isysroot, isysroot, isysroot,
144 	    sysroot, sysroot, NULL };
145 	size_t i, sysroot_len, value_len;
146 	char *path;
147 
148 	assert(sizeof(lists) / sizeof(lists[0]) ==
149 	       sizeof(sysroots) / sizeof(sysroots[0]));
150 
151 	for (i = 0; lists[i] != NULL; ++i) {
152 		STRLIST_FOREACH(s, lists[i]) {
153 			if (s->value[0] != '=')
154 				continue;
155 			sysroot_len = strlen(sysroots[i]);
156 			/* Skipped '=' compensates additional space for '\0' */
157 			value_len = strlen(s->value);
158 			path = xmalloc(sysroot_len + value_len);
159 			memcpy(path, sysroots[i], sysroot_len);
160 			memcpy(path + sysroot_len, s->value + 1, value_len);
161 			free(s->value);
162 			s->value = path;
163 		}
164 	}
165 }
166 
167 static void
missing_argument(const char * argp)168 missing_argument(const char *argp)
169 {
170 	error("Option `%s' required an argument", argp);
171 }
172 
173 static void
split_and_append(struct strlist * l,char * arg)174 split_and_append(struct strlist *l, char *arg)
175 {
176 	char *next;
177 
178 	for (; arg != NULL; arg = NULL) {
179 		next = strchr(arg, ',');
180 		if (next != NULL)
181 			*next++ = '\0';
182 		strlist_append(l, arg);
183 	}
184 }
185 
186 static int
strlist_exec(struct strlist * l)187 strlist_exec(struct strlist *l)
188 {
189 	char **argv;
190 	size_t argc;
191 	int result;
192 
193 	strlist_make_array(l, &argv, &argc);
194 	if (verbose_mode) {
195 		printf("Calling ");
196 		strlist_print(l, stdout);
197 		printf("\n");
198 	}
199 
200 	if (exit_now)
201 		return 1;
202 
203 	switch ((child = fork())) {
204 	case 0:
205 		execvp(argv[0], argv);
206 		result = write(STDERR_FILENO, "Exec of ", 8);
207 		result = write(STDERR_FILENO, argv[0], strlen(argv[0]));
208 		result = write(STDERR_FILENO, "failed\n", 7);
209 		(void)result;
210 		_exit(127);
211 	case -1:
212 		error("fork failed");
213 	default:
214 		while (waitpid(child, &result, 0) == -1 && errno == EINTR)
215 			/* nothing */(void)0;
216 		result = WEXITSTATUS(result);
217 		if (result)
218 			error("%s terminated with status %d", argv[0], result);
219 		while (argc-- > 0)
220 			free(argv[argc]);
221 		free(argv);
222 		break;
223 	}
224 	return exit_now;
225 }
226 
227 static char *
find_file(const char * file,struct strlist * path,int mode)228 find_file(const char *file, struct strlist *path, int mode)
229 {
230 	struct string *s;
231 	char *f;
232 	size_t lf, lp;
233 	int need_sep;
234 
235 	lf = strlen(file);
236 	STRLIST_FOREACH(s, path) {
237 		lp = strlen(s->value);
238 		need_sep = (lp && s->value[lp - 1] != '/') ? 1 : 0;
239 		f = xmalloc(lp + lf + need_sep + 1);
240 		memcpy(f, s->value, lp);
241 		if (need_sep)
242 			f[lp] = '/';
243 		memcpy(f + lp + need_sep, file, lf + 1);
244 		if (access(f, mode) == 0)
245 			return f;
246 		free(f);
247 	}
248 	return xstrdup(file);
249 }
250 
251 static char *
output_name(const char * file,const char * new_suffix,int counter,int last)252 output_name(const char *file, const char *new_suffix, int counter, int last)
253 {
254 	const char *old_suffix;
255 	char *name;
256 	size_t lf, ls, len;
257 	int counter_len;
258 
259 	if (last && final_output)
260 		return xstrdup(final_output);
261 
262 	old_suffix = strrchr(file, '.');
263 	if (old_suffix != NULL && strchr(old_suffix, '/') != NULL)
264 		old_suffix = NULL;
265 	if (old_suffix == NULL)
266 		old_suffix = file + strlen(file);
267 
268 	ls = strlen(new_suffix);
269 	if (save_temps || last) {
270 		lf = old_suffix - file;
271 		name = xmalloc(lf + ls + 1);
272 		memcpy(name, file, lf);
273 		memcpy(name + lf, new_suffix, ls + 1);
274 		return name;
275 	}
276 	if (temp_directory == NULL) {
277 		const char *template;
278 		char *path;
279 		size_t template_len;
280 		int need_sep;
281 
282 		template = getenv("TMPDIR");
283 		if (template == NULL)
284 			template = "/tmp";
285 		template_len = strlen(template);
286 		if (template_len && template[template_len - 1] == '/')
287 			need_sep = 0;
288 		else
289 			need_sep = 1;
290 		path = xmalloc(template_len + need_sep + 6 + 1);
291 		memcpy(path, template, template_len);
292 		if (need_sep)
293 			path[template_len] = '/';
294 		memcpy(path + template_len + need_sep, "pcc-XXXXXX", 11);
295 		if (mkdtemp(path) == NULL)
296 			error("mkdtemp failed: %s", strerror(errno));
297 		temp_directory = path;
298 	}
299 	lf = strlen(temp_directory);
300 	counter_len = snprintf(NULL, 0, "%d", counter);
301 	if (counter_len < 1)
302 		error("snprintf failure");
303 	len = lf + 1 + (size_t)counter_len + ls + 1;
304 	name = xmalloc(len);
305 	snprintf(name, len, "%s/%d%s", temp_directory, counter, new_suffix);
306 	strlist_append(&temp_outputs, name);
307 	return name;
308 }
309 
310 static int
preprocess_input(const char * file,char * input,char ** output,const char * suffix,int counter)311 preprocess_input(const char *file, char *input, char **output,
312     const char *suffix, int counter)
313 {
314 	struct strlist args;
315 	struct string *s;
316 	char *out;
317 	int retval;
318 
319 	strlist_init(&args);
320 	strlist_append_list(&args, &preprocessor_flags);
321 	STRLIST_FOREACH(s, &includes) {
322 		strlist_append(&args, "-i");
323 		strlist_append(&args, s->value);
324 	}
325 	STRLIST_FOREACH(s, &incdirs) {
326 		strlist_append(&args, "-I");
327 		strlist_append(&args, s->value);
328 	}
329 	STRLIST_FOREACH(s, &user_sysincdirs) {
330 		strlist_append(&args, "-S");
331 		strlist_append(&args, s->value);
332 	}
333 	if (!nostdinc) {
334 		STRLIST_FOREACH(s, &sysincdirs) {
335 			strlist_append(&args, "-S");
336 			strlist_append(&args, s->value);
337 		}
338 	}
339 	strlist_append(&args, input);
340 	if (last_phase == PREPROCESS && final_output == NULL)
341 		out = xstrdup("-");
342 	else
343 		out = output_name(file, suffix, counter,
344 		    last_phase == PREPROCESS);
345 	if (strcmp(out, "-"))
346 		strlist_append(&args, out);
347 	strlist_prepend(&args, find_file(preprocessor, &progdirs, X_OK));
348 	*output = out;
349 	retval = strlist_exec(&args);
350 	strlist_free(&args);
351 	return retval;
352 }
353 
354 static int
compile_input(const char * file,char * input,char ** output,const char * suffix,int counter)355 compile_input(const char *file, char *input, char **output,
356     const char *suffix, int counter)
357 {
358 	struct strlist args;
359 	char *out;
360 	int retval;
361 
362 	strlist_init(&args);
363 	strlist_append_list(&args, &compiler_flags);
364 	if (debug_mode)
365 		strlist_append(&args, "-g");
366 	if (pic_mode)
367 		strlist_append(&args, "-k");
368 	if (profile_mode)
369 		warning("-pg is currently ignored");
370 	strlist_append(&args, input);
371 	out = output_name(file, suffix, counter, last_phase == ASSEMBLE);
372 	strlist_append(&args, out);
373 	strlist_prepend(&args, find_file(compiler, &progdirs, X_OK));
374 	*output = out;
375 	retval = strlist_exec(&args);
376 	strlist_free(&args);
377 	return retval;
378 }
379 
380 static int
assemble_input(const char * file,char * input,char ** output,const char * suffix,int counter)381 assemble_input(const char *file, char *input, char **output,
382     const char *suffix, int counter)
383 {
384 	struct strlist args;
385 	char *out;
386 	int retval;
387 
388 	strlist_init(&args);
389 	strlist_append_list(&args, &assembler_flags);
390 	strlist_append(&args, input);
391 	out = output_name(file, ".o", counter, last_phase == COMPILE);
392 	strlist_append(&args, "-o");
393 	strlist_append(&args, out);
394 	strlist_prepend(&args, find_file(assembler, &progdirs, X_OK));
395 	*output = out;
396 	retval = strlist_exec(&args);
397 	strlist_free(&args);
398 	return retval;
399 }
400 
401 static int
handle_input(const char * file)402 handle_input(const char *file)
403 {
404 	static int counter;
405 	const char *suffix;
406 	char *src;
407 	int handled, retval;
408 
409 	++counter;
410 
411 	if (strcmp(file, "-") == 0) {
412 		/* XXX see -x option */
413 		suffix = ".c";
414 	} else {
415 		suffix = strrchr(file, '.');
416 		if (suffix != NULL && strchr(suffix, '/') != NULL)
417 			suffix = NULL;
418 		if (suffix == NULL)
419 			suffix = "";
420 	}
421 
422 	src = xstrdup(file);
423 	if (strcmp(suffix, ".c") == 0) {
424 		suffix = ".i";
425 		retval = preprocess_input(file, src, &src, suffix, counter);
426 		if (retval)
427 			return retval;
428 		handled = 1;
429 	} else if (strcmp(suffix, ".S") == 0) {
430 		suffix = ".s";
431 		retval = preprocess_input(file, src, &src, suffix, counter);
432 		if (retval)
433 			return retval;
434 		handled = 1;
435 	}
436 
437 	if (last_phase == PREPROCESS)
438 		goto done;
439 
440 	if (strcmp(suffix, ".i") == 0) {
441 		suffix = ".s";
442 		retval = compile_input(file, src, &src, suffix, counter);
443 		if (retval)
444 			return retval;
445 		handled = 1;
446 	}
447 	if (last_phase == ASSEMBLE)
448 		goto done;
449 
450 	if (strcmp(suffix, ".s") == 0) {
451 		suffix = ".o";
452 		retval = assemble_input(file, src, &src, suffix, counter);
453 		if (retval)
454 			return retval;
455 		handled = 1;
456 	}
457 	if (last_phase == COMPILE)
458 		goto done;
459 	if (strcmp(suffix, ".o") == 0)
460 		handled = 1;
461 	strlist_append(&middle_linker_flags, src);
462 done:
463 	if (handled)
464 		return 0;
465 	if (last_phase == LINK)
466 		warning("unknown suffix %s, passing file down to linker",
467 		    suffix);
468 	else
469 		warning("unknown suffix %s, skipped", suffix);
470 	free(src);
471 	return 0;
472 }
473 
474 static int
run_linker(void)475 run_linker(void)
476 {
477 	struct strlist linker_flags;
478 	struct strlist *early_csu, *late_csu;
479 	struct string *s;
480 	int retval;
481 
482 	if (final_output) {
483 		strlist_prepend(&early_linker_flags, final_output);
484 		strlist_prepend(&early_linker_flags, "-o");
485 	}
486 	if (!nostdlib)
487 		strlist_append_list(&late_linker_flags, &stdlib_flags);
488 	if (!nostartfiles) {
489 		if (shared_mode) {
490 			early_csu = &early_dso_csu_files;
491 			late_csu = &late_dso_csu_files;
492 		} else {
493 			early_csu = &early_program_csu_files;
494 			late_csu = &late_program_csu_files;
495 		}
496 		STRLIST_FOREACH(s, early_csu)
497 			strlist_append_nocopy(&middle_linker_flags,
498 			    find_file(s->value, &crtdirs, R_OK));
499 		STRLIST_FOREACH(s, late_csu)
500 			strlist_append_nocopy(&late_linker_flags,
501 			    find_file(s->value, &crtdirs, R_OK));
502 	}
503 	strlist_init(&linker_flags);
504 	strlist_append_list(&linker_flags, &early_linker_flags);
505 	strlist_append_list(&linker_flags, &middle_linker_flags);
506 	strlist_append_list(&linker_flags, &late_linker_flags);
507 	strlist_prepend(&linker_flags, find_file(linker, &progdirs, X_OK));
508 
509 	retval = strlist_exec(&linker_flags);
510 
511 	strlist_free(&linker_flags);
512 	return retval;
513 }
514 
515 static void
cleanup(void)516 cleanup(void)
517 {
518 	struct string *file;
519 
520 	STRLIST_FOREACH(file, &temp_outputs) {
521 		if (unlink(file->value) == -1)
522 			warning("removal of ``%s'' failed: %s", file->value,
523 			    strerror(errno));
524 	}
525 	if (temp_directory && rmdir(temp_directory) == -1)
526 		warning("removal of ``%s'' failed: %s", temp_directory,
527 		    strerror(errno));
528 }
529 
530 int
main(int argc,char ** argv)531 main(int argc, char **argv)
532 {
533 	struct string *input;
534 	char *argp;
535 	int retval;
536 
537 	strlist_init(&crtdirs);
538 	strlist_init(&user_sysincdirs);
539 	strlist_init(&sysincdirs);
540 	strlist_init(&incdirs);
541 	strlist_init(&includes);
542 	strlist_init(&libdirs);
543 	strlist_init(&progdirs);
544 	strlist_init(&inputs);
545 	strlist_init(&preprocessor_flags);
546 	strlist_init(&compiler_flags);
547 	strlist_init(&assembler_flags);
548 	strlist_init(&early_linker_flags);
549 	strlist_init(&middle_linker_flags);
550 	strlist_init(&late_linker_flags);
551 	strlist_init(&stdlib_flags);
552 	strlist_init(&early_program_csu_files);
553 	strlist_init(&late_program_csu_files);
554 	strlist_init(&early_dso_csu_files);
555 	strlist_init(&late_dso_csu_files);
556 	strlist_init(&temp_outputs);
557 
558 	init_platform_specific(TARGOS, TARGMACH);
559 
560 	while (--argc) {
561 		++argv;
562 		argp = *argv;
563 
564 		if (*argp != '-' || strcmp(argp, "-") == 0) {
565 			strlist_append(&inputs, argp);
566 			continue;
567 		}
568 		switch (argp[1]) {
569 		case '-':
570 			if (strcmp(argp, "--param") == 0) {
571 				if (argc == 0)
572 					missing_argument(argp);
573 				--argc;
574 				++argv;
575 				/* Unused */
576 				continue;
577 			}
578 			if (strncmp(argp, "--sysroot=", 10) == 0) {
579 				sysroot = argp + 10;
580 				continue;
581 			}
582 			if (strcmp(argp, "--version") == 0) {
583 				printf("%s\n", versionstr);
584 				exit(0);
585 			}
586 			break;
587 		case 'B':
588 			strlist_append(&crtdirs, argp);
589 			strlist_append(&libdirs, argp);
590 			strlist_append(&progdirs, argp);
591 			continue;
592 		case 'C':
593 			if (argp[2] == '\0') {
594 				strlist_append(&preprocessor_flags, argp);
595 				continue;
596 			}
597 			break;
598 		case 'c':
599 			if (argp[2] == '\0') {
600 				set_last_phase(COMPILE);
601 				continue;
602 			}
603 			break;
604 		case 'D':
605 			strlist_append(&preprocessor_flags, argp);
606 			if (argp[2] == '\0') {
607 				if (argc == 0)
608 					missing_argument(argp);
609 				--argc;
610 				++argv;
611 				strlist_append(&preprocessor_flags, argp);
612 			}
613 			continue;
614 		case 'E':
615 			if (argp[2] == '\0') {
616 				set_last_phase(PREPROCESS);
617 				continue;
618 			}
619 			break;
620 		case 'f':
621 			if (strcmp(argp, "-fpic") == 0) {
622 				pic_mode = 1;
623 				continue;
624 			}
625 			if (strcmp(argp, "-fPIC") == 0) {
626 				pic_mode = 2;
627 				continue;
628 			}
629 			/* XXX GCC options */
630 			break;
631 		case 'g':
632 			if (argp[2] == '\0') {
633 				debug_mode = 1;
634 				continue;
635 			}
636 			/* XXX allow variants like -g1? */
637 			break;
638 		case 'I':
639 			if (argp[2] == '\0') {
640 				if (argc == 0)
641 					missing_argument(argp);
642 				--argc;
643 				++argv;
644 				strlist_append(&incdirs, argp);
645 				continue;
646 			}
647 			strlist_append(&incdirs, argp + 2);
648 			continue;
649 		case 'i':
650 			if (strcmp(argp, "-isystem") == 0) {
651 				if (argc == 0)
652 					missing_argument(argp);
653 				--argc;
654 				++argv;
655 				strlist_append(&user_sysincdirs, argp);
656 				continue;
657 			}
658 			if (strcmp(argp, "-include") == 0) {
659 				if (argc == 0)
660 					missing_argument(argp);
661 				--argc;
662 				++argv;
663 				strlist_append(&includes, argp);
664 				continue;
665 			}
666 			if (strcmp(argp, "-isysroot") == 0) {
667 				if (argc == 0)
668 					missing_argument(argp);
669 				--argc;
670 				++argv;
671 				isysroot = argp;
672 				continue;
673 			}
674 			/* XXX -idirafter */
675 			/* XXX -iquote */
676 			break;
677 		case 'k':
678 			if (argp[2] == '\0') {
679 				pic_mode = 1;
680 				continue;
681 			}
682 			break;
683 		case 'M':
684 			if (argp[2] == '\0') {
685 				strlist_append(&preprocessor_flags, argp);
686 				continue;
687 			}
688 			break;
689 		case 'm':
690 			/* XXX implement me */
691 			break;
692 		case 'n':
693 			if (strcmp(argp, "-nostdinc") == 0) {
694 				nostdinc = 1;
695 				continue;
696 			}
697 			if (strcmp(argp, "-nostdinc++") == 0)
698 				continue;
699 			if (strcmp(argp, "-nostdlib") == 0) {
700 				nostdlib = 1;
701 				nostartfiles = 1;
702 				continue;
703 			}
704 			if (strcmp(argp, "-nostartfiles") == 0) {
705 				nostartfiles = 1;
706 				continue;
707 			}
708 			break;
709 		case 'O':
710 			if (argp[2] != '\0' && argp[3] != '\0')
711 				break;
712 			switch(argp[2]) {
713 			case '2':
714 			case '1': case '\0':
715 				strlist_append(&compiler_flags, "-xtemps");
716 				strlist_append(&compiler_flags, "-xdeljumps");
717 				strlist_append(&compiler_flags, "-xinline");
718 			case '0':
719 				continue;
720 			}
721 			break;
722 		case 'o':
723 			if (argp[2] == '\0') {
724 				if (argc == 0)
725 					missing_argument(argp);
726 				--argc;
727 				++argv;
728 				if (final_output)
729 					error("Only one `-o' option allowed");
730 				final_output = *argv;
731 				continue;
732 			}
733 			break;
734 		case 'p':
735 			if (argp[2] == '\0' || strcmp(argp, "-pg") == 0) {
736 				profile_mode = 1;
737 				continue;
738 			}
739 			if (strcmp(argp, "-pedantic") == 0)
740 				continue;
741 			if (strcmp(argp, "-pipe") == 0)
742 				continue; /* XXX implement me */
743 			if (strcmp(argp, "-pthread") == 0) {
744 				use_pthread = 1;
745 				continue;
746 			}
747 			/* XXX -print-prog-name=XXX */
748 			/* XXX -print-multi-os-directory */
749 			break;
750 		case 'r':
751 			if (argp[2] == '\0') {
752 				strlist_append(&middle_linker_flags, argp);
753 				continue;
754 			}
755 			break;
756 		case 'S':
757 			if (argp[2] == '\0') {
758 				set_last_phase(ASSEMBLE);
759 				continue;
760 			}
761 			break;
762 		case 's':
763 			if (strcmp(argp, "-save-temps") == 0) {
764 				save_temps = 1;
765 				continue;
766 			}
767 			if (strcmp(argp, "-shared") == 0) {
768 				shared_mode = 1;
769 				continue;
770 			}
771 			if (strcmp(argp, "-static") == 0) {
772 				static_mode = 1;
773 				continue;
774 			}
775 			if (strncmp(argp, "-std=", 5) == 0)
776 				continue; /* XXX sanitize me */
777 			break;
778 		case 't':
779 			if (argp[2] == '\0') {
780 				strlist_append(&preprocessor_flags, argp);
781 				continue;
782 			}
783 		case 'U':
784 			strlist_append(&preprocessor_flags, argp);
785 			if (argp[2] == '\0') {
786 				if (argc == 0)
787 					missing_argument(argp);
788 				--argc;
789 				++argv;
790 				strlist_append(&preprocessor_flags, argp);
791 			}
792 			continue;
793 		case 'v':
794 			if (argp[2] == '\0') {
795 				verbose_mode = 1;
796 				continue;
797 			}
798 			break;
799 		case 'W':
800 			if (strncmp(argp, "-Wa,", 4) == 0) {
801 				split_and_append(&assembler_flags, argp + 4);
802 				continue;
803 			}
804 			if (strncmp(argp, "-Wl,", 4) == 0) {
805 				split_and_append(&middle_linker_flags, argp + 4);
806 				continue;
807 			}
808 			if (strncmp(argp, "-Wp,", 4) == 0) {
809 				split_and_append(&preprocessor_flags, argp + 4);
810 				continue;
811 			}
812 			/* XXX warning flags */
813 			break;
814 		case 'x':
815 			/* XXX -x c */
816 			/* XXX -c assembler-with-cpp */
817 			break;
818 		}
819 		error("unknown flag `%s'", argp);
820 	}
821 
822 	if (last_phase == DEFAULT)
823 		last_phase = LINK;
824 
825 	if (verbose_mode)
826 		printf("%s\n", versionstr);
827 
828 	if (isysroot == NULL)
829 		isysroot = sysroot;
830 	expand_sysroot();
831 
832 	if (last_phase != LINK && final_output && !STRLIST_EMPTY(&inputs) &&
833 	    !STRLIST_NEXT(STRLIST_FIRST(&inputs)))
834 		error("-o specified with more than one input");
835 
836 	if (last_phase == PREPROCESS && final_output == NULL)
837 		final_output = "-";
838 
839 	if (STRLIST_EMPTY(&inputs))
840 		error("No input specificed");
841 
842 	retval = 0;
843 
844 	signal(SIGTERM, sigterm_handler);
845 
846 	STRLIST_FOREACH(input, &inputs) {
847 		if (handle_input(input->value))
848 			retval = 1;
849 	}
850 	if (!retval && last_phase == LINK) {
851 		if (run_linker())
852 			retval = 1;
853 	}
854 
855 	if (exit_now)
856 		warning("Received signal, terminating");
857 
858 	cleanup();
859 
860 	strlist_free(&crtdirs);
861 	strlist_free(&user_sysincdirs);
862 	strlist_free(&sysincdirs);
863 	strlist_free(&incdirs);
864 	strlist_free(&includes);
865 	strlist_free(&libdirs);
866 	strlist_free(&progdirs);
867 	strlist_free(&inputs);
868 	strlist_free(&preprocessor_flags);
869 	strlist_free(&compiler_flags);
870 	strlist_free(&assembler_flags);
871 	strlist_free(&early_linker_flags);
872 	strlist_free(&middle_linker_flags);
873 	strlist_free(&late_linker_flags);
874 	strlist_free(&stdlib_flags);
875 	strlist_free(&early_program_csu_files);
876 	strlist_free(&late_program_csu_files);
877 	strlist_free(&early_dso_csu_files);
878 	strlist_free(&late_dso_csu_files);
879 	strlist_free(&temp_outputs);
880 
881 	return retval;
882 }
883