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