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