/*
 * lcc [ option ]... [ file | -llib ]...
 * front end for the ANSI C compiler
 */
static char rcsid[] = "$Id: lcc.c,v 1.3 2002/04/21 13:33:40 peter Exp $";

#include <lcc-conf.h>

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <signal.h>

#include <cpyright.h>

#ifndef TEMPDIR
#define TEMPDIR "/tmp"
#endif

#ifdef NDEBUG
#define debug(x) (void)0
#else
static int dflag = 0;
#define debug(x) (void)(dflag&&((x),0))
#endif

#ifdef __riscos__
#include <unixlib/local.h>
#endif

typedef struct list *List;
struct list {		/* circular list nodes: */
	char *str;		/* option or file name */
	List link;		/* next list element */
};

static void *alloc(int);
static List append(char *,List);
extern char *basepath(char *);
static int callsys(char *[]);
extern char *concat(char *, char *);
static int compile(char *, char *);
static void compose(char *[], List, List, List);
static void copyright(void);
static void error(char *, char *);
static char *exists(char *);
static char *first(char *);
static int filename(char *, char *);
static List find(char *, List);
static void help(void);
static void initinputs(void);
static void interrupt(int);
static void opt(char *);
static List path2list(const char *);
extern int main(int, char **);
extern char *replace(const char *, int, int);
static void rm(List);
static size_t split(const char *, char ***, const char *);
static void split_Wx_args(int *pargc, char ***pargv);
extern char *strsave(const char *);
extern char *stringf(const char *, ...);
extern int suffix(char *, char *[], int);
extern char *tempname(char *);

extern int access(char *, int);
extern int getpid(void);

extern char *cpp[], *include[], *com[], *as[],*ld[], inputs[], *suffixes[];
extern int option(char *);

static int errcnt;		/* number of errors */
static int Eflag;		/* -E specified */
static int Sflag;		/* -S specified */
static int cflag;		/* -c specified */
static int verbose;		/* incremented for each -v */
static List llist[2];		/* loader files, flags */
static List alist;		/* assembler flags */
static List clist;		/* compiler flags */
static List plist;		/* preprocessor flags */
static List ilist;		/* list of additional includes from LCCINPUTS */
static List rmlist;		/* list of files to remove */
static char *outfile;		/* ld output file or -[cS] object file */
static int ac;			/* argument count */
static char **av;		/* argument vector */
char *tempdir = TEMPDIR;	/* directory for temporary files */
static char *progname;
static List lccinputs;		/* list of input directories */

#ifdef __riscos__
#include <kernel.h>
#include <swis.h>
#endif

main(int argc, char **argv) {
	int i, j, nf;

	progname = argv[0];

	for (i = 1; i < argc; ++i)	/* convert GNU/POSIX --option to -option */
	{
	    if ((argv[i] != (char*)NULL) && (strncmp(argv[i],"--",2) == 0))
		++argv[i];
	}

	for (i = 1; i < argc; ++i)
	{	/* convert GNU/POSIX -W<letter-or-digit>,options to
		   old-lcc-style -W<letter-or-digit>options, and POSIX -W0
		   to lcc -Wf */
		if (argv[i] != (char*)NULL)
		{
			if ((argv[i][0] == '-') &&
			    (argv[i][1] == 'W') &&
			    isalnum(argv[i][2]))
			{
				if (argv[i][2] == '0')
					argv[i][2] = 'f'; /* POSIX -W0 == lcc -Wf */
				else if (argv[i][2] == 'd') {/* debug -W splitting*/
					dflag = 1;
					argv[i] = (char*)NULL;
				}
				if ((argv[i] != (char*)NULL) && (argv[i][3] == ','))
					memmove((void*)&argv[i][3],
						(void*)&argv[i][4],
						strlen(argv[i])-3);
			}
		}
	}

	split_Wx_args(&argc, &argv);
	ac = argc + 100;
	av = alloc(ac*sizeof(char *));
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, interrupt);
	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
		signal(SIGTERM, interrupt);
#ifdef SIGHUP
	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
		signal(SIGHUP, interrupt);
#endif
	if (getenv("TMP"))
		tempdir = getenv("TMP");
	else if (getenv("TEMP"))
		tempdir = getenv("TEMP");
	else if (getenv("TMPDIR"))
		tempdir = getenv("TMPDIR");
	assert(tempdir);
	i = strlen(tempdir);
	for (; i > 0 && tempdir[i-1] == '/' || tempdir[i-1] == '\\'; i--)
		tempdir[i-1] = '\0';
	if (argc <= 1) {
		help();
		exit(0);
	}
	plist = append("-D__LCC__", 0);

#ifdef __riscos__
	_swix(OS_File, _IN(0) | _IN(1) | _IN(4), 8, "<Wimp$ScrapDir>.i", 0);
	_swix(OS_File, _IN(0) | _IN(1) | _IN(4), 8, "<Wimp$ScrapDir>.s", 0);

#endif

	/* [20-Mar-2001] Bug fix: preprocessor needs to know signedness
	   of char type, so that <limits.h> behaves correctly.

	   [21-Mar-2001] Bug fix: preprocessor needs to know size of
	   wchar_t type, so <stddef.h> behaves correctly. */

	for (i = 1; i < argc; ++i)	/* check for -Wf-unsigned_char=x; */
	{
		if (argv[i] != (char*)NULL)
		{
			if (strncmp(argv[i],"-Wf-unsigned_char=",18) == 0)
			{
				/* WARNING: this code must work like
				type_init() in ../src/types.c! */
				if (argv[i][18] != '0')
				{
				    plist = append("-D__CHAR_UNSIGNED__", plist);
				    plist = append("-U_CHAR_IS_SIGNED",   plist);
				}
			}
			else if (strcmp(argv[i],"-Wf-wchar_t=unsigned_char") == 0)
				plist = append("-D_WCHAR_T_SIZE=1", plist);
			else if (strcmp(argv[i],"-Wf-wchar_t=unsigned_short") == 0)
				plist = append("-D_WCHAR_T_SIZE=2", plist);
			else if (strcmp(argv[i],"-Wf-wchar_t=unsigned_int") == 0)
				plist = append("-D_WCHAR_T_SIZE=4", plist);
		}
	}

	initinputs();

	/* Predefine compiler components set by environment variables;
	   command-line options may override them later. We need to
	   set LCCDIR first, because it changes the installation
	   directory, affecting all of the compiler component
	   locations. */
	if (getenv("LCCDIR"))	option(stringf("-lccdir=%s",	getenv("LCCDIR")));
	if (getenv("LCCAS"))	option(stringf("-as=%s",	getenv("LCCAS")));
	if (getenv("LCCCPP"))	option(stringf("-cpp=%s",	getenv("LCCCPP")));
	if (getenv("LCCLD"))	option(stringf("-ld=%s",	getenv("LCCLD")));
	if (getenv("LCCRCC"))	option(stringf("-rcc=%s",	getenv("LCCRCC")));

	for (nf = 0, i = j = 1; i < argc; i++) {
		if (argv[i] == (char*)NULL)
			continue;
		else if (strcmp(argv[i], "-o") == 0) {
			if (++i < argc) {
				if (suffix(argv[i], suffixes, 2) >= 0) {
					error("-o would overwrite %s", argv[i]);
					exit(8);
				}
				outfile = argv[i];
				continue;
			} else {
				error("unrecognized option `%s'", argv[i-1]);
				exit(8);
			}
		} else if (strcmp(argv[i], "-target") == 0) {
			if (argv[i+1] && *argv[i+1] != '-')
				i++;
			continue;
		} else if (*argv[i] == '-' && argv[i][1] != 'l') {
			opt(argv[i]);
			continue;
		} else if (*argv[i] != '-' && suffix(argv[i], suffixes, 3) >= 0)
			nf++;
		argv[j++] = argv[i];
	}
	if ((cflag || Sflag) && outfile && nf != 1) {
		fprintf(stderr, "%s: -o %s ignored\n", progname, outfile);
		outfile = 0;
	}
	argv[j] = 0;
	for (i = 0; include[i]; i++)
		plist = append(include[i], plist);
	if (ilist) {
		List b = ilist;
		do {
			b = b->link;
			plist = append(b->str, plist);
		} while (b != ilist);
	}
	ilist = 0;
	for (i = 1; argv[i]; i++)
		if (*argv[i] == '-')
			opt(argv[i]);
		else {
			char *name = exists(argv[i]);
			if (name) {
				if (strcmp(name, argv[i]) != 0
				|| nf > 1 && suffix(name, suffixes, 3) >= 0)
					fprintf(stderr, "%s:\n", name);
				filename(name, 0);
			} else
				error("can't find `%s'", argv[i]);
		}
	if (errcnt == 0 && !Eflag && !Sflag && !cflag && llist[1]) {
		compose(ld, llist[0], llist[1],
#ifdef __riscos__
			append(outfile ? outfile : concat("!RunImage", first(suffixes[4])), 0));
#else
			append(outfile ? outfile : concat("a", first(suffixes[4])), 0));
#endif
		if (callsys(av))
			errcnt++;
	}
	rm(rmlist);
	free((void*)argv);		/* was allocated in split_Wx_args() */
	return errcnt ? EXIT_FAILURE : EXIT_SUCCESS;
}

/* alloc - allocate n bytes or die */
static void *alloc(int n) {
	static char *avail, *limit;

	n = (n + sizeof(char *) - 1)&~(sizeof(char *) - 1);
	if (n >= limit - avail) {
		avail = malloc(n + 4*1024);
		assert(avail);
		limit = avail + n + 4*1024;
	}
	avail += n;
	return avail - n;
}

/* append - append a node with string str onto list, return new list */
static List append(char *str, List list) {
	List p = alloc(sizeof *p);

	p->str = str;
	if (list) {
		p->link = list->link;
		list->link = p;
	} else
		p->link = p;
	return p;
}

/* basepath - return base name for name, e.g. /usr/drh/foo.c => foo */
char *basepath(char *name) {
	char *s, *b, *t = 0;

	for (b = s = name; *s; s++)
		if (*s == '/' || *s == '\\') {
			b = s + 1;
			t = 0;
		} else if (*s == '.')
			t = s;
	s = strsave(b);
	if (t)
		s[t-b] = 0;
	return s;
}

#ifdef WIN32
#include <process.h>
#else
#define _P_WAIT 0
#ifdef __riscos__
#define fork() vfork()
#else
extern int fork(void);
#endif
extern int wait(int *);
extern void execv(const char *, char *[]);

static int _spawnvp(int mode, const char *cmdname, char *argv[]) {
	int pid, n, status;

	switch (pid = fork()) {
	case -1:
		fprintf(stderr, "%s: no more processes\n", progname);
		return 100;
	case 0:
		execv(cmdname, argv);
		fprintf(stderr, "%s: ", progname);
		perror(cmdname);
		fflush(stdout);
		exit(100);
	}
	while ((n = wait(&status)) != pid && n != -1)
		;
	if (n == -1)
		status = -1;
	if (status&0377) {
		fprintf(stderr, "%s: fatal error in %s\n", progname, cmdname);
		status |= 0400;
	}
	return (status>>8)&0377;
}
#endif

/* callsys - execute the command described by av[0...], return status */
static int callsys(char **av) {
	int i, status = 0;
	static char **argv;
	static int argc;

	for (i = 0; av[i] != NULL; i++)
		;
	if (i + 1 > argc) {
		argc = i + 1;
		if (argv == NULL)
			argv = malloc(argc*sizeof *argv);
		else
			argv = realloc(argv, argc*sizeof *argv);
		assert(argv);
	}
	for (i = 0; status == 0 && av[i] != NULL; ) {
		int j = 0;
		char *s;
		for ( ; av[i] != NULL && (s = strchr(av[i], '\n')) == NULL; i++)
			argv[j++] = av[i];
		if (s != NULL) {
			if (s > av[i])
				argv[j++] = stringf("%.*s", s - av[i], av[i]);
			if (s[1] != '\0')
				av[i] = s + 1;
			else
				i++;
		}
		argv[j] = NULL;
		if (verbose > 0) {
			int k;
			fprintf(stderr, "%s", argv[0]);
			for (k = 1; argv[k] != NULL; k++)
				fprintf(stderr, " %s", argv[k]);
			fprintf(stderr, "\n");
		}
		if (verbose < 2)
			status = _spawnvp(_P_WAIT, argv[0], argv);
		if (status == -1) {
			fprintf(stderr, "%s: ", progname);
			perror(argv[0]);
		}
	}
	return status;
}

/* concat - return concatenation of strings s1 and s2 */
char *concat(char *s1, char *s2) {
	int n = strlen(s1);
	char *s = alloc(n + strlen(s2) + 1);

	strcpy(s, s1);
	strcpy(s + n, s2);
	return s;
}

/* compile - compile src into dst, return status */
static int compile(char *src, char *dst) {
	compose(com, clist, append(src, 0), append(dst, 0));
	return callsys(av);
}

/* compose - compose cmd into av substituting a, b, c for $1, $2, $3, resp. */
static void compose(char *cmd[], List a, List b, List c) {
	int i, j;
	List lists[3];

	lists[0] = a;
	lists[1] = b;
	lists[2] = c;
	for (i = j = 0; cmd[i]; i++) {
		char *s = strchr(cmd[i], '$');
		if (s && isdigit(s[1])) {
			int k = s[1] - '0';
			assert(k >=1 && k <= 3);
			if (b = lists[k-1]) {
				b = b->link;
				av[j] = alloc(strlen(cmd[i]) + strlen(b->str) - 1);
				strncpy(av[j], cmd[i], s - cmd[i]);
				av[j][s-cmd[i]] = '\0';
				strcat(av[j], b->str);
				strcat(av[j++], s + 2);
				while (b != lists[k-1]) {
					b = b->link;
					assert(j < ac);
					av[j++] = b->str;
				};
			}
		} else if (*cmd[i]) {
			assert(j < ac);
			av[j++] = cmd[i];
		}
	}
	av[j] = NULL;
}

/* copyright -- display the copyright and a book pointer */
static void copyright(void)
{
	int k;

	for (k = 0; cpyright[k] != (char*)NULL; ++k)
		printf("%s\n", cpyright[k]);
	printf("\nFor further information, see also this book:\n\n"
	       "\tChris W. Fraser and David R. Hanson\n"
	       "\tA Retargetable C Compiler: Design and Implementation\n"
	       "\tAddison-Wesley, 1995\n"
	       "\tISBN 0-8053-1670-1.\n");
}


/* error - issue error msg according to fmt, bump error count */
static void error(char *fmt, char *msg) {
	fprintf(stderr, "%s: ", progname);
	fprintf(stderr, fmt, msg);
	fprintf(stderr, "\n");
	errcnt++;
}

/* exists - if `name' readable return its path name or return null */
static char *exists(char *name) {
	List b;

	if ( (name[0] == '/' || name[0] == '\\' || name[2] == ':')
	&& access(name, 4) == 0)
		return name;
	if (!(name[0] == '/' || name[0] == '\\' || name[2] == ':')
	&& (b = lccinputs))
		do {
			b = b->link;
			if (b->str[0]) {
				char buf[1024];
				sprintf(buf, "%s/%s", b->str, name);
				if (access(buf, 4) == 0)
					return strsave(buf);
			} else if (access(name, 4) == 0)
				return name;
		} while (b != lccinputs);
#ifdef __riscos__
       {
         int faccess;

         __riscosify_control = __RISCOSIFY_NO_PROCESS;
         faccess = access(name, 4);
         __riscosify_control = __RISCOSIFY_DONT_TRUNCATE;

         if (faccess == 0) return name;
       }
#endif
	if (verbose > 1)
		return name;
	return 0;
}

/* first - return first component in semicolon separated list */
static char *first(char *list) {
	char *s = strchr(list, ';');

	if (s) {
		char buf[1024];
		strncpy(buf, list, s-list);
		buf[s-list] = '\0';
		return strsave(buf);
	} else
		return list;
}

/* filename - process file name argument `name', return status */
static int filename(char *name, char *base) {
	int status = 0;
	static char *stemp, *itemp;

	if (base == 0)
		base = basepath(name);
	switch (suffix(name, suffixes, 4)) {
	case 0:	/* C source files */
		compose(cpp, plist, append(name, 0), 0);
		if (Eflag) {
			status = callsys(av);
			break;
		}
		if (itemp == NULL)
			itemp = tempname(first(suffixes[1]));
		compose(cpp, plist, append(name, 0), append(itemp, 0));
		status = callsys(av);
		if (status == 0)
			return filename(itemp, base);
		break;
	case 1:	/* preprocessed source files */
		if (Eflag)
			break;
		if (Sflag)
			status = compile(name, outfile ? outfile : concat(base, first(suffixes[2])));
		else if ((status = compile(name, stemp?stemp:(stemp=tempname(first(suffixes[2]))))) == 0)
			return filename(stemp, base);
		break;
	case 2:	/* assembly language files */
		if (Eflag)
			break;
		if (!Sflag) {
			char *ofile;
			if (cflag && outfile)
				ofile = outfile;
			else if (cflag)
				ofile = concat(base, first(suffixes[3]));
			else
				ofile = tempname(first(suffixes[3]));
			compose(as, alist, append(name, 0), append(ofile, 0));
			status = callsys(av);
			if (!find(ofile, llist[1]))
				llist[1] = append(ofile, llist[1]);
		}
		break;
	case 3:	/* object files */
		if (!find(name, llist[1]))
			llist[1] = append(name, llist[1]);
		break;
	default:
		if (Eflag) {
			compose(cpp, plist, append(name, 0), 0);
			status = callsys(av);
		}
		llist[1] = append(name, llist[1]);
		break;
	}
	if (status)
		errcnt++;
	return status;
}

/* find - find 1st occurrence of str in list, return list node or 0 */
static List find(char *str, List list) {
	List b;

	if (b = list)
		do {
			if (strcmp(str, b->str) == 0)
				return b;
		} while ((b = b->link) != list);
	return 0;
}

/* help - print help message */
static void help(void) {
	static char *msgs[] = {
"", " [ option | file ]...\n",
"	except for -l, options are processed left-to-right before files\n",
"	unrecognized options are taken to be linker options\n",
"-A	warn about nonANSI usage; 2nd -A warns more\n",
"-b	emit expression-level profiling code; see bprint(1)\n",
#ifdef sparc
"-Bstatic -Bdynamic	specify static or dynamic libraries\n",
#endif
#ifdef __riscos__
"-Bunix	link with UnixLib rather than SharedCLibrary\n",
#endif
"-Bdir/	use the compiler named `dir/rcc'\n",
"-c	compile only\n",
"-copyright	show copyright information and exit immediately\n",
"-dn	set switch statement density to `n'\n",
"-Dname -Dname=def	define the preprocessor symbol `name'\n",
"-E	run only the preprocessor on the named C programs and unsuffixed files\n",
"-g	produce symbol table information for debuggers\n",
"-help or -?	print this message\n",
"-Idir	add `dir' to the beginning of the list of #include directories\n",
"-Ldir	add `dir' to the library search path\n",
"-lx	search library `x'\n",
"-N	do not search the standard directories for #include files\n",
"-n	emit code to check for dereferencing zero pointers\n",
"-O	is ignored\n",
"-o file	leave the output in `file'\n",
"-P	print ANSI-style declarations for globals on stderr\n",
"-p -pg	emit profiling code; see prof(1) and gprof(1)\n",
"-S	compile to assembly language\n",
"-dynamic	specify dynamic (shared-library) linking\n",
"-static	specify static libraries (default is -dynamic)\n",
"-t -tname	emit function tracing calls to printf or to `name'\n",
"-target name	is ignored\n",
"-tempdir=dir	place temporary files in `dir/'", "\n"
"-Uname	undefine the preprocessor symbol `name'\n",
"-v	show commands as they are executed; 2nd -v suppresses execution\n",
"-version	show compiler version and exit immediately\n",
"-w	suppress warnings\n",
"-Woarg	specify system-specific `arg'\n",
"-W[pfal]arg	pass `arg' to the preprocessor, compiler, assembler, or linker\n",
	0 };
	int i;
	char *s;

	msgs[0] = progname;
	for (i = 0; msgs[i]; i++) {
		fprintf(stderr, "%s", msgs[i]);
		if (strncmp("-tempdir", msgs[i], 8) == 0 && tempdir)
			fprintf(stderr, "; default=%s", tempdir);
	}
#define xx(v) if (s = getenv(#v)) fprintf(stderr, #v "=%s\n", s)
	xx(LCCINPUTS);
	xx(LCCDIR);
#ifdef WIN32
	xx(include);
	xx(lib);
#endif
#undef xx
}

/* initinputs - if LCCINPUTS or include is defined, use them to initialize various lists */
static void initinputs(void) {
	char *s = getenv("LCCINPUTS");
#ifdef WIN32
	List list;
#endif
	List b;

	if (s == 0 && (s = inputs)[0] == 0)
		s = ".";
	if (s) {
		lccinputs = path2list(s);
		if (b = lccinputs)
			do {
				b = b->link;
				if (strcmp(b->str, ".") != 0) {
					ilist = append(concat("-I", b->str), ilist);
					if (strstr(com[1], "win32") == NULL)
						llist[0] = append(concat("-L", b->str), llist[0]);
				} else
					b->str = "";
			} while (b != lccinputs);
	}
#ifdef WIN32
	if (list = b = path2list(getenv("include")))
		do {
			b = b->link;
			ilist = append(stringf("-I\"%s\"", b->str), ilist);
		} while (b != list);
#endif
}

/* interrupt - catch interrupt signals */
static void interrupt(int n) {
	rm(rmlist);
	exit(n = 100);
}

/* opt - process option in arg */
static void opt(char *arg) {
	switch (arg[1]) {	/* multi-character options */
	case 'W':	/* -Wxarg */
		if (arg[2] && arg[3])
			switch (arg[2]) {
			case 'o':
				if (option(&arg[3]))
					return;
				break;
			case 'p':
				plist = append(&arg[3], plist);
				return;
			case 'f':
				if (strcmp(&arg[3], "-C") || option("-b")) {
					clist = append(&arg[3], clist);
					if (strncmp(&arg[3],"-target=",8) == 0)
					{
						/* If a non-assembly-code target is invoked,
						   or a non-native architecture/os is requested,
						   pretend -S was specified, for user convenience */
						if (strcmp(&arg[11],"bytecode") == 0)
							Sflag++;
						else if (strcmp(&arg[11],"null") == 0)
							Sflag++;
						else if (strcmp(&arg[11],"symbolic") == 0)
							Sflag++;
						else if (strncmp(&arg[11],"symbolic/",9) == 0)
							Sflag++;
						else if (strcmp(&arg[11],(ARCHITECTURE "/" OS)) != 0)
							Sflag++;
					}
					return;
				}
				break; /* and fall thru */
			case 'a':
				alist = append(&arg[3], alist);
				return;
			case 'l':
				llist[0] = append(&arg[3], llist[0]);
				return;
			}
		fprintf(stderr, "%s: %s ignored\n", progname, arg);
		return;
	case 'd':	/* -dn or -dynamic */
		if (strcmp(arg,"-dynamic") == 0) {
			if (!option(arg))
				fprintf(stderr, "%s: %s ignored\n", progname, arg);
		}
		else
		{
			arg[1] = 's';
			clist = append(arg, clist);
		}
		return;
	case 't':	/* -t -tname -tempdir=dir */
		if (strncmp(arg, "-tempdir=", 9) == 0)
			tempdir = arg + 9;
		else
			clist = append(arg, clist);
		return;
	case 'p':	/* -p -pg */
		if (option(arg))
			clist = append(arg, clist);
		else
			fprintf(stderr, "%s: %s ignored\n", progname, arg);
		return;
	case 'D':	/* -Dname -Dname=def */
	case 'U':	/* -Uname */
	case 'I':	/* -Idir */
		plist = append(arg, plist);
		return;
	case 'B':	/* -Bdir -Bstatic -Bdynamic */
#ifdef sparc
		if (strcmp(arg, "-Bstatic") == 0 || strcmp(arg, "-Bdynamic") == 0)
			llist[1] = append(arg, llist[1]);
		else
#endif
#ifdef __riscos__
		if (strcmp(arg, "-Bunix") == 0) {
			include[0] = "-IUnix:";
			ld[3] = "";
			ld[6] = "-Lgccbin:arm-riscos-aof.2_95_4.apcs26.unixlib";
			ld[7] = "-lgcc";
			ld[8] = "-LUnix:";
			ld[9] = "-lUnixLib";
		} else
#endif
		{
		static char *path;
		if (path)
			error("-B overwrites earlier option", 0);
		path = arg + 2;
		if (strstr(com[1], "win32") != NULL)
			com[0] = concat(replace(path, '/', '\\'), concat("rcc", first(suffixes[4])));
		else
			com[0] = concat(path, "rcc");
		if (path[0] == 0)
			error("missing directory in -B option", 0);
		}
		return;
	case 'h':
		if (strcmp(arg, "-help") == 0) {
			static int printed = 0;
	case '?':
			if (!printed)
				help();
			printed = 1;
			return;
		}
	case 's':
		if (strcmp(arg,"-static") == 0) {
			if (!option(arg))
				fprintf(stderr, "%s: %s ignored\n", progname, arg);
			return;
		}
	case 'c':			/* handle -copyright */
		{
			size_t minlen;
			minlen = strlen(arg);
			minlen = (minlen < 3 ? 3 : minlen);
			if (strncmp("-copyright",arg,minlen) == 0)
			{
				copyright();
				exit(EXIT_SUCCESS);
			}
			else if (arg[2] != 0)
			{
				fprintf(stderr, "%s: %s ignored\n", progname, arg);
				return;
			}
			else
			    ;			/* fall through to single-char case */
		}
	case 'v':			/* handle -version */
		{
			size_t minlen;
			minlen = strlen(arg);
			minlen = (minlen < 3 ? 3 : minlen);
			if (strncmp("-version",arg,minlen) == 0)
			{
				printf("%s version %s [%s]\n",
				       progname, VERSIONNUMBER, VERSIONDATE);
				exit(EXIT_SUCCESS);
			}
			else if (arg[2] != 0)
			{
				fprintf(stderr, "%s: %s ignored\n", progname, arg);
				return;
			}
			else
			    ;			/* fall through to single-char case */
		}
	}
	if (arg[2] == 0)
		switch (arg[1]) {	/* single-character options */
		case 'S':
			Sflag++;
			return;
		case 'O':
			fprintf(stderr, "%s: %s ignored\n", progname, arg);
			return;
		case 'A': case 'n': case 'w': case 'P':
			clist = append(arg, clist);
			return;
		case 'g': case 'b':
			if (option(arg))
				clist = append(arg[1] == 'g' ? "-g2" : arg, clist);
			else
				fprintf(stderr, "%s: %s ignored\n", progname, arg);
			return;
		case 'G':
			if (option(arg)) {
				clist = append("-g3", clist);
				llist[0] = append("-N", llist[0]);
			} else
				fprintf(stderr, "%s: %s ignored\n", progname, arg);
			return;
		case 'E':
			Eflag++;
			return;
		case 'c':
			cflag++;
			return;
		case 'M':
			Eflag++;	/* -M implies preprocess only */
			plist = append("-M",plist);
			return;
		case 'N':
			if (strcmp(basepath(cpp[0]), "gcc-cpp") == 0)
				plist = append("-nostdinc", plist);
			include[0] = 0;
			ilist = 0;
			return;
		case 'v':
			if (verbose++ == 0) {
				if (strcmp(basepath(cpp[0]), "gcc-cpp") == 0)
					plist = append(arg, plist);
				clist = append(arg, clist);
				fprintf(stderr, "%s %s\n", progname, rcsid);
			}
			return;
		}
	if (cflag || Sflag || Eflag)
		fprintf(stderr, "%s: %s ignored\n", progname, arg);
	else
		llist[1] = append(arg, llist[1]);
}

/* path2list - convert a colon- or semicolon-separated list to a list */
static List path2list(const char *path) {
	List list = NULL;
	char sep = ':';

	if (path == NULL)
		return NULL;
	if (strchr(path, ';'))
		sep = ';';
	while (*path) {
		char *p, buf[512];
		if (p = strchr(path, sep)) {
			assert(p - path < sizeof buf);
			strncpy(buf, path, p - path);
			buf[p-path] = '\0';
		} else {
			assert(strlen(path) < sizeof buf);
			strcpy(buf, path);
		}
		if (!find(buf, list))
			list = append(strsave(buf), list);
		if (p == 0)
			break;
		path = p + 1;
	}
	return list;
}

/* replace - copy str, then replace occurrences of from with to, return the copy */
char *replace(const char *str, int from, int to) {
	char *s = strsave(str), *p = s;

	for ( ; (p = strchr(p, from)) != NULL; p++)
		*p = to;
	return s;
}

/* rm - remove files in list */
static void rm(List list) {
	if (list) {
		List b = list;
		if (verbose)
			fprintf(stderr, "rm");
		do {
			if (verbose)
				fprintf(stderr, " %s", b->str);
			if (verbose < 2)
				remove(b->str);
		} while ((b = b->link) != list);
		if (verbose)
			fprintf(stderr, "\n");
	}
}

/* split s[] at sepstr[], and allocate and return parts[], and as a function value, the number of parts */
static size_t split(const char *s, char ***parts, const char *sepstr) {
	size_t max_parts, n;

	max_parts = 10;
	*parts = (char **)malloc(max_parts * sizeof(char*));
	assert(*parts);
	debug(fprintf(stderr,"DEBUG 0: split(): *parts = %p\n", *parts));
	for (n = 0; *s; n++) {
		size_t len_part;
		const char *p;

		if (n >= max_parts) {
			max_parts += 10;
			*parts = (char**)realloc(*parts, max_parts * sizeof(char*));
			assert(*parts);
		}
		p = (const char*)strstr(s,sepstr);
		len_part = (p == (const char*)NULL) ? strlen(s) : (size_t)(p - s);
		(*parts)[n] = malloc(len_part + 1);
		assert((*parts)[n]);
		strncpy((*parts)[n], s, len_part);
		(*parts)[n][len_part] = '\0';
		debug(fprintf(stderr,"\tDEBUG 1: split(): parts[%u] = [%s] @ %p\ts = [%s] @ %p\n", n, (*parts)[n], (*parts)[n], s,s));
		s += len_part;
		if (*s) s += strlen(sepstr);
		debug(fprintf(stderr,"\tDEBUG 2: split(): parts[%u] = [%s] @ %p\ts = [%s] @ %p\n\n", n, (*parts)[n], (*parts)[n], s, s));
	}
	assert(*s == '\0');
	debug(fprintf(stderr,"DEBUG 3: split(): *parts = %p\n", *parts));
	*parts = (char**)realloc(*parts, n * sizeof(char*));
	debug(fprintf(stderr,"DEBUG 4: split(): *parts = %p\n", *parts));
	assert(*parts);
	return (n);
}


/* split_Wx_args - split -Wx,-a,-b,-c,-d,... into -Wx,-a -Wx,-b, -Wx,-c -Wx,-d, updating argc and argv */
static void split_Wx_args(int *pargc, char ***pargv) {
	int i, ac;
	size_t max_ac;
	char **av;
	max_ac = ac = *pargc;
	av = (char**)malloc((max_ac + 1) * sizeof(char*)); /* +1 for trailing NULL-pointer */
	assert(av);
	(void)memcpy(av,*pargv,(max_ac + 1) * sizeof(char*));
	for (i = 0; i < ac; ++i) {
		if (av[i] != (char*)NULL) {
			if ((av[i][0] == '-') &&
			    (av[i][1] == 'W')) {
				size_t j, n;
				char **parts;

				n = split(av[i],&parts,",-");
				assert(n > 0);
				if (n > 1) {
					if ((ac + (n - 1)) > max_ac) {
						char *p;
						max_ac += n + 10;
						av = (char**)realloc(av, (max_ac + 1) * sizeof(char*));
						assert(av);
						(void)memmove(&av[i + 1 + n - 1],
							      &av[i + 1],
							      (size_t)(ac - (i + 1)) * sizeof(av[0]));
						p = parts[0];
						parts[0] = concat("", parts[0]);
						free((void*)p);
						for (j = 1; j < n; ++j) {
							char W[5];
							strncpy(W,av[i],3);
							W[3] = '-';
							W[4] = '\0';
							p = parts[j];
							parts[j] = concat(W, parts[j]);
							free((void*)p);
						}
						(void)memmove(&av[i],
							      &parts[0],
							      n * sizeof(av[0]));
					}
					ac += (n - 1);
					i += (n - 1);
				}
				else
					free((void*)parts[0]);
				free((void*)parts);
			}
		}
	}
	{				/* delete any emdedded NULL arguments */
		int i, j;
		for (i = j = 0; j < ac; ++j) {
			if (av[j] != (char*)NULL)
				av[i++] = av[j];
		}
		ac = i;
		for (j = 0; j < ac; ++j)
			debug(fprintf(stderr, "DEBUG: new argv[%d]\t= [%s]\n", j, av[j]));
	}
	av[ac] = (char*)NULL;		/* av[] must always be NULL-pointer terminated */
	*pargc = ac;
	*pargv = av;
}


/* strsave - return a saved copy of string str */
char *strsave(const char *str) {
	return strcpy(alloc(strlen(str)+1), str);
}

/* stringf - format and return a string */
char *stringf(const char *fmt, ...) {
	char buf[1024];
	va_list ap;
	int n;

	va_start(ap, fmt);
	n = vsprintf(buf, fmt, ap);
	va_end(ap);
	return strsave(buf);
}

/* suffix - if one of tails[0..n-1] holds a proper suffix of name, return its index */
int suffix(char *name, char *tails[], int n) {
	int i, len = strlen(name);

	for (i = 0; i < n; i++) {
		char *s = tails[i], *t;
		for ( ; t = strchr(s, ';'); s = t + 1) {
			int m = t - s;
			if (len > m && strncmp(&name[len-m], s, m) == 0)
				return i;
		}
		if (*s) {
			int m = strlen(s);
			if (len > m && strncmp(&name[len-m], s, m) == 0)
				return i;
		}
	}
	return -1;
}

/* tempname - generate a temporary file name in tempdir with given suffix */
char *tempname(char *suffix) {
	static int n;
	char *name = stringf("%s/lcc%d%d%s", tempdir, getpid(), n++, suffix);

	if (strstr(com[1], "win32") != NULL)
		name = replace(name, '/', '\\');
	rmlist = append(name, rmlist);
	return name;
}
