static char *SccsId = "@(#)main.c 3.15 (TU-Delft) 03/26/91";
/**********************************************************

Name/Version      : impcell/3.15

Language          : C
Operating system  : UNIX SYSTEM V
Host machine      : HP9000

Author(s)         : S. de Graaf
Creation date     : 26-Mar-1987
Modified by       : P. Bingley
Modification date : 24-Mar-1988
Modified by       : S. de Graaf
Modification date : 11-Apr-1988


        Delft University of Technology
        Department of Electrical Engineering
        Network Theory Section
        Mekelweg 4 - P.O.Box 5031
        2600 GA DELFT
        The Netherlands

        Phone : 015 - 786234

        COPYRIGHT (C) 1987-1988, All rights reserved
**********************************************************/
#include "dmstd.h"
#include "signal.h"
#ifdef ESE
#include "eseOption.h"
#include "tversion.h"
#endif

#define PE fprintf(stderr,
#define BUFLEN DM_MAXNAME+DM_MAXNAME+MAXLINE+20

struct impcell_tree  {
    char   a_name[DM_MAXNAME + 1];
    struct impcell_tree *ht_next;
};

#define HASHSIZE 128
struct impcell_tree *Hashtab[DM_NOVIEWS][HASHSIZE];
int   icl_cnt[DM_NOVIEWS];
FILE *icl_fp[DM_NOVIEWS];

DM_PROJECT *cp_key;
DM_PROJECT *lib_key;
char *projlist = "./projlist";
int view_flag[DM_NOVIEWS] = { 0, 0, 0 };
char *view_buf[DM_NOVIEWS] = { NULL, NULL, NULL };
char *argumentlist[3] = { NULL, NULL, NULL };
char ** viewlist = NULL;
char  Buf0[BUFLEN];
char  Buf1[BUFLEN];
char  Buf2[BUFLEN];
char  Buf3[BUFLEN];
int   vflag;
int   A_opt = 0;
int   I_opt = 0;
int   R_opt = 0;
char *prefix = NULL;

char *argv0 = "impcell";	/* program name */
#ifndef ESE
char *use_msg1 =			/* command line */
"\nUsage: %s -c|f|l -a|i|r [-p prefix] project";
char *use_msg2 =			/* command line */
"\n   or: %s -c|f|l [project remote [local]]\n\n";
#else
char *use_msg1 =			/* command line */
"";
char *use_msg2 =			/* command line */
"Usage:  %s options -view viewtype project [remote [local]]\n\n";
#endif

#ifdef ESE
OptionSpec optionSpecs[] = {
    { "", NO, eseHelp, (void *) optionSpecs,
            "usage:  impcell options -view viewtype project [remote [local]]\nOptions (may be abbreviated) are:"},
    { "release", NO, esePrintString, (void *) TOOLVERSION,
            "    -release:        print the release number of this tool"},
    { "help", NO, eseHelp, (void *) optionSpecs,
            "    -help:           print this list" },
    { "%etext", NO, eseText, (void *) NULL,
            "    -%etext:          print the '(int) & etext' number" },
    { "all", NO, eseTurnOn, (void *) & A_opt,
            "    -all:            import all cells from specified project" },
    { "interactive", NO, eseTurnOn, (void *) & I_opt,
            "    -interactive:    import all cells interactively (answers: ynq)" },
    { "root", NO, eseTurnOn, (void *) & R_opt,
            "    -root:           import only all the root cells from specified project" },
    { "prefix", YES, eseAssignArgument, (void *) & prefix,
            "    -prefix prefix:  'prefix' is used to generate local names from remote names" },
    { "view", YES, eseListArguments, (void *) & viewlist,
            "    -view viewtypes: import for cells of specified viewtype(s)" }, 
    { "%layout", NO, eseTurnOn, (void *) & view_flag[0],
            "    -%layout:        do it for cells in the layout view" },
    { "%circuit", NO, eseTurnOn, (void *) & view_flag[1],
            "    -%circuit:       do it for cells in the circuit view" },
    { (char *) 0, (char) 0, (IFP) 0, (void *) 0, (char *) 0 },
};
#endif

main (argc, argv)
int   argc;
char *argv[];
{
    char *lib_path;
    char *loc_name;
    char *rem_name;
    char *cp;
    int  arg;
    int  ok = 0;
    char *view;
    register char **clp;
    int  view_entry;

#ifndef ESE
    if (argc < 2) {
	PE "%s: illegal number of arguments specified\n", argv0);
	usage ();
    }

    for (arg = 1; arg < argc; arg++) {
	if (argv[arg][0] == '-') {
	    for (cp = &argv[arg][1]; *cp; ++cp) {
		switch (*cp) {
		case 'a':
		    A_opt = 1;
		    break;
		case 'i':
		    A_opt = I_opt = 1;
		    break;
		case 'r':
		    A_opt = R_opt = 1;
		    break;
		case 'p':
		    if (arg+1 < argc) {
			if (dmTestname (prefix = argv[arg+1])) {
			    PE "%s: prefix '%s' not allowed\n", argv0, prefix);
			    exit (1);
			}
			A_opt = 1;
		    }
		    break;
		case 'l':
		    view_buf[0] = LAYOUT;
		    ok = 1;
		    break;
		case 'c':
		    view_buf[1] = CIRCUIT;
		    ok = 1;
		    break;
		case 'f':
		    view_buf[2] = FLOORPLAN;
		    ok = 1;
		    break;
		default:
		    usage ();
		}
	    }
	}
    }

    if (!ok) {
	PE "%s: no view specified\n", argv0);
	usage ();
    }


    ok = 0;
    for (arg = 1; arg < argc; arg++) {
	if (argv[arg][0] != '-') {
	    if (A_opt) {
		if (!prefix || prefix != argv[arg]) {
		    if (ok != 2) {
			lib_path = argv[arg];
			ok = 2;
		    }
		    else {
			PE "%s: warning: extra argument '%s' ignored\n",
			    argv0, argv[arg]);
		    }
		}
	    }
	    else if (argc != 2) {
		switch (ok) {
		case 0:
		    lib_path = argv[arg];
		    ok = 1;
		    break;
		case 1:
		    loc_name = rem_name = argv[arg];
		    ok = 2;
		    break;
		case 2:
		    loc_name = argv[arg];
		    ok = 3;
		    break;
		default:
		    PE "%s: warning: extra argument '%s' ignored\n",
			argv0, argv[arg]);
		}
	    }
	}
    }

    if (ok < 2) {
	if (ok == 1) PE "%s: no remote cell name specified\n", argv0);
	else PE "%s: no library project specified\n", argv0);
	usage ();
    }
#else 
    if (eseOptionHandler (argc, argv, optionSpecs, 3, argumentlist) > 0) {
        exit (1);
    }

    while (viewlist && viewlist[0]) {
        if (strncmp (viewlist[0], "layout", strlen (viewlist[0])) == 0) {
	    view_buf[0] = LAYOUT;
        }
	else if (strncmp (viewlist[0], "circuit", strlen (viewlist[0])) == 0) {
            view_buf[1] = CIRCUIT;
        }
        else {
	    PE "-view: '%s' does not exist", viewlist[0]);
	    usage ();
	}
	viewlist++;
    }

    if (view_flag[0]) {
        view_buf[0] = LAYOUT;
    }
    if (view_flag[1]) {
        view_buf[1] = CIRCUIT;
    }

    if (!view_buf[0] && !view_buf[1]) {
        PE "%s: no view specified\n", argv0);
        usage ();
    }

    if (I_opt || R_opt) {
        A_opt = 1;
    }

    if (!A_opt && prefix) {
        PE "%s: illegal combination of options\n", argv0);
        usage ();
    }

    if (prefix) {
	if (dmTestname (prefix)) {
	    PE "%s: prefix '%s' not allowed\n", argv0, prefix);
	    exit (1);
	}
    }
    lib_path = argumentlist[0];

    if (!lib_path) {
	PE "%s: No library project specified\n", argv0);
	usage ();
    }

    if (A_opt) {
	if (argumentlist[1] != NULL) {
	    PE "%s: Too many arguments\n", argv0, prefix);
	    usage ();
	}
    }
    else {
        if (lib_path && !argumentlist[1]) {
            PE "%s: Not enough arguments\n", argv0, prefix);
            usage ();
        }
	if (argumentlist[1]) {
	    rem_name = loc_name = argumentlist[1];
	    if (argumentlist[2]) {
	        loc_name = argumentlist[2];
	    }
        }
    }
/*
    if (isatty (0)) {
	++vflag;
	PE "%s: -- reading from stdin --\n", argv0);
    }
*/
#endif

    /* ignore all signals */
#ifndef MSDOS
    signal (SIGHUP, SIG_IGN);
    signal (SIGQUIT, SIG_IGN);
    signal (SIGTERM, SIG_IGN);
#endif
    signal (SIGINT, SIG_IGN);

    if (dmInit (argv0)) exit (1);

    if ((cp_key = dmOpenProject (".", PROJ_WRITE)) == NULL) {
	PE "%s: the cwd is no valid project\n", argv0);
	die (1);
    }

    build_celllist ();

    if (!lib_path && !A_opt) {
	if (isatty (0)) {
	    ++vflag;
	    PE "%s: -- reading from stdin --\n", argv0);
	}
	/* get lines in impcelllist format: alias, cellname, project-path */
	while (fgets (Buf0, BUFLEN, stdin)) {
	    if (sscanf (Buf0, "%s%s%s", Buf1, Buf2, Buf3) != 3) {
		PE "%s: stdin: line contains incorrect number of strings\n",
		    argv0);
		die (1);
	    }
	    for (view_entry = 0; view_entry < DM_NOVIEWS; ++view_entry) {
		if (!view_buf[view_entry]) continue;

		impcell (Buf1, Buf2, Buf3, view_entry);
	    }
	}
    }
    else if (A_opt) {	/* import all cells from library */
	if (I_opt) ++vflag;

	dmerrno = 0;

	if ((lib_key = dmOpenProject (dmSubstituteEnvironmentVars(lib_path),
				      PROJ_READ)) == NULL) {
	    PE "%s: path '%s' is no valid project\n", argv0,
	       dmSubstituteEnvironmentVars(lib_path));
	    die (1);
	}

	if (lib_key == cp_key) {
	    PE "%s: path '%s' is the current project\n", argv0, lib_path);
	    die (1);
	}

        /* if lib_path contains environment variables, then preserve it: */
        if (strcmp(lib_path,dmSubstituteEnvironmentVars(lib_path)) == 0)
           lib_path = lib_key -> netpath;

	if (dmerrno != DME_PRLOCK) {
	    is_legal_lib (lib_path); /* (exits if not legal) */
	}

	for (view_entry = 0; view_entry < DM_NOVIEWS; ++view_entry) {
	    if (!(view = view_buf[view_entry])) continue;

	    if (I_opt) PE "view: %s\n", view);

	    /* read library celllist */
	    if ((clp = lib_key -> celllist[view_entry] = (char **)
		    dmGetMetaDesignData (CELLLIST, lib_key, view)) == NULL) {

		PE "%s: error: cannot read file '%s/%s/celllist'\n",
		    argv0, lib_path, view);
		die (1);
	    }

	    /* for all entries in the library celllist */
	    for (; *clp != NULL; clp++) {
		if (R_opt && (int) _dmCellIsRoot2 (lib_key, *clp, view,
			lib_key -> celllist[view_entry]) != 1) continue;

		_dmSprintf (Buf0, "%s%s", (prefix ? prefix : ""), *clp);
		loc_name = Buf0;

		if (!I_opt) {
		    impcell (loc_name, *clp, lib_path, view_entry);
		}
		else {	/* I_opt: interactive */
		    ok = 0;
		    while (!ok) {
			/* import this cell ? */
			PE "%s [n]? ", *clp);
			if (fgets (Buf1, BUFLEN, stdin)) {
			    switch (*Buf1) {
			    case 'y':
				ok = 2;
				break;
			    case '\n':
			    case 'n':
				ok = 1;
				break;
			    case 'q':
				die (0);
				break;
			    default:
				Buf1[strlen (Buf1) - 1] = '\0';
	    PE "%s: answer '%s' incorrect, type 'y', 'n' or 'q'\n",
		argv0, Buf1);
				break;
			    }
			}
			else die (1);

			/* get alias name */
			while (ok == 2) {
			    PE "local name [%s]: ", loc_name);
			    if (fgets (Buf2, BUFLEN, stdin)) {
				Buf2[strlen (Buf2) - 1] = '\0';
				if (*Buf2) {
				    if (dmTestname (Buf2)) *loc_name = '\0';
				    else loc_name = Buf2;
				}
				if (*loc_name && impcell (loc_name, *clp,
					lib_path, view_entry)) {
				    /* cell imported */
				    ok = 3;
				}
				else { /* impcell failed */
				    if (*Buf0 || *Buf2) {
					*Buf0 = '\0';
					loc_name = Buf0;
				    }
				    else { /* skip this cell after all */
		    PE "%s: warning: cell '%s' skipped\n",
			argv0, *clp);
					ok = 4;
				    }
				}
			    }
			    else die (1);
			}
		    }
		}
	    }
	}
    }
    else {
	for (view_entry = 0; view_entry < DM_NOVIEWS; ++view_entry) {
	    if (!view_buf[view_entry]) continue;

	    impcell (loc_name, rem_name, lib_path, view_entry);
	}
    }

    die (0);
}

build_celllist ()
{
    register FILE *fp;
    char *view;
    int   found;
    register int count;
    register int view_entry;

    for (view_entry = 0; view_entry < DM_NOVIEWS; ++view_entry) {
	if (!(view = view_buf[view_entry])) continue;

	if ((cp_key -> celllist[view_entry] = (char **)
	    dmGetMetaDesignData (CELLLIST, cp_key, view)) == NULL) {
	    PE "%s: error: cannot read file './%s/celllist'\n", argv0, view);
	    die (1);
	}

#ifdef MSDOS
	_dmSprintf (Buf0, "./%s/impclist", view);
#else
	_dmSprintf (Buf0, "./%s/impcelllist", view);
#endif
	if (!(fp = fopen (Buf0, "r+"))) {
	    PE "%s: error: cannot read file '%s'\n", argv0, Buf0);
	    die (1);
	}

	count = 0;
	/* format: alias, cellname, project-path */
	while (fscanf (fp, "%s%*s%*s", Buf0) != EOF) {
	    ++count;
	    append_tree (Buf0, view_entry);
	}
	icl_cnt[view_entry] = count;
	icl_fp [view_entry] = fp;
    }
}

impcell (loc_name, rem_name, lib_path, view_entry)
char *loc_name, *rem_name, *lib_path;
register int view_entry;
{
    register FILE *fp;
    char *view;
    int   found;
    register char **clp;

    if (strlen (lib_path) >= MAXLINE) {
	PE "%s: too long project path name\n", argv0);
	if (vflag) return (0);
	else die (1);
    }

    if (dmTestname (rem_name)) {
	PE "%s: remote cell name '%s' illegal\n", argv0, rem_name);
	if (vflag) return (0);
	else die (1);
    }

    if (dmTestname (loc_name)) {
	PE "%s: local cell name '%s' illegal\n", argv0, loc_name);
	if (vflag) return (0);
	else die (1);
    }

    view = view_buf[view_entry];
    dmerrno = 0;

    if (!A_opt) {
	if ((lib_key = dmOpenProject (dmSubstituteEnvironmentVars(lib_path),
				      PROJ_READ)) == NULL) {
	    PE "%s: '%s' is no valid project\n", argv0,
               dmSubstituteEnvironmentVars(lib_path));
	    if (vflag) return (0);
	    else die (1);
	}

	if (lib_key == cp_key) {
	    PE "%s: '%s' is the current project\n", argv0, lib_path);
	    if (vflag) return (0);
	    else die (1);
	}

        /* if lib_path contains environment variables, then preserve it: */
        if (strcmp(lib_path,dmSubstituteEnvironmentVars(lib_path)) == 0)
           lib_path = lib_key -> netpath;

	if (dmerrno != DME_PRLOCK) {
	    is_legal_lib (lib_path); /* (exits if not legal) */
	}

	if ((clp = lib_key -> celllist[view_entry]) == NULL) {
	    if ((clp = lib_key -> celllist[view_entry] = (char **)
		dmGetMetaDesignData (CELLLIST, lib_key, view)) == NULL) {

		PE "%s: error: cannot read file '%s/%s/celllist'\n",
		    argv0, lib_path, view);
		die (1);
	    }
	}

	found = 0;
	while (*clp != NULL) {
	    if (strcmp (*clp++, rem_name) == 0) {
		++found;
		break;
	    }
	}

	if (!found) {
	    PE "%s: cell '%s' not found in '%s/%s/celllist'\n",
		argv0, rem_name, lib_path, view);
	    PE "%s: warning: '%s' not added to view '%s'\n",
		argv0, loc_name, view);
	    return (0);
	}
    }

    clp = cp_key -> celllist[view_entry];

    found = 0;
    while (*clp != NULL) {
	if (strcmp (*clp++, loc_name) == 0) {
	    ++found;
	    break;
	}
    }

    if (found) {
	PE "%s: cell '%s' found in './%s/celllist'\n",
	    argv0, loc_name, view);
	PE "%s: warning: '%s' not added to view '%s'\n",
	    argv0, loc_name, view);
	return (0);
    }

    if (append_tree (loc_name, view_entry)) {
	PE "%s: cell '%s' already imported in view '%s'\n",
	    argv0, loc_name, view);
	return (0);
    }

    if (++icl_cnt[view_entry] > DM_MAXCELLS) {
	PE "%s: too many imported cells\n", argv0);
	PE "%s: warning: cell '%s' not added to view '%s'\n",
	    argv0, loc_name, view);
	return (0);
    }

    fp = icl_fp[view_entry];
    fseek (fp, 0L, 2);
    fprintf (fp, "%s %s %s\n", loc_name, rem_name, lib_path);

    return (1);
}

append_tree (name, nr)
char *name;
int   nr;
{
    register char *s;
    register struct impcell_tree *ptr;
    register int  hashval;

    s = name;
    hashval = 0;
    while (*s) hashval += *s++;
    hashval %= HASHSIZE;

    for (ptr = Hashtab[nr][hashval]; ptr; ptr = ptr -> ht_next)
	if (strcmp (name, ptr -> a_name) == 0)
	    return (1); /* found */

    /*
    ** allocate new hash-table element
    */
    ptr = (struct impcell_tree *) malloc (sizeof (struct impcell_tree));
    if (!ptr) {
	PE "%s: error: cannot malloc\n", argv0);
	die (1);
    }
    strcpy (ptr -> a_name, name);
    ptr -> ht_next = Hashtab[nr][hashval];
    Hashtab[nr][hashval] = ptr;
    return (0); /* not found */
}

is_legal_lib (lib_path)
char *lib_path;
{
    register FILE *fp;
    int found;

    if (!(fp = fopen (projlist, "r"))) {
	PE "%s: error: cannot read file '%s'\n", argv0, projlist);
	die (1);
    }

    found = 0;
    while (fscanf (fp, "%s", Buf0) != EOF) {
	if (strcmp (dmSubstituteEnvironmentVars(Buf0),
		    dmSubstituteEnvironmentVars(lib_path)) == 0) {
	    ++found;
	    break;
	}
    }
    fclose (fp);

    if (!found) {
	PE "%s: path '%s' not found in 'projlist'\n", argv0,
	   dmSubstituteEnvironmentVars(lib_path));
	die (1);
    }
}

usage ()
{
    PE use_msg1, argv0);
    PE use_msg2, argv0);
    exit (1);
}

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

die (status)
int status;
{
    dmQuit ();
    if (status) PE "%s: -- program aborted --\n", argv0);
    else if (vflag) PE "%s: -- program finished --\n", argv0);
    exit (status);
}
