/*ident	"@(#)cls4:tools/pt/ptlink.c	1.6" */
#include <stdio.h>
#include <ctype.h>
#include "dem.h"
#include "pt.h"

char* tdir;
int verbose = 0;
long start_time;
char* PROGNAME = "CC[ptlink]";
static int plusi = 0;

/******************** STRINGS LISTS FOR ERROR CLEANUP ********************/

static char* sstack[25];
static int sptr = -1;
void pushstring(s)
char* s;
{
	if (s == NULL || !*s)
		fatal("bad argument to pushstring()", 0, 0);
	sstack[++sptr] = copy(s);
}
void popstring()
{
	if (sptr < 0)
		fatal("popstring() called without pushstring()", 0, 0);
	del_file(sstack[sptr]);
	fs(sstack[sptr--]);
}
void handlesig()
{
	static int entry_count = 0;

	if (entry_count++)
		return;
	while (sptr >= 0)
		popstring();
	fatal("interrupted by INT or QUIT signal", 0, 0);
}

/************************* TABLE OF FILENAMES SEEN *************************/

typedef struct Fname Fname;
struct Fname {
	char* name;
	Fname* next;
};
static Fname* ftable[FTSIZE];
static Fname* otable[FTSIZE];

/* look up a filename */
Fname* flookup(s, ftab)
char* s;
Fname* ftab[];
{
	Fname* p;

	if (s == NULL || !*s || ftab == NULL)
		fatal("bad argument to flookup()", 0, 0);

	p = ftab[(int)hash(s, FTSIZE)];
	while (p != NULL) {
		if (!strcmp(s, p->name))
			return p;
		p = p->next;
	}

	return NULL;
}

/* add a filename */
void fadd(s, ftab)
char* s;
Fname* ftab[];
{
	Fname* p;
	int h;

	if (s == NULL || !*s || ftab == NULL)
		fatal("bad argument to fadd()", 0, 0);

	p = ftab[h = (int)hash(s, FTSIZE)];
	while (p != NULL) {
		if (!strcmp(s, p->name))
			return;
		p = p->next;
	}
	if (p == NULL) {
		p = (Fname*)gs(sizeof(Fname));
		p->next = ftab[h];
		ftab[h] = p;
		p->name = copy(s);
	}
}

/* delete the table of file names */
void ftab_del(ftab)
Fname* ftab[];
{
	int i;
	Fname* p;
	Fname* r;

	if (ftab == NULL)
		fatal("bad argument to ftab_del()", 0, 0);

	for (i = 0; i < FTSIZE; i++) {
		p = ftab[i];
		while (p != NULL) {
			fs(p->name);
			r = p;
			p = p->next;
			fs(r);
		}
		ftab[i] = NULL;
	}
}

/************************* NM / SYMBOL TABLE *************************/

typedef struct Ename Ename;
typedef struct Obj Obj;
typedef struct Flist Flist;

struct Ename {
	char* name;
	Ename* next;
	char type;
};

struct Obj {
	Ename* nlist;
	Obj* next;
	char pi;
	char aflag;
};

struct Flist {
	Obj* obj;
	char* file;
	Flist* next;
	char rep;
};

static Flist* fhead = NULL;
static Flist* fcurr = NULL;

/* split the output of nm into fields */
int nm_split(buf, flds)
char* buf;
char* flds[];
{
	char c;
	int nf;
	char* s;
	int i;
	char* start;
	int flag;
	char* f[100];
	char* t;

	if (buf == NULL || flds == NULL)
		fatal("bad argument to nm_split()", 0, 0);

	nf = 0;
	buf[strlen(buf) - 1] = 0;

	/* verbose format from nm (SysV.3) */

	s = buf;
	if (s[0] == 'S' && !strncmp(s, "Symbols from ", 13)) {

		/* filename - might have to separate out object file name */

		t = s + 13;
		while (*t && *t != '[')
			t++;
		if (!*t) {
			if (t[-1] == ':')
				t[-1] = 0;
			flds[0] = s + 13;
			return 1;
		}
		else {
			s = ++t;
			while (*t && *t != ']')
				t++;
			if (*t)
				*t = 0;
			flds[0] = s;
			return 1;
		}
	}
	flag = 0;
	while (*s) {
		if (*s == '|') {
			flag = 1;
			break;
		}
		s++;
	}
	s = buf;
	if (flag) {

		/* grab separate fields */

#ifdef svr4
		for (i = 0; i <= 7; i++) {
#else
		for (i = 0; i <= 6; i++) {
#endif
			start = s;
			while (*s && *s != '|')
				s++;
			if (*s)
				*s++ = 0;
			else
				*s = 0;
			while (*start && *start <= ' ')
				start++;
			t = start + strlen(start) - 1;
			while (t >= start && *t <= ' ')
				*t-- = 0;
			f[i] = start;
		}
#ifdef svr4
		if (strcmp(f[4], "GLOB"))
			return 0;
		if (!strcmp(f[6], "UNDEF"))
			flds[0] = "U";
		else if (!strcmp(f[6], "COMMON"))
			flds[0] = "C";
		else if (!strcmp(f[3], "FUNC"))
			flds[0] = "T";
		else
			flds[0] = "D";
		flds[1] = f[7];
		return 2;
#else
		if (strcmp(f[2], "extern"))
			return 0;
		if (!strcmp(f[6], ".text"))
			flds[0] = "T";
		else if (!strcmp(f[6], ".data"))
			flds[0] = "D";
		else if (strcmp(f[1], "0"))
			flds[0] = "C";
		else
			flds[0] = "U";
		flds[1] = f[0];
		return 2;
#endif
	}

	/* terse format */

	else {
		for (i = 0; i <= 99; i++) {
			while (*s && *s <= ' ')
				s++;
			start = s;
			while (*s && *s > ' ')
				s++;
			if (s == start)
				break;
			if (*s)
				*s++ = 0;
			f[nf++] = start;
		}
		if (nf < 1 || nf > 3)
			return 0;
		if (nf == 3) {
			c = f[1][0];
			if (c == 'T')
				flds[0] = "T";
			else if (c == 'B' || c == 'D' || c == 'G' || c == 'S')
				flds[0] = "D";
			else if (c == 'C' || c == 'E' || c == 'c')
				flds[0] = "C";
			else if (c == 'U' || c == 'V')
				flds[0] = "U";
			else
				return 0;
			flds[1] = f[2];
			return 2;
		}
		if (nf == 2) {
			c = f[0][0];
			if (c == 'T')
				flds[0] = "T";
			else if (c == 'B' || c == 'D' || c == 'G' || c == 'S')
				flds[0] = "D";
			else if (c == 'C' || c == 'E' || c == 'c')
				flds[0] = "C";
			else if (c == 'U' || c == 'V')
				flds[0] = "U";
			else
				return 0;
			flds[1] = f[1];
			return 2;
		}

		/* grab filename and separate out if necessary */

		t = f[0];
		while (*t && *t != '[')
			t++;
		if (!*t) {
			if (t[-1] == ':')
				t[-1] = 0;
			flds[0] = f[0];
			return 1;
		}
		s = ++t;
		while (*t && *t != ']')
			t++;
		if (*t)
			*t = 0;
		flds[0] = s;
		return 1;
	}
}

/* return 1 if file is an archive library */
int is_archive(f)
char* f;
{
	if (f == NULL || !*f)
		fatal("bad argument to is_archive()", 0, 0);

	if (strlen(f) > strlen(ARC_EXT) &&
	    !strcmp(f + strlen(f) - strlen(ARC_EXT), ARC_EXT))
		return 1;
	else
		return 0;
}

/* read one object file or archive */
Obj* readnm(f)
char* f;
{
	char buf[MAXPATH];
	char* flds[2];
	int nf;
	FILE* fp;
	char c;
	Ename* enp;
	Ename* enplist;
	Obj* olist;
	Obj* olistp;
	Obj* olisth;

	if (f == NULL || !*f)
		fatal("bad argument to readnm()", 0, 0);

	/* establish pipe to file */

	sprintf(buf, "%s %s", NMPATH, f);
	if ((fp = fast_popen(buf, "r")) == NULL) {
		fprintf(stderr, "%s warning: nm failed on %s, ignoring ...\n", PROGNAME, f);
		return NULL;
	}

	/* split each line into fields */

	enplist = NULL;
	olist = NULL;
	olisth = NULL;
	while (fgets(buf, MAXPATH, fp) != NULL) {
		nf = nm_split(buf, flds);
		if (nf == 0)
			continue;

		/* start of a file in an archive */

		if (nf == 1) {
			fadd(basename(flds[0], 1), ftable);
			if (enplist != NULL) {
				olistp = (Obj*)gs(sizeof(Obj));
				olistp->nlist = enplist;
				enplist = NULL;
				olistp->next = NULL;
				if (olist != NULL)
					olist->next = olistp;
				else
					olisth = olistp;
				olist = olistp;
			}
		}

		/* regular record in a file */

		else {
			c = flds[0][0];
			if (c != 'B' && c != 'C' && c != 'D' &&
			    c != 'T' && c != 'U')
				fatal("bad type returned by nm_split()", 0, 0);
			enp = (Ename*)gs(sizeof(Ename));
			enp->name = copy(flds[1]);
			enp->type = c;
			enp->next = enplist;
			enplist = enp;
		}
	}

	/* finish up */

	if (enplist != NULL) {
		olistp = (Obj*)gs(sizeof(Obj));
		olistp->nlist = enplist;
		enplist = NULL;
		olistp->next = NULL;
		if (olist != NULL)
			olist->next = olistp;
		else
			olisth = olistp;
		olist = olistp;
	}
	if (olisth != NULL)
		olisth->aflag = (is_archive(f) || olisth->next != NULL);

	fast_pclose(fp);

	return olisth;
}

/* delete a name list */
void obj_del2(r)
Obj* r;
{
	Obj* r2;
	Ename* s;
	Ename* s2;

	if (r == NULL)
		fatal("bad argument to obj_del2()", 0, 0);

	while (r != NULL) {
		s = r->nlist;
		while (s != NULL) {
			fs(s->name);
			s2 = s;
			s = s->next;
			fs(s2);
		}
		r2 = r;
		r = r->next;
		fs(r2);
	}
}

/* add a file to the set of objects that are known */
void addfile(objp, f, flag)
Obj* objp;
char* f;
int flag;
{
	Flist* p;

	if (f == NULL || !*f || flag < 0 || flag > 1)
		fatal("bad argument to addfile()", 0, 0);
	if (objp == NULL)
		return;

	/* might already be on list if instantiation object */

	if (flag) {
		p = fhead;
		while (p != NULL) {
			if (!strcmp(f, p->file))
				break;
			p = p->next;
		}
		if (p != NULL) {
			obj_del2(p->obj);
			p->obj = objp;
			return;
		}
	}

	/* otherwise add to list */

	p = (Flist*)gs(sizeof(Flist));
	p->obj = objp;
	p->file = copy(f);
	p->next = NULL;
	p->rep = flag;
	if (!flag) {
		if (fhead == NULL) {
			fhead = p;
			fcurr = fhead;
		}
		else {
			fcurr->next = p;
			fcurr = p;
		}
	}
	else {
		if (fhead == NULL) {
			fhead = p;
			fcurr = p;
		}
		else {
			p->next = fhead;
			fhead = p;
		}
	}
}

/* delete a list of object files */
void obj_del()
{
	Flist* p;
	Flist* p2;

	p = fhead;
	while (p != NULL) {
		fs(p->file);
		obj_del2(p->obj);
		p2 = p;
		p = p->next;
		fs(p2);
	}

	fhead = NULL;
	fcurr = NULL;
}

typedef struct Symbol Symbol;
struct Symbol {
	char* name;
	char* file;
	Symbol* next;
	char* rn;
	char* f;
	char type;
	char rep;
};

static Symbol* symtab[SYMTSIZE];

/* look up a symbol */
Symbol* lookup(s)
char* s;
{
	Symbol* p;

	if (s == NULL || !*s)
		fatal("bad argument to lookup()", 0, 0);

	p = symtab[(int)hash(s, SYMTSIZE)];
	while (p != NULL) {
		if (!strcmp(s, p->name))
			return p;
		p = p->next;
	}

	return NULL;
}

/* add a symbol */
void add(s, t, f, rep)
char* s;
char t;
char* f;
char rep;
{
	Symbol* p;
	int h;

	if (s == NULL || !*s || f == NULL || !*f || rep < 0 || rep > 1)
		fatal("bad argument to add()", 0, 0);

	p = symtab[h = (int)hash(s, SYMTSIZE)];

	/* may be there already */

	while (p != NULL) {
		if (!strcmp(s, p->name))
			break;
		p = p->next;
	}

	/* add if not */

	if (p == NULL) {
		p = (Symbol*)gs(sizeof(Symbol));
		p->next = symtab[h];
		symtab[h] = p;
		p->name = copy(s);
		p->file = copy(f);
		p->type = t;
		p->rep = rep;
		p->rn = NULL;
		p->f = NULL;
	}
	else {
		p->type = t;
		fs(p->file);
		p->file = copy(f);
		p->rep = rep;
	}
}

/* delete the symbol table */
void symtab_del()
{
	int i;
	Symbol* p;
	Symbol* r;

	for (i = 0; i < SYMTSIZE; i++) {
		p = symtab[i];
		while (p != NULL) {
			fs(p->name);
			fs(p->file);
			if (p->rn != NULL)
				fs(p->rn);
			if (p->f != NULL)
				fs(p->f);
			r = p;
			p = p->next;
			fs(r);
		}
		symtab[i] = NULL;
	}
}

/************************* SIMULATE LINKING *************************/

/* add one object */
void addobj(nl, f, rep)
Ename* nl;
char* f;
char rep;
{
	char t;
	Symbol* sp;

	if (f == NULL || !*f || rep < 0 || rep > 1)
		fatal("bad argument to addobj()", 0, 0);

	while (nl != NULL) {
		t = nl->type;

		/* always add symbols that are already defined */

		if (t == 'B' || t == 'D' || t == 'T') {
			add(nl->name, t, f, rep);
		}

		/* add undefined if not already seen */

		else if (t == 'U') {
			if (lookup(nl->name) == NULL)
				add(nl->name, t, f, rep);
		}

		/* add global common if previously undefined */

		else if (t == 'C') {
			sp = lookup(nl->name);
			if (sp == NULL || sp->type == 'U')
				add(nl->name, t, f, rep);
		}
		else {
			fatal("bad symbol table type in addobj()", 0, 0);
		}
		nl = nl->next;
	}
}

/* fake a link */
void fakelink()
{
	Flist* flp;
	Obj* op;
	char t;
	int pulled_in;
	int addflag;
	Symbol* sp;
	Ename* nl;
	char* f;
	char rep;

	flp = fhead;
	while (flp != NULL) {
		op = flp->obj;
		f = flp->file;
		rep = flp->rep;

		/* an object, unconditionally link its symbols */

		if (!op->aflag) {
			addobj(op->nlist, f, rep);
		}

		/* an archive;  conditionally link */

		else {

			/* clear pulled-in flags */

			while (op != NULL) {
				op->pi = 0;
				op = op->next;
			}
loop:
			pulled_in = 0;
			op = flp->obj;
			while (op != NULL) {

				/* ignore if already pulled in */

				if (op->pi) {
					op = op->next;
					continue;
				}
				nl = op->nlist;
				addflag = 0;

				/* add object if its symbols are needed */

				while (nl != NULL) {
					t = nl->type;
					if (t == 'B' || t == 'D' || t == 'T') {
						sp = lookup(nl->name);
						if (sp != NULL && (sp->type == 'U' || sp->type == 'C'))
							addflag = 1;
					}
					else if (t == 'C') {
						sp = lookup(nl->name);
						if (sp != NULL && sp->type == 'U')
							addflag = 1;
					}
					if (addflag) {
						pulled_in++;
						op->pi = 1;
						addobj(op->nlist, f, rep);
						break;
					}
					nl = nl->next;
				}
				op = op->next;
			}
			if (pulled_in > 0)
				goto loop;
		}
		flp = flp->next;
	}
}

/************************* NAME DEMANGLING *************************/

/* see if a a name is PTish */
int ispt(s, f)
char* s;
char* f;
{
	DEM d;
	char buf[MAXDBUF];

	if (s == NULL || !*s || f == NULL)
		fatal("bad argument to ispt()", 0, 0);

	*f = 0;
	if (dem(s, &d, buf) < 0)
		return 0;

	/* member of a template class */

	if (d.cl != NULL && d.cl->clargs != NULL && d.vtname == NULL)
		return 1;

	/* function template or regular function */

	if (d.cl == NULL && d.args != NULL) {
		strcpy(f, d.f);
		return 1;
	}

	return 0;
}

/* get the distinguishing part of a PT name */
char* getname(s, pbuf, dbuf)
char* s;
char* pbuf;
char* dbuf;
{
	DEM d;

	if (s == NULL || !*s || pbuf == NULL || dbuf == NULL)
		fatal("bad argument to getname()", 0, 0);

	/* use the raw PT template class name */

	if (dem(s, &d, dbuf) < 0)
		fatal("call to demangler failed for %s", s, 0);
	if (d.cl != NULL) {
		dem_printcl(d.cl, pbuf);
		return d.cl->rname;
	}

	/* or the function template name */

	else {
		dem_print(&d, pbuf);
		return s;
	}
}

/* make a filename from a template class name */
void makefnam(ptr, s, bbuf)
char* ptr;
char* s;
char* bbuf;
{
#ifndef LONG_NAMES
	char buf1[MAXPATH];
	char buf[MAXLINE];
	unsigned long h;
	unsigned long hs;
	FILE* fp;
#endif
	if (ptr == NULL || !*ptr || s == NULL || !*s || bbuf == NULL)
		fatal("bad argument to makefnam()", 0, 0);

#ifdef LONG_NAMES
	strcpy(bbuf, s);
#else
	h = hash(s, 90000049L);		/* allow room for overflows */
	hs = h;

	/* find a hash code that is unused */

	for (;;) {
		sprintf(bbuf, "pt%08lu", h);
		sprintf(buf1, "%s/%s%s", ptr, bbuf, HC_EXT);
		if ((fp = fopen(buf1, "r")) == NULL) {
			if (facc(buf1))
				fatal("cannot open hash conflict file %s for reading", buf1, 0);
			break;
		}
		fgets(buf, MAXLINE, fp);
		fclose(fp);
		buf[strlen(buf) - 1] = 0;
		if (!strcmp(buf, s))
			break;
		h++;
	}
	del_file(buf1);
	if ((fp = fopen(buf1, "w")) == NULL)
		fatal("could not create hash collision file %s", buf1, 0);
	fprintf(fp, "%s\n", s);
	fclose(fp);

	if (h != hs)
		fprintf(stderr, "%s warning: filename hash conflict for %s, using pt%08lu instead of pt%08lu\n", PROGNAME, s, h, hs);
#endif
}

/************************* NAME MAPPING STUFF *************************/

typedef struct Tfile Tfile;
struct Tfile {
	char* name;
	Slist files;
	Slist bns;
	Tfile* next;
	int fnum;
	char type;
};

/* get a new file table */
Tfile** tf_get()
{
	Tfile** p;
	int i;

	p = (Tfile**)gs(sizeof(Tfile*) * TFTABSIZE);
	for (i = 0; i < TFTABSIZE; i++)
		p[i] = NULL;
	return p;
}

static int fcnt = 0;

/* delete a file/type table */
void tf_del(tftab)
Tfile** tftab;
{
	Tfile* p;
	Tfile* r;
	int i;

	if (tftab == NULL)
		fatal("bad argument to tf_del()", 0, 0);

	for (i = 0; i < TFTABSIZE; i++) {
		p = tftab[i];
		tftab[i] = NULL;
		while (p != NULL) {
			fs(p->name);
			slp_del(&p->files);
			slp_del(&p->bns);
			r = p;
			p = p->next;
			fs(r);
		}
	}
}

/* read a map file */
void tf_read(f, tftab)
char* f;
Tfile** tftab;
{
	FILE* fp;
	char buf[MAXMAPL];
	char lbuf[MAXLINE];
	int ln;
	char* s;
	Slist* slp;
	Tfile* p;
	int h;
	char* t;
	Slist* slp2;
	int err;
	char* start;
	int len;
	Slist tab;
	int flag;
	int indx;
	int len2;
	int nbl;
	char* t2;

	if (f == NULL || !*f || tftab == NULL)
		fatal("bad argument to tf_read()", 0, 0);
	if ((fp = fopen(f, "r")) == NULL) {

		/* may be defmap file, where failure to */
		/* read should only be a warning */

		if (!fcnt) {
			fcnt++;
			fprintf(stderr, "%s warning: cannot open map file %s for reading\n", PROGNAME, f);
			return;
		}
		fatal("cannot open map file %s for reading", f, 0);
	}

	ln = 0;
	slp = NULL;
	tab.n = 0;
	nbl = 0;

	/* iterate over lines of file */

	while (fgets(buf, MAXMAPL, fp) != NULL) {
		ln++;
		len = strlen(buf) - 1;
		if (buf[len] != '\n') {
			sprintf(lbuf, "%d", ln);
			fatal("missing newline at line %s of map file %s", lbuf, f);
		}
		buf[len] = 0;
		s = buf;
		while (*s && *s <= ' ')
			s++;
		if (!*s)
			continue;
		nbl++;

		/* might be string table */

		if (!strcmp(s, "@tab") && nbl == 1) {
			flag = 0;
			while (fgets(buf, MAXMAPL, fp) != NULL) {
				ln++;
				len2 = strlen(buf) - 1;
				if (buf[len2] != '\n') {
					sprintf(lbuf, "%d", ln);
					fatal("missing newline at line %s of map file %s", lbuf, f);
				}
				buf[len2] = 0;
				if (!strcmp(buf, "@etab")) {
					flag = 1;
					break;
				}
				slp_add(&tab, buf);
			}
			if (!flag || !tab.n) {
				sprintf(lbuf, "%d", ln);
				fatal("incorrectly formatted map file %s at line %s", f, lbuf);
			}
			continue;
		}

		/* check formatting */

		err = 0;
		if (s[0] == '@') {
			if (strncmp(s, "@dec", 4) && strncmp(s, "@def", 4))
				err = 1;
			if (!err && s[4] != ' ')
				err = 1;
			if (!err) {
				t = s + 5;
				while (*t && *t <= ' ')
					t++;
			}
			if (!err && !isalpha(*t) && *t != '_')
				err = 1;
			if (err) {
				sprintf(lbuf, "%d", ln);
				fatal("incorrectly formatted map file %s at line %s", f, lbuf);
			}
		}

		/* start of a new type */

		if (s[0] == '@') {
			if (slp != NULL && slp->n < 1) {
				sprintf(lbuf, "%d", ln);
				fatal("no files found for type in map file %s at line %s", f, lbuf);
			}
			p = (Tfile*)gs(sizeof(Tfile));
			p->type = s[3];

			/* get type name */

			t = s + 5;
			while (*t && *t <= ' ')
				t++;
			t2 = t;
			while (isalnum(*t) || *t == '_')
				t++;
			if (*t)
				*t++ = 0;
			p->name = copy(t2);
			slp = &p->files;
			slp->n = 0;

			/* get list of basenames */

			slp2 = &p->bns;
			slp2->n = 0;
			for (;;) {
				while (*t && *t <= ' ')
					t++;
				if (!*t)
					break;
				start = t;
				while (*t && *t > ' ')
					t++;
				if (*t)
					*t++ = 0;
				if (start[0] == '\\' && !start[1]) {
					if (fgets(buf, MAXMAPL, fp) == NULL)
						fatal("premature EOF for map file %s", f, 0);
					ln++;
					len = strlen(buf) - 1;
					if (buf[len] != '\n') {
						sprintf(lbuf, "%d", ln);
						fatal("missing newline at line %s of map file %s", lbuf, f);
					}
					buf[len] = 0;
					t = buf;
				}
				else {
					if (start[0] == '@' && isdigit(start[1])) {
						indx = atoi(start + 1);
						if (indx < 0 || indx >= tab.n) {
							sprintf(lbuf, "%d", ln);
							fatal("index for string table out of range at line %s of map file %s", lbuf, f);
						}
						slp_add(slp2, tab.ptrs[indx]);
					}
					else {
						slp_add(slp2, start);
					}
				}
			}
			p->fnum = fcnt;
			h = (int)hash(p->name, TFTABSIZE);
			p->next = tftab[h];
			tftab[h] = p;			
		}

		/* another file for preceding type */

		else {
			if (slp == NULL) {
				sprintf(lbuf, "%d", ln);
				fatal("incorrectly formatted map file %s at line %s", f, lbuf);
			}
			slp_add(slp, s);
		}
	}
	if (slp != NULL && slp->n < 1)
		fatal("premature EOF for map file %s", f, 0);

	fclose(fp);
	fcnt++;
	slp_del(&tab);
}

/* get the type names used in a parameter list */
void getcl(clp, slp)
DEMCL* clp;
Slist* slp;
{
	DEMARG* p;
	DEMCL** r;
	void getfcl();

	if (clp == NULL || slp == NULL)
		fatal("bad argument to getcl()", 0, 0);

	/* add the class name */

	slp_adduniq(slp, clp->name);

	/* might be a template class;  recurse if so */

	if (clp->clargs != NULL)
		getfcl(clp->clargs, slp);
}

/* get the type names used in a function template parameter list */
void getfcl(p, slp)
DEMARG* p;
Slist* slp;
{
	DEMCL** r;

	if (p == NULL || slp == NULL)
		fatal("bad argument to getfcl()", 0, 0);

	while (p != NULL) {
		if (p->clname != NULL)
			getcl(p->clname, slp);
		if (p->mname != NULL) {
			r = p->mname;
			while (*r != NULL) {
				getcl(*r, slp);
				r++;
			}
		}
		if (p->func != NULL)
			getfcl(p->func, slp);
		if (p->ret != NULL)
			getfcl(p->ret, slp);
		p = p->next;
	}
	
}

/* check whether an include exists */
int check_incl(incl, f, quot)
Slist* incl;
char* f;
char quot;
{
	char fbuf[MAXPATH];
	int i;
	char* s;

	if (f == NULL || !*f || incl == NULL || (quot != '"' && quot != '<'))
		fatal("bad argument to check_incl()", 0, 0);

	/* hard path or current directory */

	if ((quot == '"' || f[0] == '/') && facc(f))
		return 0;

	/* go through list */

	for (i = 0; i < incl->n; i++) {
		s = incl->ptrs[i];
		if (s[0] != '-' || s[1] != 'I')
			continue;
		s += 2;
		sprintf(fbuf, "%s/%s", s, f);
		if (facc(fbuf))
			return 0;
	}

	return -1;
}

/* add to a unique include file list */
void addincl(slp, tfp, ext, incl)
Slist* slp;
Slist* tfp;
char* ext;
Slist* incl;
{
	int i;
	char buf[MAXPATH];
	char buf2[MAXPATH];
	char c1;
	char c2;

	if (slp == NULL || tfp == NULL || tfp->n < 1)
		fatal("bad argument to addincl()", 0, 0);

	for (i = 0; i < tfp->n; i++) {
		strcpy(buf2, tfp->ptrs[i]);
		c1 = '"';
		c2 = '"';
		if (buf2[0] == '"') {
			buf2[strlen(buf2) - 1] = 0;
			strcpy(buf, buf2 + 1);
		}
		else if (buf2[0] == '<') {
			buf2[strlen(buf2) - 1] = 0;
			strcpy(buf, buf2 + 1);
			c1 = '<';
			c2 = '>';
		}
		else {
			strcpy(buf, buf2);
		}
		if (ext != NULL)
			replace_ext(buf, ext);
		if (ext != NULL && incl != NULL && !strcmp(ext, SRC_EXT) &&
		    check_incl(incl, buf, c1) < 0) {
			fprintf(stderr, "%s warning: template definition file %s not found\n", PROGNAME, buf);
		}
		else {
			sprintf(buf2, "#include %c%s%c", c1, buf, c2);
			slp_adduniq(slp, buf2);
		}
	}
}

/* validate a file basename */
int valid_bn(r)
Tfile* r;
{
	int i;
	Slist* slp;

	if (r == NULL)
		fatal("bad argument to valid_bn()", 0, 0);

	slp = &r->bns;
	for (i = 0; i < slp->n; i++) {
		if (flookup(slp->ptrs[i], ftable) != NULL)
			return 1;
	}

	return 0;
}

/* read and sort a list of map file names from a directory */
void read_mname(dir, list, defmap)
char* dir;
Slist* list;
char* defmap;
{
	char cmd[MAXCBUF];
	FILE* fp;
	char fbuf[MAXPATH];
	int i;
	int j;
	char* t;
	int n;

	if (dir == NULL || !*dir || list == NULL ||
	    defmap == NULL || !*defmap)
		fatal("bad argument to read_mname()", 0, 0);

	sprintf(fbuf, "%s/%s", dir, defmap);
	slp_add(list, fbuf);

	/* do an "ls" on the repository */

	sprintf(cmd, "%s %s/nmap* 2>/dev/null", LSPATH, dir);
	if ((fp = popen(cmd, "r")) == NULL)
		fatal("popen() failed on %s while reading map files", cmd, 0);
	while (fgets(fbuf, MAXPATH, fp) != NULL) {
		fbuf[strlen(fbuf) - 1] = 0;
		slp_add(list, fbuf);
	}
	pclose(fp);

	/* reverse the list */

	n = (list->n - 1) / 2;
	i = 1;
	j = list->n - 1;
	while (n-- > 0) {
		t = list->ptrs[i];
		list->ptrs[i] = list->ptrs[j];
		list->ptrs[j] = t;
		i++;
		j--;
	}
}

/* tlookup() helper function */
int tlookup_help(t, r, pdp, pip, minfile, maxfile, bnflag)
char* t;
Tfile* r;
Tfile** pdp;
Tfile** pip;
int minfile;
int maxfile;
int bnflag;
{
	Tfile* pd;
	Tfile* pi;
	int pdnum;
	int pinum;
	int err;

	if (t == NULL || !*t || pdp == NULL || pip == NULL ||
	    minfile < 0 || maxfile < 0 || minfile > maxfile ||
	    bnflag < 0 || bnflag > 1)
		fatal("bad argument to tlookup_help()", 0, 0);

	pd = NULL;
	pi = NULL;
	pdnum = -1;
	pinum = -1;
	err = 0;

	for (; r != NULL; r = r->next) {

		/* check file number */

		if (r->fnum < minfile || r->fnum > maxfile)
			continue;

		/* check name */

		if (strcmp(r->name, t))
			continue;

		/* check basenames flag */

		if ((bnflag && !valid_bn(r)) || (!bnflag && r->bns.n))
			continue;

		if (r->type == 'c') {
			pd = (pd == NULL ? r : pd);

			/* check for duplicates */

			if (pdnum == r->fnum) {
				err = 1;
				break;
			}
			pdnum = r->fnum;
		}
		else {
			pi = (pi == NULL ? r : pi);

			/* check for duplicates */

			if (pinum == r->fnum) {
				err = 1;
				break;
			}
			pinum = r->fnum;
		}
	}

	if (err)
		return -2;
	if (pd == NULL && pi == NULL)
		return -1;

	*pdp = pd;
	*pip = pi;
	return 0;
}

/* look up a type in one set of map files */
int tlookup(t, tlp, fflag, tftab, incl)
char* t;
Slist* tlp;
char fflag;
Tfile** tftab;
Slist* incl;
{
	Tfile* pd;
	Tfile* pi;
	int h;
	int err;

	if (t == NULL || !*t || tlp == NULL ||
	    (fflag != 'c' && fflag != 'f') || tftab == NULL)
		fatal("bad argument to tlookup()", 0, 0);

	/* find the type */

	h = (int)hash(t, TFTABSIZE);

	/* check nmapXXX files with basenames */

	err = tlookup_help(t, tftab[h], &pd, &pi, 1, 9999, 1);

	/* check nmapXXX files without basenames */

	if (err == -1)
		err = tlookup_help(t, tftab[h], &pd, &pi, 1, 9999, 0);

	/* check default map file with basenames */

	if (err == -1)
		err = tlookup_help(t, tftab[h], &pd, &pi, 0, 0, 1);

	/* check default map file without basenames */

	if (err == -1)
		err = tlookup_help(t, tftab[h], &pd, &pi, 0, 0, 0);

	/* check out errors */

	if (err == -2)
		fatal("type %s defined twice in map files", t, 0);
	if (err == -1)
		return -1;

	/* format to instantiation file */

	if (fflag == 'f') {
		addincl(tlp, pd == NULL ? &pi->files : &pd->files,
		    pd == NULL ? INC_EXT : NULL, incl);
		addincl(tlp, pi == NULL ? &pd->files : &pi->files,
		    pi == NULL ? SRC_EXT : NULL, incl);
	}
	else {
		addincl(tlp, pd == NULL ? &pi->files : &pd->files,
		    pd == NULL ? INC_EXT : NULL, incl);
	}

	return 0;
}

static Tfile*** tf_cache = NULL;

/* look up a type in map files */
int tlookup2(name, tlist, t, tfp, dlp, defmap, incl)
char* name;
Slist* tlist;
char t;
Tfile** tfp;
Slist* dlp;
char* defmap;
Slist* incl;
{
	int j;
	int k;
	Slist* mlp;
	Slist ml;

	if (name == NULL || !*name || tlist == NULL ||
	    (t != 'c' && t != 'f') || tfp == NULL || dlp == NULL ||
	    defmap == NULL || !*defmap)
		fatal("bad argument to tlookup2()", 0, 0);

	mlp = &ml;

	if (tlookup(name, tlist, t, tfp, incl)) {

		/* allocate cache */

		if (tf_cache == NULL) {
			tf_cache = (Tfile***)gs(sizeof(Tfile**) * dlp->n);
			for (j = 0; j < dlp->n; j++)
				tf_cache[j] = NULL;
		}


		/* look in other repositories */

		for (j = 1; j < dlp->n; j++) {
			if (tf_cache[j] == NULL) {
				tf_cache[j] = tf_get();
				mlp->n = 0;
				read_mname(dlp->ptrs[j], mlp, defmap);
				fcnt = 0;
				for (k = 0; k < mlp->n; k++)
					tf_read(mlp->ptrs[k], tf_cache[j]);
				slp_del(mlp);
			}
			if (!tlookup(name, tlist, t, tf_cache[j], incl))
				break;
		}

		/* anything found? */

		if (j == dlp->n)
			return -1;
	}

	return 0;
}

/* get list of undefined symbols */
void getund(slp, tfp, dlp, defmap, rawn)
Slist* slp;
Tfile** tfp;
Slist* dlp;
char* defmap;
Slist* rawn;
{
	int i;
	Symbol* sp;
	Slist tlist;
	char f[MAXLINE];
	char dbuf[MAXDBUF];
	DEM d;

	if (slp == NULL || tfp == NULL || dlp == NULL ||
	    defmap == NULL || !*defmap || rawn == NULL)
		fatal("bad argument to getund()", 0, 0);

	tlist.n = 0;

	for (i = 0; i < SYMTSIZE; i++) {
		sp = symtab[i];
		while (sp != NULL) {

			/* must be undefined and PT-ish */

			if ((sp->type == 'U' || sp->type == 'C') &&
			    ispt(sp->name, f)) {

				/* check for possible function template */

				if (!*f || !tlookup2(f, &tlist, 'c', tfp, dlp, defmap, NULL))
					slp_add(slp, sp->name);
				slp_del(&tlist);
			}
			sp = sp->next;
		}
	}

	slp_sort(slp);

	/* create list of raw names for each symbol */

	for (i = 0; i < slp->n; i++) {
		if (dem(slp->ptrs[i], &d, dbuf) < 0)
			fatal("call to demangler failed for %s", slp->ptrs[i], 0);
		if (d.cl != NULL && d.cl->rname != NULL)
			slp_add(rawn, d.cl->rname);
		else
			slp_add(rawn, " ");
	}
}

/****************************** INSTANTIATION ******************************/

/* write set of includes to instantiation file */
void doincl(fp, name, tfp, dlp, defmap, incl, tlist)
FILE* fp;
char* name;
Tfile** tfp;
Slist* dlp;
char* defmap;
Slist* incl;
Slist* tlist;
{
	DEM d;
	int i;
	Slist clist;
	char t;
	char buf[MAXLINE];
	char* n;
	Slist dlist;
	char dbuf[MAXDBUF];

	if (fp == NULL || name == NULL || !*name ||
	    tfp == NULL || dlp == NULL || defmap == NULL || !*defmap ||
	    incl == NULL || tlist == NULL)
		fatal("bad argument to doincl()", 0, 0);

	clist.n = 0;

	/* figure out needed types */

	if (dem(name, &d, dbuf) < 0)
		fatal("call to demangler failed for %s", name, 0);
	if (d.cl != NULL) {
		getcl(d.cl, &clist);
		if (d.args != NULL)
			getfcl(d.args, &clist);
		if (d.fargs != NULL)
			getfcl(d.fargs, &clist);
	}
	else {
		slp_add(&clist, d.f);
		getfcl(d.args, &clist);
		if (d.fargs != NULL)
			getfcl(d.fargs, &clist);
	}

	/* look them up */

	for (i = 0; i < clist.n; i++) {
		n = clist.ptrs[i];
		t = (i == 0 ? 'f' : 'c');
		if (tlookup2(n, tlist, t, tfp, dlp, defmap, incl)) {

			/* forwardize type */

			if (t == 'c') {
				sprintf(buf, "struct %s;", n);
				slp_adduniq(tlist, buf);
			}

			/* a template type, can't forwardize */

			else {
				sprintf(buf, "%s%s", n, INC_EXT);
				dlist.n = 0;
				slp_add(&dlist, buf);
				addincl(tlist, &dlist, NULL, incl);
				addincl(tlist, &dlist, SRC_EXT, incl);
				slp_del(&dlist);
				fprintf(stderr, "%s warning: assuming template type %s found in %s%s and %s%s\n", PROGNAME, n, n, INC_EXT, n, SRC_EXT); 
			}
		}
	}

	/* add to instantiation file */

	for (i = 0; i < tlist->n; i++)
		fprintf(fp, "%s\n", tlist->ptrs[i]);

	slp_del(&clist);
}

/* handle literals for an instantiation */
void do_lit(fp, clp)
FILE* fp;
DEMCL* clp;
{
	DEMARG* p;
	DEMCL** r;
	char* s;
	char abuf[MAXLINE];
	char* t;

	if (fp == NULL || clp == NULL)
		fatal("bad argument to do_lit()", 0, 0);

	p = clp->clargs;

	/* step through arguments */

	while (p != NULL) {
		if (p->lit != NULL) {
			s = p->lit;

			/* might be pointer to member */

			while (*s) {
				if (*s == ':')
					break;
				s++;
			}

			/* skip pointers to members and integral literals */

			if (!*s && (isalpha(p->lit[0]) || p->lit[0] == '_')) {
				s = p->mods;

				/* remove one level of pointer */

				while (*s && *s != 'P')
					s++;
				if (*s == 'P') {
					if (s > p->mods && s[-1] == 'C')
						strcpy(s - 1, s + 1);
					else
						strcpy(s, s + 1);
				}

				/* format the result as an extern */

				t = p->lit;
				p->lit = NULL;
				dem_printarg(p, abuf, 1);
				p->lit = t;
				fputs("extern ", fp);
				s = abuf;
				while (*s != '@') {
					fputc(*s, fp);
					s++;
				}
				if (s == abuf || !*s)
					fatal("missing @ in do_lit()", 0, 0);
				if (isalnum(s[-1]) || s[-1] == '_')
					fputc(' ', fp);
				fprintf(fp, "%s%s;\n", p->lit, s + 1);
			}
		}

		/* recurse if need be */

		if (p->clname != NULL)
			do_lit(fp, p->clname);
		if (p->mname != NULL) {
			r = p->mname;
			while (*r != NULL) {
				do_lit(fp, *r);
				r++;
			}
		}
		p = p->next;
	}
}

/* force an instantiation */
void doforce(fp, raw, fmt)
FILE* fp;
char* raw;
char* fmt;
{
	DEM d;
	char dbuf[MAXDBUF];
	char abuf[MAXLINE];
	int i;
	DEMARG* p;
	int n;
	char buf[MAXLINE];

	if (fp == NULL || raw == NULL || !*raw || fmt == NULL || !*fmt)
		fatal("bad argument to doforce()", 0, 0);

	if (dem(raw, &d, dbuf) < 0)
		fatal("call to demangler failed for %s", raw, 0);

	/* for template classes, use typedef */

	if (d.cl != NULL) {
		do_lit(fp, d.cl);
		fprintf(fp, "typedef %s _dummy_;\n", fmt);
	}

	/* for function templates, fake a call to the function */

	else {
		fprintf(fp, "static void _dummy_(");
		p = d.args;
		n = 0;

		/* print the arguments to the dummy function */

		while (p != NULL) {
			n++;
			dem_printarg(p, abuf, 1);
			i = 0;
			while (abuf[i] && abuf[i] != '@')
				i++;
			if (!i || !abuf[i])
				fatal("missing @ in doforce()", 0, 0);
			abuf[i] = 0;
			fprintf(fp, "%s%s%sa%d%s", n > 1 ? ", " : "", abuf, " ", n, abuf + i + 1);
			p = p->next;
		}
		fprintf(fp, ")\n{\n");

		/* print the call to the function */

		dem_printfunc(&d, buf);
		fprintf(fp, "\t%s(", buf);
		for (i = 1; i <= n; i++)
			fprintf(fp, "%sa%d", i > 1 ? ", " : "", i);
		fprintf(fp, ");\n}\n");
	}
}

/******************* COMPILE AN INSTANTIATION FILE ********************/

/* format a compile invocation */
void fcomp(ccpath, incl, ccopts, type, buf, dip)
char* ccpath;
Slist* incl;
Slist* ccopts;
char* type;
char* buf;
Slist* dip;
{
	char fbuf[MAXPATH];
	FILE* fp;
	char dinst[MAXPATH];
	int i;

	if (ccpath == NULL || !*ccpath || incl == NULL ||
	    type == NULL || buf == NULL)
		fatal("bad argument to fcomp()", 0, 0);

	if (dip == NULL) {
		dinst[0] = 0;
	}

	/* directed instantiation */

	else {
		sprintf(fbuf, "%s/instfile", tdir);
		sprintf(dinst, "+T%s", fbuf);
		if ((fp = fopen(fbuf, "w")) == NULL)
			fatal("cannot open instantiation file %s for writing", fbuf, 0);
		for (i = 0; i < dip->n; i++)
			fprintf(fp, "%s\n", dip->ptrs[i]);
		fclose(fp);
	}

	sprintf(buf, "%s %s %s ", ccpath, type, dinst);

	/* includes */

	for (i = 0; i < incl->n; i++) {
		strcat(buf, incl->ptrs[i]);
		strcat(buf, " ");
	}

	/* other options */

	if (ccopts == NULL)
		return;
	for (i = 0; i < ccopts->n; i++) {
		strcat(buf, ccopts->ptrs[i]);
		strcat(buf, " ");
	}
}

#ifdef CHECKSUM
unsigned long docs(ptr, bbuf, cpp, incl, ccopts)
char* ptr;
char* bbuf;
char* cpp;
Slist* incl;
Slist* ccopts;
{
	char cmd[MAXCBUF];
	char cmd2[MAXCBUF];
	FILE* fp;
	unsigned long cs;
	char buf1[MAXPATH];
	char buf2[MAXPATH];
	int flag;
	char buf[MAXCPP];
	char* s;

	if (ptr == NULL || !*ptr || bbuf == NULL || !*bbuf ||
	    cpp == NULL || !*cpp || incl == NULL || ccopts == NULL)
		fatal("bad argument to docs()", 0, 0);

	/* format */

	fcomp(cpp, incl, NULL, "", cmd, NULL);
	sprintf(buf1, "%s%s", bbuf, SRC_EXT);
	sprintf(buf2, "%s/%s", ptr, buf1);
	sprintf(cmd2, "%s %s", cmd, buf1);

	/* establish pipe */

	flag = (icmp(ptr, ".") == 0);
	if (flag)
		pushstring(buf1);
	signal_block(handlesig);
	if (flag && cp(buf2, buf1) < 0)
		fatal("could not copy %s --> %s", buf2, buf1);
	if ((fp = fast_popen(cmd2, "r")) == NULL) {
		if (flag)
			popstring();
		fatal("fast_popen() failed on %s while taking checksum", buf2, 0);
	}

	/* read pipe, ignoring #line and leading whitespace */

	cs = 0;
	while (fgets(buf, MAXCPP, fp) != NULL) {
		s = buf;
		while (*s && *s <= ' ')
			s++;
		if (!*s || *s == '#')
			continue;
		while (*s)
			cs = cs * 271 + *s++;
	}
	fast_pclose(fp);
	signal_unblock();
	if (flag)
		popstring();

	/* return checksum, with 0 as special case */

	if (cs)
		return cs;
	else
		return 1L;
}

/* check timestamps */
int check_time(r, bbuf, incl, tlist, wbuf2)
char* r;
char* bbuf;
Slist* incl;
Slist* tlist;
char* wbuf2;
{
	char ibuf[MAXPATH];
	char ibuf2[MAXPATH];
	int i;
	int j;
	unsigned long tso;
	unsigned long tsi;
	char obj[MAXPATH];
	char c;
	int hard;

	if (r == NULL || !*r || bbuf == NULL || !*bbuf ||
	    incl == NULL || tlist == NULL || wbuf2 == NULL)
		fatal("bad argument to check_time()", 0, 0);

	wbuf2[0] = 0;

	/* get time stamp for object */

	sprintf(obj, "%s/%s%s", r, bbuf, OBJ_EXT);
	if ((tso = timestamp(obj)) == 0)
		return 0;

	/* go through list of includes and check against object */

	for (i = 0; i < tlist->n; i++) {
		if (strncmp(tlist->ptrs[i], "#include ", 9))
			continue;
		c = tlist->ptrs[i][9];
		strcpy(ibuf, tlist->ptrs[i] + 10);
		ibuf[strlen(ibuf) - 1] = 0;
		hard = 0;
		if (ibuf[0] == '/')
			hard = 1;

		/* local directories and hard paths */

		if ((c == '"' || hard) && (tsi = timestamp(ibuf))) {
			if (tsi < tso)
				continue;
			strcpy(wbuf2, ibuf);
			return 0;
		}

		/* go through -I paths */

		for (j = 0; j < incl->n; j++) {
			if (incl->ptrs[j][0] != '-' || incl->ptrs[j][1] != 'I')
				continue;
			if (hard)
				strcpy(ibuf2, ibuf);
			else
				sprintf(ibuf2, "%s/%s", incl->ptrs[j] + 2, ibuf);
			tsi = timestamp(ibuf2);
			if (tsi == 0)
				continue;
			if (tsi >= tso) {
				strcpy(wbuf2, ibuf);
				return 0;
			}
			else {
				break;
			}
		}
	}

	return 1;
}

/* check dependencies */
int dodep(ptr, bbuf, ccpath, incl, ccopts, obuf, dlp, dip, cs, tflag, wbuf, cpp, tlist, ptoflag)
char* ptr;
char* bbuf;
char* ccpath;
Slist* incl;
Slist* ccopts;
char* obuf;
Slist* dlp;
Slist* dip;
unsigned long* cs;
int tflag;
char* wbuf;
char* cpp;
Slist* tlist;
int ptoflag;
{
	char fbuf[MAXPATH];
	char fbuf2[MAXPATH];
	unsigned long csold;
	char csbuf[25];
	char csbuf2[25];
	FILE* fp;
	int i;
	char* r;
	char buf[MAXLINE];
	Slist cslist;
	int ret;
	Slist nlist;
	int j;
	char wbuf2[MAXPATH];
	Fname* fnp;

	if (ptr == NULL || !*ptr || bbuf == NULL || !*bbuf ||
	    ccpath == NULL || !*ccpath || incl == NULL ||
	    ccopts == NULL || obuf == NULL || !*obuf ||
	    dlp == NULL || dip == NULL || tflag < 0 || tflag > 1 ||
	    wbuf == NULL || cpp == NULL || !*cpp || tlist == NULL ||
	    ptoflag < 0 || ptoflag > 1)
		fatal("bad argument to dodep()", 0, 0);

	csbuf[0] = 0;
	cslist.n = 0;
	nlist.n = 0;
	*cs = 0;

	/* iterate over repositories */

	for (i = 0; i < dlp->n; i++) {
		r = dlp->ptrs[i];

		/* check for object */

		sprintf(fbuf, "%s/%s%s", r, bbuf, OBJ_EXT);
		if (!facc(fbuf)) {
			strcpy(wbuf, "missing object");
			continue;
		}

		/* maybe -pto; if on list then known to be out of date */

		sprintf(fbuf2, "%s%s", bbuf, OBJ_EXT);
		if (ptoflag && (fnp = flookup(fbuf2, otable)) != NULL) {
			strcpy(wbuf, "-pto indicates out of date");
			fs(fnp->name);		/* can be out of */
			fnp->name = copy(" ");	/* date only once */
			continue;
		}

		/* check for instantiation file */

		sprintf(fbuf2, "%s/%s%s", r, bbuf, SRC_EXT);
		if (!facc(fbuf2)) {
			strcpy(wbuf, "missing instantiation file");
			continue;
		}

		/* see if needed members are a subset of */
		/* those stored with checksum */

		sprintf(fbuf, "%s/%s%s", r, bbuf, CS_EXT);
		if ((fp = fopen(fbuf, "r")) == NULL) {
			strcpy(wbuf, "checksum missing or unreadable");
			continue;
		}
		if (fgets(csbuf2, 25, fp) == NULL) {
			strcpy(wbuf, "checksum unreadable");
			fclose(fp);
			continue;
		}
		while (fgets(buf, MAXLINE, fp) != NULL) {
			buf[strlen(buf) - 1] = 0;
			slp_add(&cslist, buf);
		}
		fclose(fp);
		if (cslist.n == 1 && !strcmp(cslist.ptrs[0], "@alltc"))
			ret = 1;
		else
			ret = slp_subset(dip, &cslist, &nlist);

		/* might have to handle exclude list for @data */

		if (!ret &&
		    nlist.n == 1 && !strncmp(nlist.ptrs[0], "@data", 5) &&
		    !strncmp(dip->ptrs[0], "@data", 5) &&
		    !strncmp(cslist.ptrs[0], "@data", 5)) {
			char* ptr[2];
			Slist list[2];
			Slist scr;
			char* s;
			char* t;
			int k;
			ptr[0] = copy(dip->ptrs[0]);
			ptr[1] = copy(cslist.ptrs[0]);
			scr.n = 0;
			for (k = 0; k <= 1; k++) {
				s = ptr[k];
				list[k].n = 0;
				for (;;) {
					t = s;
					while (*s && *s > ' ')
						s++;
					if (*s)
						*s++ = 0;
					if (!*t)
						break;
					slp_add(&list[k], t);
					while (*s && *s <= ' ')
						s++;
				}
			}
			ret = slp_subset(&list[0], &list[1], &scr);
			slp_del(&list[0]);
			slp_del(&list[1]);
			slp_del(&scr);
			fs(ptr[0]);
			fs(ptr[1]);
		}
		slp_del(&cslist);
		if (!ret) {
			strcpy(wbuf, "members missing: ");
			for (j = 0; j < nlist.n; j++) {
				strcat(wbuf, j > 0 ? "," : "");
				strcat(wbuf, nlist.ptrs[j]);
			}
			slp_del(&nlist);
			continue;
		}
		slp_del(&nlist);

		/* might be -pto */

		if (ptoflag)
			break;

		/* might have specified to check timestamps only */

		if (tflag) {
			if (check_time(r, bbuf, incl, tlist, wbuf2)) {
				break;
			}
			else {
				sprintf(wbuf, "header changed: %s", wbuf2);
				continue;
			}
		}

		/* compare checksums */

		csold = docs(r, bbuf, cpp, incl, ccopts, dip);
		if (i == 0)
			*cs = csold;
		sprintf(csbuf, "%lx\n", csold);
		if (strcmp(csbuf, csbuf2)) {
			strcpy(wbuf, "checksum mismatch");
			continue;
		}

		break;
	}

	/* found a matching one */

	if (i < dlp->n) {
		sprintf(obuf, "%s/%s%s", r, bbuf, OBJ_EXT);
		strcpy(wbuf, "OK");
		return 0;
	}

	return 1;
}

#else

/* rebuild cache */
void build_cache(cfn, incl, cpp, id, ptr, bbuf)
char* cfn;
Slist* incl;
char* cpp;
char* id;
char* ptr;
char* bbuf;
{
	char cmd[MAXCBUF];
	char cmd2[MAXCBUF];
	int flag;
	char buf1[MAXPATH];
	char buf2[MAXPATH];
	char buf[MAXCPP];
	char prev[MAXPATH];
	Slist flist;
	FILE* fp;
	FILE* fp2;
	int i;
	char* s;
	char* t;
	long len;

	if (cfn == NULL || !*cfn || incl == NULL || cpp == NULL || !*cpp ||
	   id == NULL || ptr == NULL || !*ptr || bbuf == NULL || !*bbuf)
		fatal("bad argument to build_cache()", 0, 0);

	flist.n = 0;

	/* format cpp line */

	fcomp(cpp, incl, NULL, "", cmd, NULL);
	sprintf(buf1, "%s%s", bbuf, SRC_EXT);
	sprintf(buf2, "%s/%s", ptr, buf1);
	sprintf(cmd2, "%s %s", cmd, buf1);

	/* establish pipe */

	flag = (icmp(ptr, ".") == 0);
	if (flag)
		pushstring(buf1);
	signal_block(handlesig);
	if (flag && cp(buf2, buf1) < 0)
		fatal("could not copy %s --> %s", buf2, buf1);
	if ((fp = fast_popen(cmd2, "r")) == NULL) {
		if (flag)
			popstring();
		fatal("fast_popen() failed on %s while building cache", buf2, 0);
	}

	/* read pipe and look for #lines */

	prev[0] = 0;
	while (fgets(buf, MAXCPP, fp) != NULL) {
		s = buf;

		/* skip # or #line */

		while (*s && *s <= ' ')
			s++;
		if (*s != '#')
			continue;
		t = s + 1;
		while (*s && *s > ' ')
			s++;
		if (*s)
			*s++ = 0;
		if (*t > ' ' && strcmp(t, "line"))
			continue;

		/* skip line number */

		while (*s && *s <= ' ')
			s++;
		if (!isdigit(*s))
			continue;
		while (*s && *s > ' ')
			s++;

		/* get filename */

		while (*s && *s <= ' ')
			s++;
		if (*s == '"')
			s++;
		if (!*s)
			continue;
		t = s;
		while (*s && *s > ' ' && *s != '"')
			s++;
		*s = 0;

		/* check if we've already seen this file */

		if (!strcmp(t, prev))
			continue;
		else
			strcpy(prev, t);

		/* screen out instantiation file */

		while (s > t && *s != '.')
			s--;
		if (s > t && !strcmp(s, SRC_EXT) && icmp(t, buf1))
			continue;

		/* add to unique list */

		slp_adduniq(&flist, t);
	}
	fast_pclose(fp);
	signal_unblock();
	if (flag)
		popstring();

	/* rewrite cache */

	del_file(cfn);
	if ((fp2 = fopen(cfn, "w")) == NULL)
		fatal("cannot open header cache %s for writing", cfn, 0);
	fprintf(fp2, "%s\n", id);
	len = strlen(id);
	slp_sort(&flist);
	for (i = 0; i < flist.n; i++) {
		fprintf(fp2, "%s\n", flist.ptrs[i]);
		len += strlen(flist.ptrs[i]);
	}
	fprintf(fp2, "%ld\n", len);
	fclose(fp2);
	slp_del(&flist);

	progress("rebuilt header file cache", 0, 0);
}

/* check timestamps */
int check_time(r, bbuf, incl, cpp, wbuf)
char* r;
char* bbuf;
Slist* incl;
char* cpp;
char* wbuf;
{
	int redo_flag;
	int comp_flag;
	char cfn[MAXPATH];
	char ifn[MAXPATH];
	char ofn[MAXPATH];
	unsigned long cts;
	unsigned long its;
	unsigned long hts;
	unsigned long ots;
	char fbuf[MAXCPP];
	int first_flag;
	FILE* fp;
	char id[MAXCBUF];
	int i;
	char* s;
	long len;
	int saw_check;
	int id_flag;

	if (r == NULL || !*r || bbuf == NULL || !*bbuf || incl == NULL ||
	    cpp == NULL || !*cpp || wbuf == NULL)
		fatal("bad argument to check_time()", 0, 0);

	redo_flag = 0;
	comp_flag = 0;
	saw_check = 0;
	wbuf[0] = 0;
	id_flag = 0;

	/* format I/D */

	id[0] = 0;
	for (i = 0; i < incl->n; i++) {
		strcat(id, incl->ptrs[i]);
		strcat(id, " ");
	}

	/* format cache and instantiation filenames */

	sprintf(cfn, "%s/%s%s", r, bbuf, HE_EXT);
	sprintf(ifn, "%s/%s%s", r, bbuf, SRC_EXT);
	sprintf(ofn, "%s/%s%s", r, bbuf, OBJ_EXT);

	/* check cache existence and date */

	cts = timestamp(cfn);
	its = timestamp(ifn);
	ots = timestamp(ofn);
	if (!its)
		fatal("instantiation file %s missing", ifn, 0);
	if (!ots)
		fatal("object file %s missing", ofn, 0);

	if (!cts || its > cts) {
		redo_flag = 1;
		id_flag = 1;
	}

	/* go through cache */

	else {
		if ((fp = fopen(cfn, "r")) == NULL)
			fatal("cannot open header cache %s for reading", cfn, 0);
		first_flag = 1;
		len = 0;
		while (fgets(fbuf, MAXCPP, fp) != NULL) {
			fbuf[strlen(fbuf) - 1] = 0;

			/* might be check field at end of cache */

			if (isdigit(fbuf[0])) {
				s = fbuf;
				while (isdigit(*s))
					s++;
				if (!*s) {
					saw_check = 1;
					if (len != atoi(fbuf))
						redo_flag = 1;
					break;
				}
			}

			len += strlen(fbuf);

			/* check out -I/-D line */

			if (first_flag) {
				first_flag = 0;
				if (strcmp(fbuf, id)) {
					redo_flag = 1;
					id_flag = 1;
					strcpy(wbuf, "-I/-D changed");
					break;
				}
				continue;
			}

			/* check times */

			hts = timestamp(fbuf);
			if (!hts) {
				redo_flag = 1;
				break;
			}
			if (hts >= cts) {
				redo_flag = 1;
				break;
			}
			if (hts >= ots) {
				comp_flag = 1;
				sprintf(wbuf, "header changed: %s", fbuf);
			}
		}
		fclose(fp);
	}

	/* see what we've got */

	if (redo_flag || !saw_check)
		build_cache(cfn, incl, cpp, id, r, bbuf);
	if (!redo_flag && saw_check && !comp_flag && !id_flag)
		return 1;
	if (!redo_flag && saw_check && comp_flag && !id_flag)
		return 0;
	if (id_flag)
		return 0;

	/* go through rebuilt cache */

	if ((fp = fopen(cfn, "r")) == NULL)
		fatal("cannot open header cache %s for reading", cfn, 0);
	first_flag = 1;
	saw_check = 0;
	while (fgets(fbuf, MAXCPP, fp) != NULL) {
		fbuf[strlen(fbuf) - 1] = 0;

		/* might be check field at end of cache */

		if (isdigit(fbuf[0])) {
			s = fbuf;
			while (isdigit(*s))
				s++;
			if (!*s) {
				saw_check = 1;
				if (len != atoi(fbuf))
					redo_flag = 1;
				break;
			}
		}
		len += strlen(fbuf);

		/* check out -I/-D line */

		if (first_flag) {
			first_flag = 0;
			if (strcmp(fbuf, id))
				fatal("mismatched -I/-D in header cache %s", cfn, 0);
			continue;
		}

		/* check times */

		hts = timestamp(fbuf);
		if (!hts)
			fatal("header file %s does not exist", fbuf, 0);
		if (hts >= ots) {
			fclose(fp);
			sprintf(wbuf, "header changed: %s", fbuf);
			return 0;
		}
	}
	fclose(fp);
	if (!saw_check)
		fatal("header cache %s corrupted", cfn, 0);

	return 1;
}

/* check dependencies */
int dodep(ptr, bbuf, ccpath, incl, ccopts, obuf, dlp, dip, cs, tflag, wbuf, cpp, tlist, ptoflag)
char* ptr;
char* bbuf;
char* ccpath;
Slist* incl;
Slist* ccopts;
char* obuf;
Slist* dlp;
Slist* dip;
unsigned long* cs;
int tflag;
char* wbuf;
char* cpp;
Slist* tlist;
int ptoflag;
{
	char fbuf[MAXPATH];
	char fbuf2[MAXPATH];
	char csbuf[MAXLINE];
	FILE* fp;
	int i;
	char* r;
	char buf[MAXLINE];
	Slist cslist;
	int ret;
	Slist nlist;
	int j;
	Fname* fnp;
	int k;
	char id[MAXCBUF];
	char cfn[MAXPATH];

	if (ptr == NULL || !*ptr || bbuf == NULL || !*bbuf ||
	    ccpath == NULL || !*ccpath || incl == NULL ||
	    ccopts == NULL || obuf == NULL || !*obuf ||
	    dlp == NULL || dip == NULL || tflag < 0 || tflag > 1 ||
	    wbuf == NULL || cpp == NULL || !*cpp || tlist == NULL ||
	    ptoflag < 0 || ptoflag > 1)
		fatal("bad argument to dodep()", 0, 0);

	csbuf[0] = 0;
	cslist.n = 0;
	nlist.n = 0;
	*cs = 0;

	/* iterate over repositories */

	for (i = 0; i < dlp->n; i++) {
		r = dlp->ptrs[i];

		/* check for instantiation file */

		sprintf(fbuf2, "%s/%s%s", r, bbuf, SRC_EXT);
		if (!facc(fbuf2)) {
			strcpy(wbuf, "missing instantiation file");
			continue;
		}

		/* check for object */

		sprintf(fbuf, "%s/%s%s", r, bbuf, OBJ_EXT);
		if (!facc(fbuf)) {
			strcpy(wbuf, "missing object");

			/* build header cache */

			id[0] = 0;
			for (k = 0; k < incl->n; k++) {
				strcat(id, incl->ptrs[k]);
				strcat(id, " ");
			}
			sprintf(cfn, "%s/%s%s", r, bbuf, HE_EXT);
			build_cache(cfn, incl, cpp, id, r, bbuf);
			continue;
		}

		/* check timestamps */

		if (tflag && !check_time(r, bbuf, incl, cpp, wbuf))
			continue;

		/* maybe -pto; if on list then known to be out of date */

		sprintf(fbuf2, "%s%s", bbuf, OBJ_EXT);
		if (ptoflag && (fnp = flookup(fbuf2, otable)) != NULL) {
			strcpy(wbuf, "-pto indicates out of date");
			fs(fnp->name);		/* can be out of */
			fnp->name = copy(" ");	/* date only once */
			continue;
		}

		/* see if needed members are a subset of */
		/* those stored with checksum */

		sprintf(fbuf, "%s/%s%s", r, bbuf, CS_EXT);
		if ((fp = fopen(fbuf, "r")) == NULL) {
			strcpy(wbuf, "checksum missing or unreadable");
			continue;
		}
		while (fgets(buf, MAXLINE, fp) != NULL) {
			buf[strlen(buf) - 1] = 0;
			slp_add(&cslist, buf);
		}
		fclose(fp);
		if (cslist.n == 1 && !strcmp(cslist.ptrs[0], "@alltc"))
			ret = 1;
		else
			ret = slp_subset(dip, &cslist, &nlist);

		/* might have to handle exclude list for @data */

		if (!ret &&
		    nlist.n == 1 && !strncmp(nlist.ptrs[0], "@data", 5) &&
		    !strncmp(dip->ptrs[0], "@data", 5) &&
		    !strncmp(cslist.ptrs[0], "@data", 5)) {
			char* ptrs[2];
			Slist list[2];
			Slist scr;
			char* s;
			char* t;
			ptrs[0] = copy(dip->ptrs[0]);
			ptrs[1] = copy(cslist.ptrs[0]);
			scr.n = 0;
			for (k = 0; k <= 1; k++) {
				s = ptrs[k];
				list[k].n = 0;
				for (;;) {
					t = s;
					while (*s && *s > ' ')
						s++;
					if (*s)
						*s++ = 0;
					if (!*t)
						break;
					slp_add(&list[k], t);
					while (*s && *s <= ' ')
						s++;
				}
			}
			ret = slp_subset(&list[0], &list[1], &scr);
			slp_del(&list[0]);
			slp_del(&list[1]);
			slp_del(&scr);
			fs(ptrs[0]);
			fs(ptrs[1]);
		}
		slp_del(&cslist);
		if (!ret) {
			strcpy(wbuf, "members missing: ");
			for (j = 0; j < nlist.n; j++) {
				strcat(wbuf, j > 0 ? "," : "");
				strcat(wbuf, nlist.ptrs[j]);
			}
			slp_del(&nlist);
			continue;
		}
		slp_del(&nlist);

		break;
	}

	/* found a matching one */

	if (i < dlp->n) {
		sprintf(obuf, "%s/%s%s", r, bbuf, OBJ_EXT);
		strcpy(wbuf, "OK");
		return 0;
	}

	return 1;
}
#endif

/* compile an instantiation file */
int docomp(ptr, bbuf, ccpath, incl, ccopts, dip, prevcs, cpp)
char* ptr;
char* bbuf;
char* ccpath;
Slist* incl;
Slist* ccopts;
Slist* dip;
unsigned long prevcs;
char* cpp;
{
	char cmd[MAXCBUF];
	int ret;
	char obuf[MAXPATH];
	char ddcbuf[MAXPATH];
	char ptrbuf[MAXPATH];
	char cbuf[MAXCBUF];
	unsigned long cs;
	FILE* fp;
	int i;
	char type[MAXLINE];
	char buf1[MAXPATH];
	char buf2[MAXPATH];
	char buf[MAXPATH];
	int flag;

	if (ptr == NULL || !*ptr || bbuf == NULL || !*bbuf ||
	    ccpath == NULL || !*ccpath || incl == NULL ||
	    ccopts == NULL || dip == NULL ||
	    cpp == NULL || !*cpp)
		fatal("bad argument to docomp()", 0, 0);

	/* format for compiling */

	sprintf(buf, "%s/%s%s", ptr, bbuf, OBJ_EXT);
	del_file(buf);
	if (plusi)
		sprintf(type, "-c +i -ptr%s", ptr);
	else
		sprintf(type, "-c -ptr%s", ptr);
	fcomp(ccpath, incl, ccopts, type, cbuf, dip);
	sprintf(buf1, "%s%s", bbuf, SRC_EXT);
	sprintf(buf2, "%s/%s", ptr, buf1);
	sprintf(cmd, "%s %s", cbuf, buf1);
	progress("CC line is: %s", cmd, 0);

	/* compile */

	sprintf(obuf, "%s%s", bbuf, OBJ_EXT);
	sprintf(ddcbuf, "%s%s", bbuf, DDC_EXT);
	flag = (icmp(ptr, ".") == 0);
	if (flag) {
		pushstring(buf1);
		pushstring(obuf);
		pushstring(ddcbuf);
	}
	signal_block(handlesig);
	if (flag && cp(buf2, buf1) < 0)
		fatal("could not copy file %s --> %s", buf2, buf1);
	ret = fast_system(cmd);
	if (ret) {
		if (flag) {
			popstring();
			popstring();
			popstring();
		}
		return -1;
	}

	/* move the resulting object file to the repository */

	sprintf(ptrbuf, "%s/%s", ptr, obuf);
	if (flag && mv(obuf, ptrbuf, ".", ptr) < 0) {
		popstring();
		popstring();
		popstring();
		fatal("could not move %s --> %s", obuf, ptrbuf);
	}

	/* move the ..c file to the repository if +i */

	if (plusi) {
		sprintf(ptrbuf, "%s/%s", ptr, ddcbuf);
		if (flag && mv(ddcbuf, ptrbuf, ".", ptr) < 0) {
			popstring();
			popstring();
			popstring();
			fatal("could not move %s --> %s", ddcbuf, ptrbuf);
		}
	}
	signal_unblock();
	if (flag) {
		popstring();
		popstring();
		popstring();
	}

	/* write its checksum there */

	sprintf(buf, "%s/%s%s", ptr, bbuf, CS_EXT);
#ifdef CHECKSUM
	if (prevcs)
		cs = prevcs;
	else
		cs = docs(ptr, bbuf, cpp, incl, ccopts, dip);
#endif
	del_file(buf);
	if ((fp = fopen(buf, "w")) == NULL)
		fatal("cannot open checksum file %s for writing", buf, 0);
#ifdef CHECKSUM
	fprintf(fp, "%lx\n", cs);
#endif
	for (i = 0; i < dip->n; i++)
		fprintf(fp, "%s\n", dip->ptrs[i]);
	fclose(fp);

	return 0;
}

/******************** DIRECTED INSTANTIATION LISTS ********************/

typedef struct Dilist Dilist;
struct Dilist {
	char* name;
	Slist list;
	Dilist* next;
};

static Dilist* ditab[DISIZE];

/* look up a list */
Slist* di_lookup(s)
char* s;
{
	int h;
	Dilist* p;

	if (s == NULL || !*s)
		fatal("bad argument to di_lookup()", 0, 0);

	/* look up in list */

	p = ditab[h = (int)hash(s, DISIZE)];
	while (p != NULL && strcmp(s, p->name))
		p = p->next;

	/* create if not found */

	if (p == NULL) {
		p = (Dilist*)gs(sizeof(Dilist));
		p->name = copy(s);
		p->list.n = 0;
		p->next = ditab[h];
		ditab[h] = p;
	}

	return &p->list;
}

/* delete the lists */
void di_del()
{
	int i;
	Dilist* p;
	Dilist* r;

	for (i = 0; i < DISIZE; i++) {
		p = ditab[i];
		while (p != NULL) {
			fs(p->name);
			slp_del(&p->list);
			r = p;
			p = p->next;
			fs(r);
		}
		ditab[i] = NULL;
	}
}

/* get defined data members for a class */
void get_defdata(rn, out, obuf)
char* rn;
char* out;
char* obuf;
{
	int i;
	Symbol* sp;
	DEM d;
	char dbuf[MAXDBUF];
	Slist list;
	char t;

	if (rn == NULL || !*rn || out == NULL || obuf == NULL || !*obuf)
		fatal("bad argument to get_defdata()", 0, 0);

	list.n = 0;
	slp_add(&list, "@data");
	out[0] = 0;

	/* get the list */

	for (i = 0; i < SYMTSIZE; i++) {
		sp = symtab[i];
		while (sp != NULL) {

			/* find data/virtual members for template class */

			/* only retrieve those members that are defined, */
			/* not in the repository, and members of template */
			/* classes */

			t = sp->type;
			if (t == 'T' || t == 'D' || t == 'B') {
				if (sp->rn == NULL) {
				    if (!dem(sp->name, &d, dbuf) &&
					d.cl != NULL &&
					d.vtname == NULL &&
					d.cl->rname != NULL) {
						sp->rn = copy(d.cl->rname);
						sp->f = copy(d.f);
					}
					else {
						sp->rn = copy(" ");
					}
				}
				if (sp->rn != NULL &&
				    !STRCMP(sp->rn, rn) &&
				    STRCMP(sp->file, obuf) &&
				    (strcmp(basename(sp->file, 0),
					    basename(obuf, 0)) || !sp->rep))
					slp_adduniq(&list, sp->f);
			}
			sp = sp->next;
		}
	}

	/* sort and format */

	slp_sort(&list);
	for (i = 0; i < list.n; i++) {
		if (i > 0)
			strcat(out, " ");
		strcat(out, list.ptrs[i]);
	}
	slp_del(&list);
}

/**********************************************************************/

/* add to list of link objects */
void add_lnobj(olp, obuf)
Slist* olp;
char* obuf;
{
	char obase[MAXPATH];
	int i;

	if (olp == NULL || obuf == NULL || !*obuf)
		fatal("bad argument to add_lnobj()", 0, 0);

	strcpy(obase, basename(obuf, 0));
	for (i = 0; i < olp->n; i++) {
		if (!strcmp(obase, basename(olp->ptrs[i], 0))) {
			fs(olp->ptrs[i]);
			olp->ptrs[i] = copy(obuf);
			return;
		}
	}

	slp_adduniq(olp, obuf);
}

main(argc, argv)
int argc;
char** argv;
{
	Slist nores;
	Slist* noresp;
	Slist tlist;
	char wbuf[MAXLINE*4];
	int tflag;
	char* memname;
	int k;
	int split_flag;
	char buf3[MAXPATH];
	Slist dilist2;
	Slist* dip2;
	unsigned long cs;
	int df;
	Slist* dip;
	int all_flag;
	int ret;
	Obj* p;
	int i;
	int niter;
	Slist sc;
	Slist so;
	Slist* scp;
	Slist* sop;
	Slist nl;
	Slist* nlp;
	Slist pl;
	Slist* plp;
	Slist rl;
	Slist* rlp;
	Slist olist;
	Slist* olp;
	Slist mlist;
	Slist* mlp;
	char pbuf[MAXLINE];
	char bbuf[MAXPATH];
	char sbuf[MAXPATH];
	char obuf[MAXPATH];
	char dbuf[MAXDBUF];
	char prbuf[MAXLINE];
	DEM d;
	FILE* fp;
	char* ccpath;
	Slist* incl;
	Slist incl2;
	char* infile;
	char* outfile;
	char fbuf[MAXPATH];
	FILE* fpin;
	FILE* fpout;
	char* ptr;
	char* defmap;
	Slist* ccopts;
	Slist ccoptslist;
	char* ptrlist;
	Slist dl;
	Slist* dlp;
	Tfile** tfp;
	int j;
	Slist ndiplist;
	Slist* ndip;
	char databuf[MAXLINE*4];
	char* fincl;
	char* cpp;
	Slist rawn;
	Slist* rawnp;
	int del_flag;
	int ptoflag;
	char* opts;

	start_time = time(0);

	/* check out arguments */

	all_flag = 0;
	split_flag = 0;
	tflag = 0;
	ptoflag = 0;
	while (argc >= 2 && !strncmp(argv[1], "-pt", 3)) {
		switch (argv[1][3]) {
			case 't':
				tflag = 1;
				break;
			case 'v':
				verbose = 1;
				break;
			case 'i':
				plusi = 1;
				break;
			case 'a':
				all_flag = 1;
				break;
			case 's':
				split_flag = 1;
				break;
			case 'o':
				if ((fp = fopen(&argv[1][4], "r")) == NULL)
					fatal("bad file argument to -pto", 0, 0);
				del_flag = 0;
				while (fgets(buf3, MAXPATH, fp) != NULL) {
					buf3[strlen(buf3) - 1] = 0;
					if (!strcmp(buf3, "@delete"))
						del_flag = 1;
					else
						fadd(buf3, otable);
				}
				fclose(fp);
				if (del_flag)
					del_file(&argv[1][4]);
				ptoflag = 1;
				break;
			default:
				break;		/* ignore bad options */
		}
		argc--;
		argv++;
	}
#ifndef CHECKSUM
	if (tflag)
		fprintf(stderr, "%s warning: -ptt option the default\n", PROGNAME);
	tflag = 1;
#endif
	if (argc != 11)
		fatal("usage is: [-pta] [-ptopathname] [-pts] [-ptv] repository temp_dir CC_path includes input_file output_file defmap CCopts ptrlist cpp_path", 0, 0);
	ptr = argv[1];
	tdir = argv[2];
	ccpath = argv[3];
	fincl = argv[4];
	infile = argv[5];
	outfile = argv[6];
	defmap= argv[7];
	opts = argv[8];
	ptrlist = argv[9];
	cpp = argv[10];
	if (all_flag && split_flag)
		fatal("-pta and -pts cannot both be specified", 0, 0);

	progress2();

	/* set umask */

	set_umask(ptr);

	/* lock repository */

	lock_rep(ptr);
	progress("locked repository", 0, 0);

	/* basic initialization */

	niter = 0;
	scp = &sc;
	rawnp = &rawn;
	sop = &so;
	nlp = &nl;
	plp = &pl;
	rlp = &rl;
	olp = &olist;
	mlp = &mlist;
	dlp = &dl;
	ccopts = &ccoptslist;
	ndip = &ndiplist;
	incl = &incl2;
	noresp = &nores;
	rawnp->n = 0;
	scp->n = 0;
	sop->n = 0;
	nlp->n = 0;
	plp->n = 0;
	rlp->n = 0;
	olp->n = 0;
	mlp->n = 0;
	dlp->n = 0;
	ndip->n = 0;
	incl->n = 0;
	tlist.n = 0;
	noresp->n = 0;
	ccopts->n = 0;

	tfp = tf_get();

	/* read in include list */

	if ((fp = fopen(fincl, "r")) == NULL)
		fatal("cannot open include list %s for reading", fincl, 0);
	while (fgets(buf3, MAXPATH, fp) != NULL) {
		buf3[strlen(buf3) - 1] = 0;
		slp_add(incl, buf3);
	}
	fclose(fp);

	/* read in options */

	if ((fp = fopen(opts, "r")) == NULL)
		fatal("cannot open options list %s for reading", opts, 0);
	while (fgets(buf3, MAXPATH, fp) != NULL) {
		buf3[strlen(buf3) - 1] = 0;
		slp_add(ccopts, buf3);
	}
	fclose(fp);

	/* read in map file names */

	read_mname(ptr, mlp, defmap);
	progress("read name map file list", 0, 0);

	/* read in list of repositories */

	slp_add(dlp, ptr);
	if ((fp = fopen(ptrlist, "r")) == NULL)
		fatal("cannot open repository list %s for reading", ptrlist, 0);
	while (fgets(fbuf, MAXPATH, fp) != NULL) {
		fbuf[strlen(fbuf) - 1] = 0;
		slp_add(dlp, fbuf);
	}
	fclose(fp);

	/* read in all objects and archives */

	if ((fpin = fopen(infile, "r")) == NULL)
		fatal("cannot open input file %s for reading", infile, 0);
	while (fgets(fbuf, MAXPATH, fpin) != NULL) {
		fbuf[strlen(fbuf) - 1] = 0;
		p = readnm(fbuf);
		addfile(p, fbuf, 0);

		/* add to file list for later type lookup */

		fadd(basename(fbuf, 1), ftable);
	}
	fclose(fpin);
	progress("read in all objects and archives", 0, 0);

loop:
	/* top of main loop - abandon hope all ye who enter here! */

	niter++;

	/* read in map files */

	tf_del(tfp);
	fcnt = 0;
	for (i = 0; i < mlp->n; i++)
		tf_read(mlp->ptrs[i], tfp);

	/* simulate linking */

	symtab_del();
	fakelink();
	progress("finished link simulation to pick up undefineds", 0, 0);

	/* get undefined PT symbols and bail out if none */

	if (niter & 0x1) {
		scp = &sc;
		sop = &so;
	}
	else {
		scp = &so;
		sop = &sc;
	}
	slp_del(scp);
	slp_del(rawnp);
	slp_del(noresp);
	getund(scp, tfp, dlp, defmap, rawnp);
	if (scp->n == 0)
		goto done;

	/* maybe in an infinite loop */

	if (niter > 1 && slp_eq(scp, sop, noresp)) {
		fprintf(stderr, "%s: unresolveable template symbols are:\n", PROGNAME);
		for (i = 0; i < noresp->n; i++) {
			ret = dem(noresp->ptrs[i], &d, dbuf);
			if (ret < 0)
				strcpy(prbuf, noresp->ptrs[i]);
			else
				dem_print(&d, prbuf);
			fprintf(stderr, "\t%s\n", prbuf);
		}
		fatal("template symbols cannot be resolved", 0, 0);
	}

	/* make list of unique template class or function names */

	slp_del(nlp);
	slp_del(plp);
	slp_del(rlp);
	for (i = 0; i < scp->n; i++) {
		if (slp_adduniq(nlp, getname(scp->ptrs[i], pbuf, dbuf))) {
			slp_add(plp, pbuf);
			slp_add(rlp, scp->ptrs[i]);
		}
	}
	progress("made list of unique template class names", 0, 0);

	/* write instantiation files and do compile */

	for (i = 0; i < nlp->n; i++) {
		progress2();
		progress("now looking at template %s", plp->ptrs[i], 0);

		/* figure out the target list for directed instantiation */

		dip = di_lookup(nlp->ptrs[i]);
		if (dem(rlp->ptrs[i], &d, dbuf) < 0)
			fatal("call to demangler failed for %s", rlp->ptrs[i], 0);
		if (d.cl)
			df = 1;
		else
			df = 2;
		if (df == 2) {

			/* function templates */

			slp_adduniq(dip, "@ft");
		}
		else if (all_flag) {

			/* whole template class */

			slp_adduniq(dip, "@alltc");
		}
		else {

			/* some members of template class */

			slp_del(ndip);
			if (!split_flag)
				strcpy(buf3, nlp->ptrs[i]);
			else
				sprintf(buf3, "%s____data", nlp->ptrs[i]);
			makefnam(ptr, buf3, bbuf);
			sprintf(obuf, "%s/%s%s", ptr, bbuf, OBJ_EXT);
			get_defdata(nlp->ptrs[i], databuf, obuf);

			/* find needed members */

			for (j = 0; j < scp->n; j++) {
				if (STRCMP(rawnp->ptrs[j], nlp->ptrs[i]))
					continue;
				if (dem(scp->ptrs[j], &d, dbuf) < 0)
					fatal("call to demangler failed for %s", scp->ptrs[j], 0);
				if (d.args != NULL) {
					if (slp_adduniq(dip, d.f))
						slp_adduniq(ndip, d.f);
				}
			}

			/* handle data list - replace @data with new list */

			for (j = 0; j < dip->n; j++) {
				if (!strncmp(dip->ptrs[j], "@data", 5)) {
					fs(dip->ptrs[j]);
					dip->ptrs[j] = copy(databuf);
					break;
				}
			}
			if (j == dip->n)
				slp_add(dip, databuf);
			slp_add(ndip, databuf);
		}
		slp_sort(dip);

		/* handle split case */

		k = 0;
		dilist2.n = 0;
loop2:

		/* make the name of the instantiation file */

		if (df == 2 || !split_flag) {
			strcpy(buf3, nlp->ptrs[i]);
			memname = "";
		}
		else {
			if (!strncmp(ndip->ptrs[k], "@data", 5))
				memname = "__data";
			else
				memname = ndip->ptrs[k];
			sprintf(buf3, "%s__%s", nlp->ptrs[i], memname);
		}
		makefnam(ptr, buf3, bbuf);
		if (split_flag && df == 1) {
			dip2 = &dilist2;
			slp_del(dip2);
			slp_add(dip2, ndip->ptrs[k]);
		}
		else {
			dip2 = dip;
		}

		/* make the file */

		sprintf(sbuf, "%s/%s%s", ptr, bbuf, SRC_EXT);
		sprintf(buf3, "%s/_instfile", ptr);
		del_file(buf3);
		if ((fp = fopen(buf3, "w")) == NULL)
			fatal("cannot open instantiation file %s for writing", sbuf, 0);
		if (*memname)
			fprintf(fp, "/* %s::%s */\n\n", plp->ptrs[i], memname);
		else
			fprintf(fp, "/* %s */\n\n", plp->ptrs[i]);
		slp_del(&tlist);
		doincl(fp, rlp->ptrs[i], tfp, dlp, defmap, incl, &tlist);
		fprintf(fp, "\n");
		doforce(fp, rlp->ptrs[i], plp->ptrs[i]);
		fclose(fp);

		/* only update the file if it has changed */

		if (!facc(sbuf) || !fcmp(buf3, sbuf)) {
			if (mv(buf3, sbuf, ptr, ptr) < 0)
				fatal("could not move file %s --> %s", buf3, sbuf);
		}

		progress("wrote instantiation file %s%s", bbuf, SRC_EXT);

		/* do dependency check */

		sprintf(obuf, "%s/%s%s", ptr, bbuf, OBJ_EXT);
		ret = dodep(ptr, bbuf, ccpath, incl, ccopts, obuf, dlp, dip2, &cs, tflag, wbuf, cpp, &tlist, ptoflag);
		sprintf(buf3, "%s%s", bbuf, SRC_EXT);
		progress("did dependency check on %s (%s)", buf3, wbuf);

		/* add object file to global list */

		add_lnobj(olp, obuf);

		/* recompile if necessary */

		if (ret) {
			if (docomp(ptr, bbuf, ccpath, incl, ccopts, dip2, cs, cpp) < 0)
				fatal("compile of instantiation file %s failed", sbuf, 0);
			progress("compiled %s%s", bbuf, SRC_EXT);
		}

		/* add file to global symbol table list */

		p = readnm(obuf);
		addfile(p, obuf, 1);
		fadd(basename(obuf, 1), ftable);
		progress("added symbols from %s%s to symbol table", bbuf, OBJ_EXT);

		/* handle split case */

		if (split_flag && ++k < ndip->n)
			goto loop2;
	}
	progress2();
	goto loop;

done:

	/* cleanup - mainly to track memory leaks better */

	symtab_del();
	di_del();
	ftab_del(ftable);
	ftab_del(otable);
	obj_del();
	tf_del(tfp);
	fs(tfp);
	if (tf_cache != NULL) {
		for (i = 1; i < dlp->n; i++)
			tf_del(tf_cache[i]);
		fs(tf_cache);
		tf_cache = NULL;
	}
	slp_del(&tlist);
	slp_del(&dilist2);
	slp_del(&sc);
	slp_del(&so);
	slp_del(&nl);
	slp_del(&pl);
	slp_del(&rl);
	slp_del(&mlist);
	slp_del(&incl2);
	slp_del(&dl);
	slp_del(&ndiplist);
	slp_del(&rawn);
	slp_del(&nores);
	slp_del(&ccoptslist);

	/* unlock repository */

	unlock_rep(ptr);
	progress("unlocked repository", 0, 0);

	/* all done, pass out results */

	if ((fpout = fopen(outfile, "w")) == NULL)
		fatal("cannot open output file %s for writing", outfile, 0);
	for (i = 0; i < olp->n; i++)
		fprintf(fpout, "%s\n", olp->ptrs[i]);
	slp_del(&olist);
	fclose(fpout);

	progress2();

	exit(0);	
}
