Path: tut!sunic!mcsun!hp4nl!fwi.uva.nl!croes
From: croes@fwi.uva.nl (Felix A. Croes)
Newsgroups: comp.os.minix
Subject: ld: cc.c
Keywords: cc, ld
Message-ID: <242@fwi.uva.nl>
Date: 6 Nov 89 14:17:01 GMT
Sender: news@fwi.uva.nl
Reply-To: croes@fwi.uva.nl (Felix A. Croes)
Organization: The Courts of Chaos
Lines: 967

/*
 * Minix C compiler driver, ST version
 * Author: Felix A. Croes (croes@fwi.uva.nl)
 *
 * Changes:
 *  FAC 16/10/89: replaced /usr/lib/ld.old & /usr/lib/cv by /usr/lib/ld.new
 */


/*==========================================================================*
 *				global stuff				    *
 *==========================================================================*/

# ifdef ATARI_ST
#  define NULL		0L
# endif

# include <errno.h>
# include <signal.h>
# include <sys/stat.h>
# include <minix/const.h>	/* for CLICK_SIZE and MAX_PATH */

extern int errno;
void error();			/* forward */
extern char *strcpy(), *strcat(), *strncat(), *rindex();

# undef PUBLIC		/* undo definitions in minix/const.h */
# undef PRIVATE

# define PUBLIC		/* nothing */
# define PRIVATE	/* nothing */
# define FALSE		0
# define TRUE		1

# ifndef ARGSIZE
#  define ARGSIZE	512	/* number of arguments (internal) */
# endif

# ifndef VBUFSIZ
#  define VBUFSIZ	1024	/* verbose buffer size */
# endif

# ifndef CBUFSIZ
#  define CBUFSIZ	8192	/* copy buffer size */
# endif

typedef char flag;

PUBLIC flag keep;		/* keep temporary files */
PUBLIC flag verbose;		/* show what happens */
PUBLIC flag mention;		/* mention file as it is processed */
PUBLIC flag dontdoit;		/* only show what happens */

PUBLIC int mypid;		/* process id */
PUBLIC char *command;		/* command name */
PUBLIC char **optlist;		/* list of options in command line */
PUBLIC char **filelist;		/* list of files in command line */

PUBLIC char *basename;		/* basic part of file name: /usr/x.c => x. */
PUBLIC char *tmpname = "/tmp";	/* temporary file prefix */


/*==========================================================================*
 *				I/O					    *
 *==========================================================================*/

PUBLIC void puts(s)
char *s;
{
	/* write string s to stdout (yes, descriptor 4) */
	write(4, s, strlen(s));
}

PUBLIC void eputs(s)
char *s;
{
	/* write a string to stderr */
	write(2, s, strlen(s));
}


/*==========================================================================*
 *				memory					    *
 *==========================================================================*/

PRIVATE long piece;		/* piece of free memory */

PRIVATE char *alloc(size)
register long size;
{
	/* allocate memory */
	char *address;
	extern char *brksize, *brk();

	address = brksize - piece;
	if (size > piece) {	/* always true on first call */
		size -= piece;
		piece = (size + CLICK_SIZE-1) & ~(CLICK_SIZE-1);
		if (brk(brksize+piece) == (char*)-1) {
			error(ENOMEM, "no space", NULL);
		}
	}
	piece -= size;
	return address;
}

PUBLIC char *mkstr(s)
char *s;
{
	/* make a copy of s, return pointer to copy */
	return strcpy(alloc(strlen(s)+1L), s);
}

PRIVATE char *listlist[ARGSIZE];	/* internal argument list */
PRIVATE char **thislist = listlist;

PUBLIC char **mklist(s)
char *s;
{
	/* put s in the argument list, return pointer to argument s */
	if (thislist == &listlist[ARGSIZE]) {
		error(E2BIG, "argument overflow", NULL);
	}
	*thislist = s;
	return thislist++;
}

PRIVATE char **marklist, *markstring;

PUBLIC void mark()
{
	/* mark the current memory usage limits */
	marklist = thislist;
	markstring = brksize - piece;
}

PUBLIC void release()
{
	/* release all memory allocated after the last mark() */
	thislist = marklist;
	piece = (long)brksize - (long)markstring;
}

PUBLIC char *concat(string, ext)
char *string, *ext;
{
	/* create a new string by concatenating string and ext */
	char buffer[MAX_PATH];

	return mkstr(strcat(strcpy(buffer, string), ext));
}


/*==========================================================================*
 *				passes					    *
 *==========================================================================*/

# define USE_O		0x01	/* use -o option with output file name */
# define FD_INPUT	0x02	/* input from < */
# define FD_OUTPUT	0x04	/* output to > */
# define FORCE_DOT	0x08	/* force output file in current directory */
# define FINAL_DOT	0x10	/* output in . if pass is final pass */

/*
 * cmd [catch1] [-o output] [options] [catch2] [front] input ... [output] [tail]
 *
 * option strings:
 *	opt		opt
 *	opt*		opt.*\0
 *	opt1=opt2	replace opt1 by opt2
 */

struct pass {
	char *p_name;		/* program name */
	char *p_out;		/* output extension */
	char *p_catch1;		/* 1st options catched from cc cmdline */
	char *p_options;	/* standard options */
	char *p_catch2;		/* 2nd options catched from cc cmdline */
	char *p_front, *p_tail;	/* standard front and tail strings */
	short p_flags;		/* I/O flags */
};

PUBLIC struct pass cpp = {	/* C preprocessor */
	"/usr/lib/cpp",
	"i",
	NULL,
	NULL,
	"I*,D*,U*",		/* I*=includedir, D*=define, U*=undefine */
	NULL, NULL,
	FD_OUTPUT		/* > */
};

PUBLIC struct pass cem = {	/* C to EM translator */
	"/usr/lib/cem",
	"k",
	"R,p,w",		/* R=check for K&R, p=profile, w=dontwarn */
	"L",			/* don't know that this means */
	"T*,I*,D*,U*",		/* T*=tmpfiledir, ... */
	NULL, NULL,
	0
};

PUBLIC struct pass opt = {	/* EM optimizer */
	"/usr/lib/opt",
	"m",
	"LIB=L",		/* probably obsolete */
	NULL,
	NULL,
	NULL, NULL,
	FD_INPUT | FD_OUTPUT	/* <, > */
};

PUBLIC struct pass cg = {	/* code generator */
	"/usr/lib/cg",
	"s",
	NULL,
	NULL,
	"O=p4",			/* O=optimize, would you believe it */
	NULL, NULL,
	FINAL_DOT
};

PUBLIC struct pass as = {	/* assembler */
	"/usr/bin/as",
	"o",
	NULL,
	"",			/* i.e. "-": undefined symbols are global */
	"T*",			/* T*=tmpdir */
	NULL, NULL,
	USE_O | FORCE_DOT
};

PUBLIC struct pass ld = {	/* linker/loader */
	"/usr/lib/ld.new",
	"out",
	"n,r,s,v,vn=v,w",	/* see ld/README for description */
	"a0:2,a1:2,a2:2,a3:2,X_",
	"m*,n=a2:256,u*",	/* -n: also align data segment to click size */
	"/usr/lib/crtso.o", "/usr/lib/libc.a,/usr/lib/end.o",
	USE_O
};

PUBLIC void rm(file)
char *file;
{
	/* rm file */
	if (file != NULL && !keep) {
		if (verbose) {	/* show what's happening */
			puts("rm ");
			puts(file);
			puts("\n");
		}

		if (!dontdoit) {
			unlink(file);
		}
	}
}

PUBLIC char *shift(str)
char *str;
{
	/* maintain a fifo queue of size 2 */
	static char *str1, *str2;
	char *tmp;

	tmp = str1;
	str1 = str2;
	str2 = str;

	return tmp;
}


PRIVATE char vb[VBUFSIZ];

PRIVATE void vputs(s)
char *s;
{
	/* put string s in verbose buffer */
	strcat(vb, s);
}

PRIVATE void vsputs(s)
char *s;
{
	/* put string s in verbose buffer, preceded by a blank */
	vputs(" ");
	vputs(s);
}

PRIVATE void mkargs(s, c)
register char *s;
char c;
{
	/* scan through list s, make arguments */
	char *p, buffer[MAX_PATH];

	do {
		p = s;
		while (*s != ',' && *s != '\0') s++;  /* to next separator */

		buffer[0] = c;		/* no effect if c == '\0' */
		buffer[1] = '\0';
		mklist(mkstr(strncat(buffer, p, s-p)));

		vsputs(buffer);	/* record what happens */
	} while (*s++ != '\0');
}

PRIVATE void catch(option, l)
char *option;
register char *l;
{
	/* if option is in list l then add as argument */
	char buffer[MAX_PATH];
	register char *p;

	p = option;
	do {
		switch(*l) {
		case ',':
			l++;	/* skip separator */
			/* fall through */

		case '\0':
			if (*p == '\0') {
				mkargs(option, '-');
			}
			/* else no match, no argument */
			p = option;
			continue;

		case '*':
			l++;		/* skip '*' */
			if (*l != '\0') {
				l++;	/* skip ',' */
			}
			mkargs(option, '-');
			p = option;
			continue;

		case '=':		/* translate */
			if (*p != '\0') {
				/* no match */
				p = option;
				break;
			}

			p = buffer;
			l++;		/* skip '=' */
			for (;;) {
				if (*l == '\0') {
					break;
				}
				*p++ = *l++;
				if (*l == ',') {
					l++;
					break;
				}
			}
			*p = '\0';
			/* translated option now in buffer */
			mkargs(buffer, '-');	/* buffer contains no ',' */
			p = option;
			continue;
		}

		if (*l++ == *p) {
			p++;		/* same char */
		} else {
			/* no match, skip to next separator */
			while (*l != '\0' && *l++ != ',') ;
			p = option;
		}
	} while (*l != '\0');
}

PUBLIC char *do_pass(passes, from, to)
struct pass *passes[];
char *from, *to;
{
	/*
	 * build argument list for passes and execute them. Return pointer
	 * to last file name.
	 * NOTE: memory is only allocated here, not released
	 */
	register struct pass *pass;
	register char **p, *tmp;
	char **args;
	int fd, stat;

	do {
		/* get the current pass */
		pass = *passes;

		/* empty verbose output buffer */
		vb[0] = '\0';

		/* command name */
		tmp = pass->p_name;
		if (access(tmp+4, 1) >= 0) {
			/* this is a hack */
			tmp += 4;
		}
		args = mklist(tmp);
		vputs(tmp);

		/* catch1 */
		if (optlist != NULL && pass->p_catch1 != NULL) {
			for (p = optlist; *p != NULL; p++) {
				catch(*p, pass->p_catch1);
			}
		}

		/* determine output file name */
		tmp = (passes[1] != NULL || to == NULL) ?
		    concat(((passes[1] == NULL && (pass->p_flags & FINAL_DOT))
			    || (pass->p_flags & FORCE_DOT)) ?
			basename :
			tmpname,
			pass->p_out) :
		    to;
		/* put in queue */
		rm(shift((access(tmp, 0) < 0) ? tmp : NULL));

		/* -o output.file */
		if (pass->p_flags & USE_O) {
			mkargs("o", '-');
			mklist(tmp);
			vsputs(tmp);
		}

		/* standard options */
		if (pass->p_options != NULL) {
			mkargs(pass->p_options, '-');
		}

		/* catch2 */
		if (optlist != NULL && pass->p_catch2 != NULL) {
			for (p = optlist; *p != NULL; p++) {
				catch(*p, pass->p_catch2);
			}
		}

		/* front */
		if (pass->p_front != NULL) {
			mkargs(pass->p_front, 0);
		}

		if (from == NULL) {
			/* doing a FINAL_PASS */
			while (*filelist) {
				mklist(*filelist);
				vsputs(*filelist);
				filelist++;
			}
		} else {
			/* input.file */
			if (pass->p_flags & FD_INPUT) {
				/* read from stdin */
				vsputs("<");

				if (!dontdoit) {
					fd = open(from, 0);
					if (fd < 0) {
						error(errno, "cannot open ", from);
					}
					dup2(fd, 0);
					close(fd);
				}
			} else {
				mklist(from);
			}

			vsputs(from);
		}

		/* !-o output.file */
		if (tmp != NULL && !(pass->p_flags & USE_O)) {
			if (pass->p_flags & FD_OUTPUT) {
				/* write to stdout */
				vsputs(">");

				if (!dontdoit) {
					fd = creat(tmp, 0666);
					if (fd < 0) {
						error(errno, "cannot creat ", tmp);
					}
					dup2(fd, 1);
					close(fd);
				}
			} else {
				mklist(tmp);
			}

			vsputs(tmp);
		}

		/* tail */
		if (pass->p_tail != NULL) {
			mkargs(pass->p_tail, 0);
		}

		/* terminate argument list */
		mklist(NULL);
		if (verbose) {
			puts(vb);
			puts("\n");
		}

		/* execute pass */
		if (!dontdoit) {
			int child;

			if ((child=fork()) < 0) {
				error(errno, *args, ": no more processes");
			}

			if (child == 0) {
				/* child */
				close(3);
				close(4);
				execv(*args, args);
				if (errno == ENOEXEC) {
					char *getenv();
					register char *shell;

					shell = getenv("SHELL");
					if (shell == NULL) {
						shell = "/bin/sh";
					}
					execl(shell, shell, "-c", vb, NULL);
				}
				exit(-errno);
			} else {
				/* parent */
				register int pid;

				/* get stdin and stdout back */
				dup2(3, 0);
				dup2(4, 1);

				/* get child exit status */
				do {
					if ((pid=wait(&stat)) < 0) {
						stat = ESRCH<<8;
						break;
					}
				} while (pid != child);

				/*
				 * child exit status:
				 * < 0: could not execute pass
				 * = 0: OK
				 * > 0: child encountered error
				 */
				switch (stat >>= 8) {
				case OK:
					break;	/* no error */

				case -E2BIG:
				case -ENOMEM:
					error(E2BIG, *args, ": too many arguments");

				case -EAGAIN:
					error(EAGAIN, *args, ": not enough memory.\nUse chmem to reduce its stack allocation.");

				default:
					if (stat < 0) {
						error(-stat, "cannot execute ", *args);
					} else {
						/* rm tmpfiles */
						rm(shift(NULL));
						rm(shift(NULL));

						/* abort */
						exit(stat);
					}
				}
			}
		}

		from = tmp;

	} while (*++passes);

	rm(shift(NULL));	/* remove all but last output file */
	return tmp;
}


/*==========================================================================*
 *				rules					    *
 *==========================================================================*/

struct rule {
	char *r_in;		/* extension string */
	flag (*r_match)();	/* match function */
	struct pass **r_list;	/* list of passes */
};

/* match functions */
flag mcpp();	/* forward */

# define DFLT_RULE	rules_o
# define FINAL_RULE	rules_out
# define S_EXT		"s"
# define O_EXT		"o"
# define DFLT_EXT	O_EXT
# define FINAL_EXT	O_EXT
# define DFLT_TARGET	"a.out"

/* DEFAULT RULES */

struct pass *cem_as[] =	{ &cem, &opt, &cg, &as, NULL };
# define opt_as		(cem_as + 1)
# define cg_as		(cem_as + 2)
struct pass *cpp_as[] =	{ &cpp, &as, NULL };
# define as_as		(cem_as + 3)

PUBLIC struct rule rules_o[] = {	/* from .c to .o */
	"c", NULL, cem_as,
	"i", NULL, cem_as,
	"k", NULL, opt_as,
	"m", NULL, cg_as,
	"s", mcpp, cpp_as,
	"s", NULL, as_as,
	NULL
};


/* .S RULES */

struct pass *cem_cg[] =	{ &cem, &opt, &cg, NULL };
# define opt_cg		(cem_cg + 1)
# define cg_cg		(cem_cg + 2)

PUBLIC struct rule rules_s[] = {	/* from .c to .s */
	"c", NULL, cem_cg,
	"i", NULL, cem_cg,
	"k", NULL, opt_cg,
	"m", NULL, cg_cg,
	NULL
};

/* .OUT RULES */

struct pass *ld_ld[] = { &ld, NULL };

PUBLIC struct rule rules_out[] = {	/* from .o to .out */
	"o", NULL, ld_ld,
	NULL
};

/* MATCH FUNCTIONS */

PRIVATE flag mcpp(file)
char *file;
{
	/* return TRUE if first char of file == '#', FALSE otherwise */
	register int fd;
	char c;

	fd = open(file, 0);
	if (fd < 0) {
		return FALSE;
	}
	c = '\0';
	read(fd, &c, 1);
	close(fd);

	return (c == '#');
}


PUBLIC char *do_rule(r, from, to, ext)
register struct rule *r;
char *from, *to, *ext;
{
	static char outfile[MAX_PATH];

	/* search for the right extension, then do passes */
	while (r->r_in != NULL) {
		if (strcmp(r->r_in, ext) == 0 &&	/* right .ext */
		    (r->r_match == NULL || (r->r_match)(from))) {
			/* from accepted by match function */
			if (mention) {
				puts(from);
				puts(":\n");
			}
			from = do_pass(r->r_list, from, to);
			break;
		}
		r++;
	}

	return strcpy(outfile, from);
}

/*==========================================================================*
 *				MAIN prog				    *
 *==========================================================================*/

PUBLIC void error(stat, s1, s2)
int stat;
char *s1, *s2;
{
	/* print errormesg + argument, exit(stat) */
	eputs(command);		/* program name */
	eputs(": ");

	eputs(s1);		/* errmesg (usually) */
	if (s2 != NULL) {
		eputs(s2);	/* argument */
	}
	eputs("\n");

	/* remove temporary files */
	rm(shift(NULL));
	rm(shift(NULL));

	exit(stat);		/* abort */
}

PRIVATE sigcatch()
{
	/* this function is called on SIGINT */

	signal(SIGINT, SIG_IGN);	/* ignore further signals */
	/* delete temporary files */
	rm(shift(NULL));
	rm(shift(NULL));
	signal(SIGINT, SIG_DFL);	/* allow signals again */
	/* commit suicide */
	kill(mypid, SIGINT);
}

PRIVATE void copyfile(in, out)
register char *in, *out;
{
	/* copy in to out */
	static char buffer[CBUFSIZ];
	struct stat buf;
	register int n, fdin, fdout;

	shift(in);
	shift(out);

	if ((fdin=open(in, 0)) < 0 || fstat(fdin, &buf) < 0) {
		error(errno, "cannot open ", in);
	}
	fdout = creat(out, 0666);
	if (fdout < 0) {
		error(errno, "cannot creat ", out);
	}

	while ((n=read(fdin, buffer, CBUFSIZ)) != 0) {
		if (n < 0) {
			error(errno, "cannot read ", in);
		}
		if (write(fdout, buffer, n) < 0) {
			error(errno, "cannot write ", out);
		}
	}

	close(fdin);
	close(fdout);
	chmod(out, buf.st_mode);
}

PUBLIC int main(argc, argv)
int argc;
char *argv[];
{
	/*
	 * main loop:
	 * - scan arguments and make list of options
	 * - scan arguments and process filenames
	 * - finally, make list of filenames and do the final rule, if
	 *   neccessary.
	 */
	register char *p, **ap;
	register int pid;
	struct rule *r;
	char **l, *target, *final_target, *outfile, *tmp;
	int nargs;
	flag do_final;

	/* catch SIGINT, but don't catch SIGQUIT */
	signal(SIGINT, sigcatch);
	/* who am I? */
	mypid = getpid();

	command = *argv++;
	dup2(0, 3);
	dup2(1, 4);

	do_final = TRUE;
	r = DFLT_RULE;
	tmp = target = final_target = NULL;
	nargs = 0;

	/* make option list */
	for (ap = argv; *ap != NULL; ap++) {
		p = *ap;
		if (*p++ == '-') {
			if (p[1] == '\0') {
				/* single letter options */
				switch (*p) {
/* BEGIN rule dependent part */
				case 'S':
					r = rules_s;
					do_final = FALSE;
					break;

				case 'c':
					r = rules_o;
					do_final = FALSE;
					break;
/* END rule dependent part */
				case 't':	/* keep temporary files */
					keep = TRUE;
					break;

				case 'v':	/* verbose */
					verbose = TRUE;
					break;

				case 'o':	/* name target */
					if (*++ap != NULL) {
						final_target = *ap;
						*ap = "-";	/* wipe out */
						break;
					}
					/* illegal -o option */
					--ap;
					break;
				}
			} else {
				/* more than one letter options */
				if (strcmp(p, "vn") == 0) {
					/* verbose but don't do anything */
					verbose = TRUE;
					dontdoit = TRUE;

				} else if (*p == 'T') {
					/* set temporary file directory */
					tmpname = p+1;
				} else if (*p == 'l') {
					/* create new lib argument */
					char buffer[MAX_PATH];

					strcpy(buffer, "/usr/lib/lib");
					strcat(buffer, p+1);
					*ap = concat(buffer, ".a");
					continue;
				}
			}
			l = mklist(p);
			if (optlist == NULL) {
				optlist = l;
			}
		} else {
			/* not an option */
			nargs++;
		}
	}

	if (nargs == 0) {
		return OK;
	}

	/* terminate option list */
	mklist(NULL);

	if (nargs == 1 && !do_final) {
		target = final_target;
	}
	if (final_target == NULL) {
		final_target = DFLT_TARGET;
	}

	/* determine basic part of tmpname */
	tmpname = concat(tmpname, "/ccXXXXXX.");
	p = tmpname + strlen(tmpname) - 2;
	pid = mypid;
	do {
		*p = '0' + pid % 10;
		pid /= 10;
	} while (*--p == 'X');

	if (nargs > 1) {
		mention = TRUE;
	}

	for (ap = argv; *ap != NULL; ap++) {
		if (*ap[0] != '-') {
			char *infile;

			mark();	/* mark current memory usage */

			infile = rindex(*ap, '/');
			infile = (infile != NULL) ? infile + 1 : *ap;

			/* determine basename */
			p = rindex(infile, '.');
			if (p != NULL) {
				p++;
				rindex(basename = mkstr(infile), '.')[1] = '\0';
			} else {
				p = DFLT_EXT;
				basename = concat(infile, ".");
			}

			outfile = do_rule(r, *ap, target, p);

			/* release all allocated after last mark() */
			release();

			/* put file name in file list */
			l = mklist(mkstr(outfile));
			if (filelist == NULL) {
				filelist = l;
			}
			if (shift(NULL) != NULL && nargs == 1) {
				/* to be deleted */
				tmp = *l;
			}
		}
	}

	if (do_final) {
		/* terminate file list */
		mklist(NULL);

		shift(tmp);

		mention = FALSE;
		/* apply the final rule */
		outfile = do_rule(FINAL_RULE, NULL, NULL, FINAL_EXT);

		if (verbose) {
			puts("mv ");
			puts(outfile);
			puts(" ");
			puts(final_target);
			puts("\n");
		}

		if (!dontdoit) {
			/* mv outfile to final_target */
			if (link(outfile, final_target) < 0) {
				copyfile(outfile, final_target);
			}
			unlink(outfile);
		}
	}

	/* ready */
	return OK;
}
--
+--------------------------------------|--------------------------------------+
| "GEM is dead -		       | 	    croes@fwi.uva.nl	      |
|		 long live Minix!"     |    croes%fwi.uva.nl@hp4nl.nluug.nl   |
+--------------------------------------|--------------------------------------+
