#ifndef lint
static char *SccsId = "@(#)main.c 4.6 (TU-Delft) 05/18/92";
#endif
/*
 *      Copyright 1990 by Delft University of Technology (DUT),
 *                      Delft, The Netherlands.
 * 
 *                        All Rights Reserved
 * 
 * Permission to use, copy, modify, and distribute this software and
 * its  documentation  without  fee,  is  hereby  granted  for  non-
 * commercial purposes  only,  provided  that  the  above  copyright
 * notice  appear  in all copies and that both that copyright notice
 * and this permission notice appear  in  supporting  documentation,
 * and  that the name of DUT not be used in advertising or publicity
 * pertaining to distribution  of  the  software  without  specific,
 * written prior permission.
 * 
 * THE DUT DISCLAIMS ALL WARRANTIES WITH REGARD  TO  THIS  SOFTWARE,
 * INCLUDING  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
 * IN NO EVENT SHALL THE DUT BE LIABLE FOR ANY SPECIAL, INDIRECT  OR
 * CONSEQUENTIAL  DAMAGES  OR  ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION  OF  CONTRACT,
 * NEGLIGENCE  OR  OTHER  TORTIOUS  ACTION,  ARISING  OUT  OF  OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 * 
 * Author(s):
 *           S. de Graaf
 *           T.G.R. van Leuken
 *           P. Kist
 * 
 * 
 * Contact address:
 * 
 * 
 * Dimes Design and Test Centre     phone: + 31 (15) 78 1459
 * Delft University of Technology   fax:   + 31 (15) 62 3271
 * P.O. Box 5053 		    email: nelsis@dutentb.tudelft.nl
 * Feldmannweg 17, 2600 GB Delft    The Netherlands
 */
#include "incl.h"

#define BUFLEN	80
#define HASHSIZE 128

struct na_elmt *Hashold[HASHSIZE];
struct na_elmt *Hashnew[HASHSIZE];

struct stat stat_buf;		/* file status buffer */
struct mc_elmt *inst_mc_elmt ();
struct mo_elmt *molist = 0;
struct ic_elmt *iclist = 0;
char       **LClist;		/* Local Cell list */
IMPCELL    **IClist;		/* Imported Cell list */
FILE        *fp_file;
DM_CELL     *ckey;
DM_PROJECT  *dmproject;
char    outputfile[DM_MAXNAME + 6];
char   *bmlist;
char    buf[BUFLEN];
char    buf1[DM_MAXLAY + 1];
char    buf2[DM_MAXLAY + 1];
int     layset[DM_MAXNOMASKS];
char   *lay[DM_MAXNOMASKS];
char  **ldmlay;

int     nolays;
int     level  = 1; /* current level */
int     llevel = 1; /* lowest level */
int     o_mode = 0;
int     r_mode = 0;
int     t_mode = 0;
int     u_mode = 0;
int     ut_mode = 0;
int     v_mode = 0;
int     usage = 0;
int     Pmode = LDM;
double  resol;

char   *begC = ":: "; /* begin of comment line */
char   *endC = "\n";  /*  end  of comment line */
char   *argv0 = "getlld";	/* program name */

main (argc, argv)
int   argc;
char *argv[];
{
    int     sig_handler ();
    register int    i, j, iarg;
    register char  *s;
    register struct na_elmt *p;
    DM_PROCDATA *process;	/* ptr to process info */
    FILE   *fp_bml;
    int     c;
    int     f_mode = 0;
    int     help = 0;
    int     o_opt = 0;
    char   *suffix;
    char   *topcell;

    if (argv0 = rindex (argv[0], '/')) ++argv0;
    else argv0 = argv[0];

    suffix = argv0 + 1;
    if (strcmp (argv0, "xldm") == 0) { /* process as xldm */
	Pmode = LDM;
	o_mode = 0;
    }
    else if (strcmp (argv0, "xcif") == 0) { /* process as xcif */
	Pmode = CIF;
	o_mode = 1;
	begC = "(";
	endC = ");\n";
    }
    else if (strcmp (argv0, "xcmk") == 0) { /* process as xcmk */
	Pmode = CMK;
    }
    else if (strcmp (argv0, "xdll") == 0) { /* process as xdll */
	Pmode = DLL;
	begC = "*/ ";
    }
    else {
	PE "To use this program, the program must be installed\n");
	PE "as: \"xldm\" or \"xcmk\" or \"xcif\" or \"xdll\".\n");
	exit (1);
    }

    for (iarg = 1; iarg < argc && argv[iarg][0] == '-'; ++iarg) {
	j = iarg;
	for (i = 1; argv[j][i] != '\0'; ++i) {
	    switch (argv[j][i]) {
		case 'h': 
		    ++help;
		    break;
		case 'f': 
		    f_mode = ++iarg;
		    break;
		case 'm': 
		    if (Pmode <= CIF) goto unknown;
		    bmlist = argv[++iarg];
		    break;
		case 'o': 
		    if (Pmode >= CMK) goto unknown;
		    ++o_opt;
		    break;
		case 'r': 
		    ++r_mode;
		    break;
		case 't': 
		    if (Pmode <= CIF) goto unknown;
		    t_mode = 0;
		    while (c = argv[j][++i]) {
			if (c >= '0' && c <= '9') {
			    t_mode = 10 * t_mode + (c - '0');
			}
			else break;
		    }
		    if (!t_mode) ++t_mode;
		    ++ut_mode;
		    --i;
		    break;
		case 'u': 
		    if (Pmode <= CIF) goto unknown;
		    ++u_mode;
		    ++ut_mode;
		    break;
		case 'v': 
		    ++v_mode;
		    break;
		default: 
unknown:
		    ++usage;
		    PE "%s: -%c: unknown option\n", argv0, argv[j][i]);
	    }
	}
    }

    if (help) goto helpme;

    if (f_mode) {
	if (*argv[f_mode])
	    PE "%s: -f: output to file: %s\n", argv0, argv[f_mode]);
	else
	    PE "%s: -f: output to stdout\n", argv0);
    }
    if (bmlist) {
	if (*bmlist)
	    PE "%s: -m: using basic mask list: %s\n", argv0, bmlist);
	else
	    PE "%s: -m: using no basic mask list\n", argv0);
    }
    if (o_opt) {
	if (Pmode == LDM) {
	    o_mode = 1;
	    PE "%s: -o: no cell origin mode\n", argv0);
	}
	else {
	    o_mode = 0;
	    PE "%s: -o: cell origin mode\n", argv0);
	}
    }
    if (r_mode) PE "%s: -r: only root of cell\n", argv0);

    if (t_mode) {
	if (t_mode > 3) {
	    PE "%s: -t: cell names are truncated to %d chars\n",
		argv0, t_mode);
	}
	else {
	    ++usage;
	    PE "%s: -tN: no truncation (N must be >= 4)\n", argv0);
	}
    }
    if (u_mode) PE "%s: -u: uppercase mode\n", argv0);
    if (v_mode) PE "%s: -v: verbose mode\n", argv0);

    if (argc <= iarg) {
	PE "%s: no cell name given\n", argv0);
	++usage;
    }
    if (argc > iarg + 1) {
	PE "%s: too many arguments given\n", argv0);
	++usage;
    }
    if (usage) {
helpme:
        PE "\nUsage: %s [-%s] [-f ofile] cell\n\n", argv0,
	    (Pmode <= CIF) ? "horv" : "m mlist] [-hurv] [-tN");
	if (help) {
	    PE "-h   this help menu\n");
	    PE "-f   use next arg. as output filename\n");
	    if (Pmode <= CIF)
		PE "-o   %scell origin mode\n", Pmode == LDM ? "no " : "");
	    else {
		PE "-m   use next arg. as masklist file\n");
		PE "-tN  truncate cell names to N (>= 4)\n");
		PE "-u   uppercase mode\n");
	    }
	    PE "-r   only the root of the cell\n");
	    PE "-v   verbose mode\n\n");
	}
	else {
	    PE "      (use option -h for help)\n\n");
	}
	exit (1);
    }

    signal (SIGHUP, SIG_IGN);	/* ignore hangup signal */
    signal (SIGQUIT, SIG_IGN);
    signal (SIGTERM, sig_handler);

    if (signal (SIGINT, SIG_IGN) != SIG_IGN)
	signal (SIGINT, sig_handler);

    topcell = argv[iarg];
    if (f_mode) {
	strcpy (outputfile, argv[f_mode]);
    }
    else {
	sprintf (outputfile, "%s.%s", topcell, suffix);
    }

    if (*outputfile)
	if (stat (outputfile, &stat_buf) != -1)
	    error (5, outputfile);

    dmInit (argv0);
    dmproject = dmOpenProject (DEFAULT_PROJECT, PROJ_READ);
    process = (DM_PROCDATA *) dmGetMetaDesignData (PROCESS, dmproject);
    ldmlay = process -> mask_name;
    nolays = process -> nomasks;

    inst_cells ();

#if NCF_RELEASE > 400
    if (s = index (topcell, '#')) *s = '\0';
#endif
    if (!(p = findold (topcell, ut_mode))) error (4, topcell);
#if NCF_RELEASE > 400
    if (s) *s = '#';
#endif

    if (Pmode >= CMK) { /* CMK || DLL */
	if (!bmlist) {
	    if (Pmode == CMK) {
		bmlist = (char *) dmGetMetaDesignData (PROCPATH,
		    dmproject, "bmlist.cmk");
		if (stat (bmlist, &stat_buf) == -1) {
		    PE "%s: warning: cannot read file: %s\n",
			argv0, bmlist);
		    bmlist = "";
		}
	    }
	    else bmlist = "";
	}

	if (*bmlist) {
	    if (!(fp_bml = fopen (bmlist, "r"))) error (11, bmlist);

	    while (fgets (buf, BUFLEN, fp_bml)) {
		if (*buf == '#') continue;
		if (sscanf (buf, "%s%s", buf1, buf2) != 2)
		    error (14, bmlist);
		for (i = 0; i < nolays; ++i) {
		    if (strcmp (buf2, ldmlay[i]) == 0) break;
		}
		if (i >= nolays) error (15, buf2);
		if (layset[i]) error (16, buf2);
		for (j = 0; j < nolays; ++j) {
		    if (layset[j])
			if (strcmp (buf1, lay[j]) == 0) error (16, buf1);
		}
		layset[i] = 1;
		lay[i] = strsave (buf1, strlen (buf1));
	    }

	    fclose (fp_bml);
	}

	for (i = 0; i < nolays; ++i) {
	    if (!layset[i]) {
		for (j = 0; j < nolays; ++j) {
		    if (layset[j])
			if (strcmp (ldmlay[i], lay[j]) == 0)
			    error (13, ldmlay[i]);
		}
		layset[i] = 1;
		lay[i] = strsave (ldmlay[i], strlen (ldmlay[i]));
	    }
	}
    }

    if (*outputfile) {
	if (!(fp_file = fopen (outputfile, "w")))
	    error (0, outputfile);
    }
    else
	fp_file = stdout;

    resol = dmproject -> lambda;

    switch (Pmode) {
    case LDM:
	PF "%sDelft Layout Description Modified, LDM V3.2%s", begC, endC);
	break;
    case CIF:
	PF "%sCaltech Intermediate Form, CIF V2.0%s", begC, endC);
	break;
    case CMK:
	PF "%sPhilips CIRCUITMASK (CMK) 1978 AUG%s", begC, endC);
	break;
    case DLL:
	PF "%sDElft Layout LAnguage, DELLA%s", begC, endC);
	break;
    }

    time (&c);
    PF "%sGenerated with the program x%s V4.5%s", begC, suffix, endC);
    PF "%sat %.24s%s", begC, ctime (&c), endC);
#if NCF_RELEASE < 400
    PF "%sby the Delft NELSIS IC Design System Release 3%s", begC, endC);
#else
    PF "%sby the Delft NELSIS IC Design System Release 4%s", begC, endC);
#endif

    if (Pmode <= CIF) { /* LDM || CIF */
	if ((Pmode == LDM && o_mode) || (Pmode == CIF && !o_mode))
	    PF "%suse c%s with -o%s", begC, suffix, endC);
	else
	    PF "%suse c%s without -o%s", begC, suffix, endC);
    }

    PF "%sproject = %s%s", begC, dmproject -> dmpath, endC);
    PF "%sprocess = %s%s", begC, process -> pr_name, endC);

    if (Pmode <= CIF) /* LDM || CIF */
	PF "%slambda = %g micron%s", begC, dmproject -> lambda, endC);
    if (Pmode == CIF) {
	PF "%sthe parameter unit is in centi micron%s", begC, endC);
	for (i = 0; i < nolays; ++i) {
	    lay[i] = strsave (ldmlay[i], 4);
	    for (s = lay[i]; *s; ++s)
		if (islower (*s)) *s -= 32; /* UPPER */
	}
    }

    if (Pmode == CMK) {
	i = 0;
	PF "%s   %s", begC, ldmlay[i]);
	while (++i < nolays) PF ",%s", ldmlay[i]);
	PF "    converted to ...%s", endC);
	i = -1;
	suffix = u_mode ? "MASKS " : "masks ";
	while (++i < nolays) {
	    if (u_mode)
		for (s = lay[i]; *s; ++s)
		    if (islower (*s)) *s -= 32; /* UPPER */
	    PF "%s%s", (i == 0 ? suffix : ","), lay[i]);
	}
	PF "\n");
    }

    if (Pmode == DLL) {
	if (u_mode) {
	    for (i = 0; i < nolays; ++i) {
		for (s = lay[i]; *s; ++s)
		    if (islower (*s)) *s -= 32; /* UPPER */
	    }
	}
	i = 0;
	PF "%smasks: %s", begC, ldmlay[i]);
	while (++i < nolays) PF ",%s", ldmlay[i]);
	if (*bmlist || u_mode) {
	    PF "    converted to ...%s", endC);
	    i = 0;
	    PF "%s       %s", begC, lay[i]);
	    while (++i < nolays) PF ",%s", lay[i]);
	}
	PF "%s", endC);
    }

    PF "%sextraction of ", begC);
    if (r_mode) {
	PF "only the root of cell: %s%s", topcell, endC);
    }
    else {
	PF "all related cells of cell: %s%s", topcell, endC);
    }

    ckey = dmCheckOut (dmproject, topcell,
		WORKING, DONTCARE, LAYOUT, READONLY);
    inst_mo_elmt (p -> name);
    if (!r_mode) trav_mctree (inst_mc_elmt (molist));

    if (t_mode)
	PF "%scell names are truncated to %d chars%s", begC, t_mode, endC);
    if (u_mode)
	PF "%suppercase mode is used%s", begC, endC);

    inst_impcells ();

    outp_lld ();

    if (ut_mode) { /* CMK || DLL */
	i = 0;
	for (j = 0; j < HASHSIZE; ++j) {
	    for (p = Hashnew[j]; p; p = p -> next)
	    if (p -> other -> name != p -> name) { /* different */
		if (++i == 1) {
		    PF "%slist of changed cell names%s", begC, endC);
		    PF "%s%-14s old_name:%s", begC, "new_name:", endC);
		}
		PF "%s%-14s %s%s", begC, p -> name, p -> other -> name, endC);
	    }
	}
    }

    if (iclist) {
	PF "%slist of imported cells%s", begC, endC);
	PF "%s%-14s %-14s lib_project_path:%s", begC,
	    "alias:", "rem_name:", endC);
	pr_aliases (iclist);
    }

    if (Pmode == CIF)
	PF "E\n");
    else
	PF "%seof%s", begC, endC);

    if (*outputfile) fclose (fp_file);
    die (0);
}

static
pr_aliases (p)
struct ic_elmt *p;
{
    if (p) {
	pr_aliases (p -> l);
	get_alias (p -> mo);
	pr_aliases (p -> r);
    }
}

static
get_alias (mo)
char *mo;
{
    register IMPCELL **q = IClist;
    register IMPCELL  *ic;

    while (ic = *q++) {
	if (strcmp (ic -> alias, mo) == 0) {
	    PF "%s%-14s %-14s %s%s",
		begC, mo, ic -> cellname, ic -> dmpath, endC);
	    return;
	}
    }
    error (1, mo);
}

static
trav_mctree (pmc)
struct mc_elmt *pmc;
{
    DM_STREAM *stream;
    struct mc_elmt **pp, *p;
    struct na_elmt *q;

    ++level;
    stream = dmOpenStream (pmc -> m -> key, "mc", "r");
    pp = &pmc -> c;

    while (dmGetDesignData (stream, GEO_MC) > 0) {
	if (gmc.imported) continue;
	if (!(q = findold (gmc.cell_name, ut_mode))) error (4, gmc.cell_name);
	if (q -> m) {
	    if (q -> m -> level >= level) continue; /* don't change level */
	    q -> m -> level = level;
	}
	else {
	    ckey = dmCheckOut (dmproject, q -> name,
			ACTUAL, DONTCARE, LAYOUT, READONLY);
	    inst_mo_elmt (q -> name);
	    q -> m = molist;
	}
	*pp = inst_mc_elmt (q -> m);
	pp = &(*pp) -> s;
    }
    dmCloseStream (stream, COMPLETE);

    if (p = pmc -> c) { /* childs */
	if (level > llevel) ++llevel;
	do {
	    trav_mctree (p);
	} while (p = p -> s);
    }
    FREE (pmc);
    --level;
}

static
inst_mo_elmt (cell)
char *cell;
{
    struct mo_elmt *p;
    ALLOC (p, struct mo_elmt);
    p -> mo = cell;
    p -> key = ckey;
    p -> level = level;
    p -> next = molist;
    molist = p;
}

static struct mc_elmt *
inst_mc_elmt (m)
struct mo_elmt *m;
{
    struct mc_elmt *p;
    ALLOC (p, struct mc_elmt);
    p -> m = m;
    p -> c = 0;
    p -> s = 0;
    return (p);
}

sig_handler (sig)		/* signal handler */
int sig;
{
    signal (sig, SIG_IGN);	/* ignore signal */
    PE "\n");
    error (7, "");
}

dmError (s)
char *s;
{
    PE "%s: ", argv0);
    dmPerror (s);
    error (8, "");
}

char   *errlist[] = {
     /* 0 */ "cannot create file: %s",
     /* 1 */ "%s: imported cell name not found",
     /* 2 */ "read error in nor-file of cell: %s",
     /* 3 */ "%s: no more core",
     /* 4 */ "%s: cell name not found",
     /* 5 */ "%s: file does exist (not overwritten)",
     /* 6 */ "unknown element in nor-file of cell: %s",
     /* 7 */ "received interrupt signal",
     /* 8 */ "error in DMI function",
     /* 9 */ "illegal # of xy-pairs in nor-file of cell: %s",
     /* 10 */ "empty celllist",
     /* 11 */ "cannot read file",
     /* 12 */ "cannot allocate memory",
     /* 13 */ "cannot make cross reference for mask: %s (already used)",
     /* 14 */ "read error in file: %s",
     /* 15 */ "read error in bmlist-file, unknown mask: %s",
     /* 16 */ "read error in bmlist-file, already used mask: %s",
     /* 17 */ "too many xy-pairs in nor-file of cell: %s",
     /* 18 */ "sfx != sfy in mc-file of cell: %s",
     /* 19 */ "unknown error"
};

error (nr, s)
int    nr;
char  *s;
{
    if (nr < 0 || nr > 19) nr = 19;
    PE "%s: ", argv0);
    PE errlist[nr], s);
    PE "\n");
    die (1);
}

die (status)
int  status;
{
    dmQuit ();
    if (status) {
	if (*outputfile && fp_file) unlink (outputfile);
	PE "\n%s: -- program aborted --\n", argv0);
    }
    exit (status);
}

struct na_elmt *
findold (oldname, mode)
char *oldname;
int   mode;
{
    register char *o, *s;
    register struct na_elmt *p;
    register struct na_elmt *q;
    char   *newname;
    int     hashold;
    int     hashnew;
    int     oldlen;
    static char *cn = "X00/";

    s = oldname;
    hashold = 0;
    while (*s) hashold += *s++;
    hashold %= HASHSIZE;

    for (p = Hashold[hashold]; p; p = p -> next)
	if (strcmp (oldname, p -> name) == 0) break;

    if (!p || !mode || p -> other) return (p);

    oldlen = s - oldname;
    newname = oldname = p -> name;
    hashnew = hashold;

    if (t_mode && oldlen > t_mode) {
	if (!(newname = malloc (t_mode + 1))) error (3, "char");
	o = oldname;
	s = newname;
	hashnew = 0;
	while ((s - newname) < t_mode) {
	    *s = *o++;
	    if (u_mode && islower (*s)) *s -= 32; /* UPPER */
	    hashnew += *s++;
	}
	*s = 0;
	hashnew %= HASHSIZE;
    }
    else if (u_mode) {
	s = oldname - 1;
	while (*++s) if (islower (*s)) break;
	if (*s) {
	    if (oldlen < 4) oldlen = 4;
	    if (!(newname = malloc (oldlen + 1))) error (3, "char");
	    o = oldname;
	    s = newname;
	    hashnew = 0;
	    while (*o) {
		*s = *o++;
		if (islower (*s)) *s -= 32; /* UPPER */
		hashnew += *s++;
	    }
	    *s = 0;
	    hashnew %= HASHSIZE;
	}
    }

    for (q = Hashnew[hashnew]; q; q = q -> next)
    if (strcmp (newname, q -> name) == 0) {
	/*
	** Already found, use another name!
	*/
	if (newname == oldname)
	    if (!(newname = malloc (5))) error (3, "char");
	strcpy (newname, cn);
	cn = newname;
again:
	if (++cn[3] > '9') {
	    cn[3] = '0';
	    if (++cn[2] > '9') {
		cn[2] = '0';
		if (++cn[1] > '9') {
		    cn[1] = '0';
		    if (++cn[0] > 'Z') {
			error (3, "name generation");
		    }
		}
	    }
	}
	hashnew = cn[0] + cn[1] + cn[2] + cn[3];
	hashnew %= HASHSIZE;
	for (q = Hashnew[hashnew]; q; q = q -> next)
	    if (strcmp (cn, q -> name) == 0) goto again;
	break;
    }

    ALLOC (q, struct na_elmt);
    p -> other = q;
    q -> other = p;
    q -> name = newname;
    q -> next = Hashnew[hashnew];
    Hashnew[hashnew] = q;
    return (p);
}

char *
findnew (oldname)
char *oldname;
{
    struct na_elmt *p;
    if (p = findold (oldname, 0)) return (p -> other -> name);
    error (4, oldname); /* die */
    return ("XXXX");
}

static
inst_cells ()
{
    register char **p;
    LClist = (char **) dmGetMetaDesignData (CELLLIST, dmproject, LAYOUT);
    if (!*LClist) error (10, "");
    p = LClist;
    while (*p) appendname (*p++);
}

static
inst_impcells ()
{
    register IMPCELL **q;
    IClist = (IMPCELL **) dmGetMetaDesignData (IMPORTEDCELLLIST,
	    dmproject, LAYOUT);
    q = IClist;
    while (*q) appendname ((*q++) -> alias);
}

static
appendname (oldname)
char *oldname;
{
    register char *s;
    register struct na_elmt *p;
    int     hashold;

    s = oldname;
    hashold = 0;
    while (*s) hashold += *s++;
    hashold %= HASHSIZE;

    for (p = Hashold[hashold]; p; p = p -> next)
	if (strcmp (oldname, p -> name) == 0) {
	    PE "%s: warning: %s: cell name already found!\n",
		argv0, oldname);
	    return;
	}

    ALLOC (p, struct na_elmt);
    p -> name = oldname;
    p -> next = Hashold[hashold];
    Hashold[hashold] = p;
    p -> other = 0;
    p -> m = 0;
}
