Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

lcc.c (19499B)


      1 /*
      2  * lcc [ option ]... [ file | -llib ]...
      3  * front end for the ANSI C compiler
      4  */
      5 static char rcsid[] = "Id: dummy rcsid";
      6 
      7 #include <stdio.h>
      8 #include <stdarg.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <assert.h>
     12 #include <ctype.h>
     13 #include <signal.h>
     14 
     15 #ifndef TEMPDIR
     16 #define TEMPDIR "/tmp"
     17 #endif
     18 
     19 typedef struct list *List;
     20 struct list {		/* circular list nodes: */
     21 	char *str;		/* option or file name */
     22 	List link;		/* next list element */
     23 };
     24 
     25 static void *alloc(int);
     26 static List append(char *,List);
     27 extern char *basepath(char *);
     28 static int callsys(char *[]);
     29 extern char *concat(char *, char *);
     30 static int compile(char *, char *);
     31 static void compose(char *[], List, List, List);
     32 static void error(char *, char *);
     33 static char *exists(char *);
     34 static char *first(char *);
     35 static int filename(char *, char *);
     36 static List find(char *, List);
     37 static void help(void);
     38 static void initinputs(void);
     39 static void interrupt(int);
     40 static void opt(char *);
     41 static List path2list(const char *);
     42 extern int main(int, char *[]);
     43 extern char *replace(const char *, int, int);
     44 static void rm(List);
     45 extern char *strsave(const char *);
     46 extern char *stringf(const char *, ...);
     47 extern int suffix(char *, char *[], int);
     48 extern char *tempname(char *);
     49 
     50 extern int access(char *, int);
     51 extern int getpid(void);
     52 
     53 extern char *cpp[], *include[], *com[], *as[],*ld[], inputs[], *suffixes[];
     54 extern int option(char *);
     55 
     56 static int errcnt;		/* number of errors */
     57 static int Eflag;		/* -E specified */
     58 static int Sflag;		/* -S specified */
     59 static int cflag;		/* -c specified */
     60 static int verbose;		/* incremented for each -v */
     61 static List llist[2];		/* loader files, flags */
     62 static List alist;		/* assembler flags */
     63 static List clist;		/* compiler flags */
     64 static List plist;		/* preprocessor flags */
     65 static List ilist;		/* list of additional includes from LCCINPUTS */
     66 static List rmlist;		/* list of files to remove */
     67 static char *outfile;		/* ld output file or -[cS] object file */
     68 static int ac;			/* argument count */
     69 static char **av;		/* argument vector */
     70 char *tempdir = TEMPDIR;	/* directory for temporary files */
     71 static char *progname;
     72 static List lccinputs;		/* list of input directories */
     73 
     74 main(int argc, char *argv[]) {
     75 	int i, j, nf;
     76 	
     77 	progname = argv[0];
     78 	ac = argc + 50;
     79 	av = alloc(ac*sizeof(char *));
     80 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
     81 		signal(SIGINT, interrupt);
     82 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
     83 		signal(SIGTERM, interrupt);
     84 #ifdef SIGHUP
     85 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
     86 		signal(SIGHUP, interrupt);
     87 #endif
     88 	if (getenv("TMP"))
     89 		tempdir = getenv("TMP");
     90 	else if (getenv("TEMP"))
     91 		tempdir = getenv("TEMP");
     92 	else if (getenv("TMPDIR"))
     93 		tempdir = getenv("TMPDIR");
     94 	assert(tempdir);
     95 	i = strlen(tempdir);
     96 	for (; i > 0 && tempdir[i-1] == '/' || tempdir[i-1] == '\\'; i--)
     97 		tempdir[i-1] = '\0';
     98 	if (argc <= 1) {
     99 		help();
    100 		exit(0);
    101 	}
    102 	plist = append("-D__LCC__", 0);
    103 	initinputs();
    104 	if (getenv("LCCDIR"))
    105 		option(stringf("-lccdir=%s", getenv("LCCDIR")));
    106 	for (nf = 0, i = j = 1; i < argc; i++) {
    107 		if (strcmp(argv[i], "-o") == 0) {
    108 			if (++i < argc) {
    109 				if (suffix(argv[i], suffixes, 2) >= 0) {
    110 					error("-o would overwrite %s", argv[i]);
    111 					exit(8);
    112 				}
    113 				outfile = argv[i];
    114 				continue;
    115 			} else {
    116 				error("unrecognized option `%s'", argv[i-1]);
    117 				exit(8);
    118 			}
    119 		} else if (strcmp(argv[i], "-target") == 0) {
    120 			if (argv[i+1] && *argv[i+1] != '-')
    121 				i++;
    122 			continue;
    123 		} else if (*argv[i] == '-' && argv[i][1] != 'l') {
    124 			opt(argv[i]);
    125 			continue;
    126 		} else if (*argv[i] != '-' && suffix(argv[i], suffixes, 3) >= 0)
    127 			nf++;
    128 		argv[j++] = argv[i];
    129 	}
    130 	if ((cflag || Sflag) && outfile && nf != 1) {
    131 		fprintf(stderr, "%s: -o %s ignored\n", progname, outfile);
    132 		outfile = 0;
    133 	}
    134 	argv[j] = 0;
    135 	for (i = 0; include[i]; i++)
    136 		plist = append(include[i], plist);
    137 	if (ilist) {
    138 		List b = ilist;
    139 		do {
    140 			b = b->link;
    141 			plist = append(b->str, plist);
    142 		} while (b != ilist);
    143 	}
    144 	ilist = 0;
    145 	for (i = 1; argv[i]; i++)
    146 		if (*argv[i] == '-')
    147 			opt(argv[i]);
    148 		else {
    149 			char *name = exists(argv[i]);
    150 			if (name) {
    151 				if (strcmp(name, argv[i]) != 0
    152 				|| nf > 1 && suffix(name, suffixes, 3) >= 0)
    153 					fprintf(stderr, "%s:\n", name);
    154 				filename(name, 0);
    155 			} else
    156 				error("can't find `%s'", argv[i]);
    157 		}
    158 	if (errcnt == 0 && !Eflag && !Sflag && !cflag && llist[1]) {
    159 		compose(ld, llist[0], llist[1],
    160 			append(outfile ? outfile : concat("a", first(suffixes[4])), 0));
    161 		if (callsys(av))
    162 			errcnt++;
    163 	}
    164 	rm(rmlist);	
    165 	return errcnt ? EXIT_FAILURE : EXIT_SUCCESS;
    166 }
    167 
    168 /* alloc - allocate n bytes or die */
    169 static void *alloc(int n) {
    170 	static char *avail, *limit;
    171 	
    172 	n = (n + sizeof(char *) - 1)&~(sizeof(char *) - 1);
    173 	if (n >= limit - avail) {
    174 		avail = malloc(n + 4*1024);
    175 		assert(avail);
    176 		limit = avail + n + 4*1024;
    177 	}
    178 	avail += n;
    179 	return avail - n;
    180 }
    181 
    182 /* append - append a node with string str onto list, return new list */	
    183 static List append(char *str, List list) {
    184 	List p = alloc(sizeof *p);
    185 
    186 	p->str = str;
    187 	if (list) {
    188 		p->link = list->link;
    189 		list->link = p;
    190 	} else
    191 		p->link = p;
    192 	return p;
    193 }
    194 
    195 /* basepath - return base name for name, e.g. /usr/drh/foo.c => foo */
    196 char *basepath(char *name) {
    197 	char *s, *b, *t = 0;
    198 
    199 	for (b = s = name; *s; s++)
    200 		if (*s == '/' || *s == '\\') {
    201 			b = s + 1;
    202 			t = 0;
    203 		} else if (*s == '.')
    204 			t = s;
    205 	s = strsave(b);
    206 	if (t)
    207 		s[t-b] = 0;
    208 	return s;
    209 }
    210 
    211 #ifdef WIN32
    212 #include <process.h>
    213 #else
    214 #define _P_WAIT 0
    215 extern int fork(void);
    216 extern int wait(int *);
    217 extern void execv(const char *, char *[]);
    218 
    219 static int _spawnvp(int mode, const char *cmdname, char *argv[]) {
    220 	int pid, n, status;
    221 
    222 	switch (pid = fork()) {
    223 	case -1:
    224 		fprintf(stderr, "%s: no more processes\n", progname);
    225 		return 100;
    226 	case 0:
    227 		// TTimo removing hardcoded paths, searching in $PATH
    228 		execvp(cmdname, argv);
    229 		fprintf(stderr, "%s: ", progname);
    230 		perror(cmdname);
    231 		fflush(stdout);
    232 		exit(100);
    233 	}
    234 	while ((n = wait(&status)) != pid && n != -1)
    235 		;
    236 	if (n == -1)
    237 		status = -1;
    238 	if (status&0377) {
    239 		fprintf(stderr, "%s: fatal error in %s\n", progname, cmdname);
    240 		status |= 0400;
    241 	}
    242 	return (status>>8)&0377;
    243 }
    244 #endif
    245 
    246 /* callsys - execute the command described by av[0...], return status */
    247 static int callsys(char **av) {
    248 	int i, status = 0;
    249 	static char **argv;
    250 	static int argc;
    251 
    252 	for (i = 0; av[i] != NULL; i++)
    253 		;
    254 	if (i + 1 > argc) {
    255 		argc = i + 1;
    256 		if (argv == NULL)
    257 			argv = malloc(argc*sizeof *argv);
    258 		else
    259 			argv = realloc(argv, argc*sizeof *argv);
    260 		assert(argv);
    261 	}
    262 	for (i = 0; status == 0 && av[i] != NULL; ) {
    263 		int j = 0;
    264 		char *s;
    265 		for ( ; av[i] != NULL && (s = strchr(av[i], '\n')) == NULL; i++)
    266 			argv[j++] = av[i];
    267 		if (s != NULL) {
    268 			if (s > av[i])
    269 				argv[j++] = stringf("%.*s", s - av[i], av[i]);
    270 			if (s[1] != '\0')
    271 				av[i] = s + 1;
    272 			else
    273 				i++;
    274 		}
    275 		argv[j] = NULL;
    276 		if (verbose > 0) {
    277 			int k;
    278 			fprintf(stderr, "%s", argv[0]);
    279 			for (k = 1; argv[k] != NULL; k++)
    280 				fprintf(stderr, " %s", argv[k]);
    281 			fprintf(stderr, "\n");
    282 		}
    283 		if (verbose < 2)
    284 			status = _spawnvp(_P_WAIT, argv[0], argv);
    285 		if (status == -1) {
    286 			fprintf(stderr, "%s: ", progname);
    287 			perror(argv[0]);
    288 		}
    289 	}
    290 	return status;
    291 }
    292 
    293 /* concat - return concatenation of strings s1 and s2 */
    294 char *concat(char *s1, char *s2) {
    295 	int n = strlen(s1);
    296 	char *s = alloc(n + strlen(s2) + 1);
    297 
    298 	strcpy(s, s1);
    299 	strcpy(s + n, s2);
    300 	return s;
    301 }
    302 
    303 /* compile - compile src into dst, return status */
    304 static int compile(char *src, char *dst) {
    305 	compose(com, clist, append(src, 0), append(dst, 0));
    306 	return callsys(av);
    307 }
    308 
    309 /* compose - compose cmd into av substituting a, b, c for $1, $2, $3, resp. */
    310 static void compose(char *cmd[], List a, List b, List c) {
    311 	int i, j;
    312 	List lists[3];
    313 
    314 	lists[0] = a;
    315 	lists[1] = b;
    316 	lists[2] = c;
    317 	for (i = j = 0; cmd[i]; i++) {
    318 		char *s = strchr(cmd[i], '$');
    319 		if (s && isdigit(s[1])) {
    320 			int k = s[1] - '0';
    321 			assert(k >=1 && k <= 3);
    322 			if (b = lists[k-1]) {
    323 				b = b->link;
    324 				av[j] = alloc(strlen(cmd[i]) + strlen(b->str) - 1);
    325 				strncpy(av[j], cmd[i], s - cmd[i]);
    326 				av[j][s-cmd[i]] = '\0';
    327 				strcat(av[j], b->str);
    328 				strcat(av[j++], s + 2);
    329 				while (b != lists[k-1]) {
    330 					b = b->link;
    331 					assert(j < ac);
    332 					av[j++] = b->str;
    333 				};
    334 			}
    335 		} else if (*cmd[i]) {
    336 			assert(j < ac);
    337 			av[j++] = cmd[i];
    338 		}
    339 	}
    340 	av[j] = NULL;
    341 }
    342 
    343 /* error - issue error msg according to fmt, bump error count */
    344 static void error(char *fmt, char *msg) {
    345 	fprintf(stderr, "%s: ", progname);
    346 	fprintf(stderr, fmt, msg);
    347 	fprintf(stderr, "\n");
    348 	errcnt++;
    349 }
    350 
    351 /* exists - if `name' readable return its path name or return null */
    352 static char *exists(char *name) {
    353 	List b;
    354 
    355 	if ( (name[0] == '/' || name[0] == '\\' || name[2] == ':')
    356 	&& access(name, 4) == 0)
    357 		return name;
    358 	if (!(name[0] == '/' || name[0] == '\\' || name[2] == ':')
    359 	&& (b = lccinputs))		
    360 		do {
    361 			b = b->link;
    362 			if (b->str[0]) {
    363 				char buf[1024];
    364 				sprintf(buf, "%s/%s", b->str, name);
    365 				if (access(buf, 4) == 0)
    366 					return strsave(buf);
    367 			} else if (access(name, 4) == 0)
    368 				return name;
    369 		} while (b != lccinputs);
    370 	if (verbose > 1)
    371 		return name;
    372 	return 0;
    373 }
    374 
    375 /* first - return first component in semicolon separated list */
    376 static char *first(char *list) {
    377 	char *s = strchr(list, ';');
    378 
    379 	if (s) {
    380 		char buf[1024];
    381 		strncpy(buf, list, s-list);
    382 		buf[s-list] = '\0';
    383 		return strsave(buf);
    384 	} else
    385 		return list;
    386 }
    387 
    388 /* filename - process file name argument `name', return status */
    389 static int filename(char *name, char *base) {
    390 	int status = 0;
    391 	static char *stemp, *itemp;
    392 
    393 	if (base == 0)
    394 		base = basepath(name);
    395 	switch (suffix(name, suffixes, 4)) {
    396 	case 0:	/* C source files */
    397 		compose(cpp, plist, append(name, 0), 0);
    398 		if (Eflag) {
    399 			status = callsys(av);
    400 			break;
    401 		}
    402 		if (itemp == NULL)
    403 			itemp = tempname(first(suffixes[1]));
    404 		compose(cpp, plist, append(name, 0), append(itemp, 0));
    405 		status = callsys(av);
    406 		if (status == 0)
    407 			return filename(itemp, base);
    408 		break;
    409 	case 1:	/* preprocessed source files */
    410 		if (Eflag)
    411 			break;
    412 		if (Sflag)
    413 			status = compile(name, outfile ? outfile : concat(base, first(suffixes[2])));
    414 		else if ((status = compile(name, stemp?stemp:(stemp=tempname(first(suffixes[2]))))) == 0)
    415 			return filename(stemp, base);
    416 		break;
    417 	case 2:	/* assembly language files */
    418 		if (Eflag)
    419 			break;
    420 		if (!Sflag) {
    421 			char *ofile;
    422 			if (cflag && outfile)
    423 				ofile = outfile;
    424 			else if (cflag)
    425 				ofile = concat(base, first(suffixes[3]));
    426 			else
    427 				ofile = tempname(first(suffixes[3]));
    428 			compose(as, alist, append(name, 0), append(ofile, 0));
    429 			status = callsys(av);
    430 			if (!find(ofile, llist[1]))
    431 				llist[1] = append(ofile, llist[1]);
    432 		}
    433 		break;
    434 	case 3:	/* object files */
    435 		if (!find(name, llist[1]))
    436 			llist[1] = append(name, llist[1]);
    437 		break;
    438 	default:
    439 		if (Eflag) {
    440 			compose(cpp, plist, append(name, 0), 0);
    441 			status = callsys(av);
    442 		}
    443 		llist[1] = append(name, llist[1]);
    444 		break;
    445 	}
    446 	if (status)
    447 		errcnt++;
    448 	return status;
    449 }
    450 
    451 /* find - find 1st occurrence of str in list, return list node or 0 */
    452 static List find(char *str, List list) {
    453 	List b;
    454 	
    455 	if (b = list)
    456 		do {
    457 			if (strcmp(str, b->str) == 0)
    458 				return b;
    459 		} while ((b = b->link) != list);
    460 	return 0;
    461 }
    462 
    463 /* help - print help message */
    464 static void help(void) {
    465 	static char *msgs[] = {
    466 "", " [ option | file ]...\n",
    467 "	except for -l, options are processed left-to-right before files\n",
    468 "	unrecognized options are taken to be linker options\n",
    469 "-A	warn about nonANSI usage; 2nd -A warns more\n",
    470 "-b	emit expression-level profiling code; see bprint(1)\n",
    471 #ifdef sparc
    472 "-Bstatic -Bdynamic	specify static or dynamic libraries\n",
    473 #endif
    474 "-Bdir/	use the compiler named `dir/rcc'\n",
    475 "-c	compile only\n",
    476 "-dn	set switch statement density to `n'\n",
    477 "-Dname -Dname=def	define the preprocessor symbol `name'\n",
    478 "-E	run only the preprocessor on the named C programs and unsuffixed files\n",
    479 "-g	produce symbol table information for debuggers\n",
    480 "-help or -?	print this message\n",
    481 "-Idir	add `dir' to the beginning of the list of #include directories\n",	
    482 "-lx	search library `x'\n",
    483 "-N	do not search the standard directories for #include files\n",
    484 "-n	emit code to check for dereferencing zero pointers\n",
    485 "-O	is ignored\n",
    486 "-o file	leave the output in `file'\n",
    487 "-P	print ANSI-style declarations for globals\n",
    488 "-p -pg	emit profiling code; see prof(1) and gprof(1)\n",
    489 "-S	compile to assembly language\n",
    490 #ifdef linux
    491 "-static	specify static libraries (default is dynamic)\n",
    492 #endif
    493 "-t -tname	emit function tracing calls to printf or to `name'\n",
    494 "-target name	is ignored\n",
    495 "-tempdir=dir	place temporary files in `dir/'", "\n"
    496 "-Uname	undefine the preprocessor symbol `name'\n",
    497 "-v	show commands as they are executed; 2nd -v suppresses execution\n",
    498 "-w	suppress warnings\n",
    499 "-Woarg	specify system-specific `arg'\n",
    500 "-W[pfal]arg	pass `arg' to the preprocessor, compiler, assembler, or linker\n",
    501 	0 };
    502 	int i;
    503 	char *s;
    504 
    505 	msgs[0] = progname;
    506 	for (i = 0; msgs[i]; i++) {
    507 		fprintf(stderr, "%s", msgs[i]);
    508 		if (strncmp("-tempdir", msgs[i], 8) == 0 && tempdir)
    509 			fprintf(stderr, "; default=%s", tempdir);
    510 	}
    511 #define xx(v) if (s = getenv(#v)) fprintf(stderr, #v "=%s\n", s)
    512 	xx(LCCINPUTS);
    513 	xx(LCCDIR);
    514 #ifdef WIN32
    515 	xx(include);
    516 	xx(lib);
    517 #endif
    518 #undef xx
    519 }
    520 
    521 /* initinputs - if LCCINPUTS or include is defined, use them to initialize various lists */
    522 static void initinputs(void) {
    523 	char *s = getenv("LCCINPUTS");
    524 	List list, b;
    525 
    526 	if (s == 0 || (s = inputs)[0] == 0)
    527 		s = ".";
    528 	if (s) {
    529 		lccinputs = path2list(s);
    530 		if (b = lccinputs)
    531 			do {
    532 				b = b->link;
    533 				if (strcmp(b->str, ".") != 0) {
    534 					ilist = append(concat("-I", b->str), ilist);
    535 					if (strstr(com[1], "win32") == NULL)
    536 						llist[0] = append(concat("-L", b->str), llist[0]);
    537 				} else
    538 					b->str = "";
    539 			} while (b != lccinputs);
    540 	}
    541 #ifdef WIN32
    542 	if (list = b = path2list(getenv("include")))
    543 		do {
    544 			b = b->link;
    545 			ilist = append(stringf("-I\"%s\"", b->str), ilist);
    546 		} while (b != list);
    547 #endif
    548 }
    549 
    550 /* interrupt - catch interrupt signals */
    551 static void interrupt(int n) {
    552 	rm(rmlist);
    553 	exit(n = 100);
    554 }
    555 
    556 /* opt - process option in arg */
    557 static void opt(char *arg) {
    558 	switch (arg[1]) {	/* multi-character options */
    559 	case 'W':	/* -Wxarg */
    560 		if (arg[2] && arg[3])
    561 			switch (arg[2]) {
    562 			case 'o':
    563 				if (option(&arg[3]))
    564 					return;
    565 				break;
    566 			case 'p':
    567 				plist = append(&arg[3], plist);
    568 				return;
    569 			case 'f':
    570 				if (strcmp(&arg[3], "-C") || option("-b")) {
    571 					clist = append(&arg[3], clist);
    572 					return;
    573 				}
    574 				break; /* and fall thru */
    575 			case 'a':
    576 				alist = append(&arg[3], alist);
    577 				return;
    578 			case 'l':
    579 				llist[0] = append(&arg[3], llist[0]);
    580 				return;
    581 			}
    582 		fprintf(stderr, "%s: %s ignored\n", progname, arg);
    583 		return;
    584 	case 'd':	/* -dn */
    585 		arg[1] = 's';
    586 		clist = append(arg, clist);
    587 		return;
    588 	case 't':	/* -t -tname -tempdir=dir */
    589 		if (strncmp(arg, "-tempdir=", 9) == 0)
    590 			tempdir = arg + 9;
    591 		else
    592 			clist = append(arg, clist);
    593 		return;
    594 	case 'p':	/* -p -pg */
    595 		if (option(arg))
    596 			clist = append(arg, clist);
    597 		else
    598 			fprintf(stderr, "%s: %s ignored\n", progname, arg);
    599 		return;
    600 	case 'D':	/* -Dname -Dname=def */
    601 	case 'U':	/* -Uname */
    602 	case 'I':	/* -Idir */
    603 		plist = append(arg, plist);
    604 		return;
    605 	case 'B':	/* -Bdir -Bstatic -Bdynamic */
    606 #ifdef sparc
    607 		if (strcmp(arg, "-Bstatic") == 0 || strcmp(arg, "-Bdynamic") == 0)
    608 			llist[1] = append(arg, llist[1]);
    609 		else
    610 #endif	
    611 		{
    612 		static char *path;
    613 		if (path)
    614 			error("-B overwrites earlier option", 0);
    615 		path = arg + 2;
    616 		if (strstr(com[1], "win32") != NULL)
    617 			com[0] = concat(replace(path, '/', '\\'), concat("rcc", first(suffixes[4])));
    618 		else
    619 			com[0] = concat(path, "rcc");
    620 		if (path[0] == 0)
    621 			error("missing directory in -B option", 0);
    622 		}
    623 		return;
    624 	case 'h':
    625 		if (strcmp(arg, "-help") == 0) {
    626 			static int printed = 0;
    627 	case '?':
    628 			if (!printed)
    629 				help();
    630 			printed = 1;
    631 			return;
    632 		}
    633 #ifdef linux
    634 	case 's':
    635 		if (strcmp(arg,"-static") == 0) {
    636 			if (!option(arg))
    637 				fprintf(stderr, "%s: %s ignored\n", progname, arg);
    638 			return;
    639 		}
    640 #endif         
    641 	}
    642 	if (arg[2] == 0)
    643 		switch (arg[1]) {	/* single-character options */
    644 		case 'S':
    645 			Sflag++;
    646 			return;
    647 		case 'O':
    648 			fprintf(stderr, "%s: %s ignored\n", progname, arg);
    649 			return;
    650 		case 'A': case 'n': case 'w': case 'P':
    651 			clist = append(arg, clist);
    652 			return;
    653 		case 'g': case 'b':
    654 			if (option(arg))
    655 				clist = append(arg[1] == 'g' ? "-g2" : arg, clist);
    656 			else
    657 				fprintf(stderr, "%s: %s ignored\n", progname, arg);
    658 			return;
    659 		case 'G':
    660 			if (option(arg)) {
    661 				clist = append("-g3", clist);
    662 				llist[0] = append("-N", llist[0]);
    663 			} else
    664 				fprintf(stderr, "%s: %s ignored\n", progname, arg);
    665 			return;
    666 		case 'E':
    667 			Eflag++;
    668 			return;
    669 		case 'c':
    670 			cflag++;
    671 			return;
    672 		case 'N':
    673 			if (strcmp(basepath(cpp[0]), "gcc-cpp") == 0)
    674 				plist = append("-nostdinc", plist);
    675 			include[0] = 0;
    676 			ilist = 0;
    677 			return;
    678 		case 'v':
    679 			if (verbose++ == 0) {
    680 				if (strcmp(basepath(cpp[0]), "gcc-cpp") == 0)
    681 					plist = append(arg, plist);
    682 				clist = append(arg, clist);
    683 				fprintf(stderr, "%s %s\n", progname, rcsid);
    684 			}
    685 			return;
    686 		}
    687 	if (cflag || Sflag || Eflag)
    688 		fprintf(stderr, "%s: %s ignored\n", progname, arg);
    689 	else
    690 		llist[1] = append(arg, llist[1]);
    691 }
    692 
    693 /* path2list - convert a colon- or semicolon-separated list to a list */
    694 static List path2list(const char *path) {
    695 	List list = NULL;
    696 	char sep = ':';
    697 
    698 	if (path == NULL)
    699 		return NULL;
    700 	if (strchr(path, ';'))
    701 		sep = ';';
    702 	while (*path) {
    703 		char *p, buf[512];
    704 		if (p = strchr(path, sep)) {
    705 			assert(p - path < sizeof buf);
    706 			strncpy(buf, path, p - path);
    707 			buf[p-path] = '\0';
    708 		} else {
    709 			assert(strlen(path) < sizeof buf);
    710 			strcpy(buf, path);
    711 		}
    712 		if (!find(buf, list))
    713 			list = append(strsave(buf), list);
    714 		if (p == 0)
    715 			break;
    716 		path = p + 1;
    717 	}
    718 	return list;
    719 }
    720 
    721 /* replace - copy str, then replace occurrences of from with to, return the copy */
    722 char *replace(const char *str, int from, int to) {
    723 	char *s = strsave(str), *p = s;
    724 
    725 	for ( ; (p = strchr(p, from)) != NULL; p++)
    726 		*p = to;
    727 	return s;
    728 }
    729 
    730 /* rm - remove files in list */
    731 static void rm(List list) {
    732 	if (list) {
    733 		List b = list;
    734 		if (verbose)
    735 			fprintf(stderr, "rm");
    736 		do {
    737 			if (verbose)
    738 				fprintf(stderr, " %s", b->str);
    739 			if (verbose < 2)
    740 				remove(b->str);
    741 		} while ((b = b->link) != list);
    742 		if (verbose)
    743 			fprintf(stderr, "\n");
    744 	}
    745 }
    746 
    747 /* strsave - return a saved copy of string str */
    748 char *strsave(const char *str) {
    749 	return strcpy(alloc(strlen(str)+1), str);
    750 }
    751 
    752 /* stringf - format and return a string */
    753 char *stringf(const char *fmt, ...) {
    754 	char buf[1024];
    755 	va_list ap;
    756 	int n;
    757 
    758 	va_start(ap, fmt);
    759 	n = vsprintf(buf, fmt, ap);
    760 	va_end(ap);
    761 	return strsave(buf);
    762 }
    763 
    764 /* suffix - if one of tails[0..n-1] holds a proper suffix of name, return its index */
    765 int suffix(char *name, char *tails[], int n) {
    766 	int i, len = strlen(name);
    767 
    768 	for (i = 0; i < n; i++) {
    769 		char *s = tails[i], *t;
    770 		for ( ; t = strchr(s, ';'); s = t + 1) {
    771 			int m = t - s;
    772 			if (len > m && strncmp(&name[len-m], s, m) == 0)
    773 				return i;
    774 		}
    775 		if (*s) {
    776 			int m = strlen(s);
    777 			if (len > m && strncmp(&name[len-m], s, m) == 0)
    778 				return i;
    779 		}
    780 	}
    781 	return -1;
    782 }
    783 
    784 /* tempname - generate a temporary file name in tempdir with given suffix */
    785 char *tempname(char *suffix) {
    786 	static int n;
    787 	char *name = stringf("%s/lcc%d%d%s", tempdir, getpid(), n++, suffix);
    788 
    789 	if (strstr(com[1], "win32") != NULL)
    790 		name = replace(name, '/', '\\');
    791 	rmlist = append(name, rmlist);
    792 	return name;
    793 }