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

Name/Version      : mkpr/3.25

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

Author(s)         : P. van der Wolf
Creation date     : 25-Aug-1986
Modified by       : S. de Graaf
Modification date : 16-Dec-1987


        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 , All rights reserved
**********************************************************/
/* The mkpr (make project) command should be used by the designer to
** create a project before actually starting to design.
** It shows much resemblance with the UNIX mkdir command.
** mkpr creates a directory with a file '.dmrc' in it, containing
** a 'release number' of the software by means of which this project was
** created (which will be checked automatically by dmOpenProject),
** the 'process identifier', 'lambda', and the 'nr_samples per lambda'.
** To prevent that the designer starts working in a directory which
** is not a project, dmOpenProject() should verify the existence of '.dmrc'.
**
** In the nearby future various other facilities can be incorporated
** in the mkpr command, such as project-administration, support for
** a hierarchical project organization, initialization of design data
** management facilities (creating e.g directories and tables).
*/

#include "dmstd.h"
#include "signal.h"
#ifdef ESE
#include "eseOption.h"
#include "tversion.h"
#endif

#define	RELEASE_NUM	3	/* softw. rel. nr.; see dmOpenProject */
#define DEF_N_SAMP	8	/* default number of samples */

#ifdef ESE
OptionSpec optionSpecs[] = {
    { "", NO, eseHelp, (void *) optionSpecs,
            "usage:     mkpr [options] project\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" },
    { "%help", NO, eseHelpAll, (void *) optionSpecs,
            "    -%help:                print this list" },
    { "%etext", NO, eseText, (void *) NULL,
            "    -%etext:               print the '(int) & etext' number" },
    { NULL, NULL, NULL, NULL, NULL, },
};
#endif


#ifdef SYSV
#define rindex strrchr
#define index  strchr
#endif

struct stat st_buf;
char  buf[MAXLINE];

#ifndef ESE
char *argv0 = "mkpr";	/* program name */
char *use_msg =		/* command line */
"\nUsage: %s project\n\n";
#else
char *argv0 = "mkpr";	/* program name */
char *use_msg =		/* command line */
"\nUsage: %s [options] project\n\n";
#endif

extern char icdpath[];

main (argc, argv)
int   argc;
char *argv[];
{
    char   *rindex ();
    char   *hp;
    char   *dmpath;
    char   *project_name;
    char    dir_path[MAXLINE];
    int     sel_proc_id;
    double  sel_lambda;
    double  ask_lambda ();
    char * projName;

#ifdef MSDOS
    _dmData_ref ();
#endif
#ifdef ESE
    if (eseOptionHandler (argc, argv, optionSpecs, 1, & projName) > 0) {
        fprintf (stderr, use_msg, argv0);
        exit (1);
    }

    if (!projName) {
        fprintf (stderr, use_msg, argv0);
        exit (1);
    }

    if (strlen (projName) >= MAXLINE) {
	fprintf (stderr, "%s: path name too long\n", argv0);
	exit (1);
    }

    if (hp = rindex (projName, '/')) {	/* '/' found */
	dmpath = projName;
	*hp = '\0';
	project_name = hp + 1;
    }
    else {
	dmpath = ".";
	project_name = projName;
    }

#else

    if (argc != 2) {
	fprintf (stderr, use_msg, argv0);
	exit (1);
    }

    if (strlen (argv[argc - 1]) >= MAXLINE) {
	fprintf (stderr, "%s: path name too long\n", argv0);
	exit (1);
    }

    if (hp = rindex (argv[argc - 1], '/')) {	/* '/' found */
	dmpath = argv[argc - 1];
	*hp = '\0';
	project_name = hp + 1;
    }
    else {
	dmpath = ".";
	project_name = argv[argc - 1];
    }
#endif

    if (dmTestname (project_name) == -1) {
	exit (1);
    }
    /* dmTestname might have shorten the project name	*/

#ifdef DM_DEBUG
    IFDEBUG {
	fprintf (stderr, "dmpath: '%s'\nproject_name: '%s'\n",
						dmpath, project_name);
    }
#endif /* DM_DEBUG */

    _dmSprintf (dir_path, "%s/%s", dmpath, project_name);

    /* We do not create the directory yet. We will only do that after
     * the designer has provided all information in the right way.
     * However, we will now check the possibility to create the directory
     * to prevent unnecessary failures at the end of the program.
     */
    
    /* first check if dmpath exists, is a directory, and is writable */
    if (stat (dmpath, &st_buf) != 0) {
	fprintf (stderr, "%s: cannot access %s\n", argv0, dmpath);
	exit (1);
    }

#ifdef DM_DEBUG
    IFDEBUG {
	fprintf (stderr, "st_buf.st_mode = '%o'\n", st_buf.st_mode);
    }
#endif /* DM_DEBUG */

#if defined(__STDC__)  && !defined(MSDOS)
    if (!(S_ISDIR (st_buf.st_mode))) {
	fprintf (stderr, "%s: %s is not a directory\n", argv0, dmpath);
	exit (1);
    }
    if (!(st_buf.st_mode & S_IWUSR)) {
	fprintf (stderr, "%s: %s is not writable\n", argv0, dmpath);
	exit (1);
    }
#else
     if (!(st_buf.st_mode & S_IFDIR)) {
	fprintf (stderr, "%s: %s is not a directory\n", argv0, dmpath);
	exit (1);
    }
     if (!(st_buf.st_mode & S_IWRITE)) {
	fprintf (stderr, "%s: %s is not writable\n", argv0, dmpath);
	exit (1);
    }
#endif
    /* project name may not already exist */
    if (stat (dir_path, &st_buf) != -1) {
	fprintf (stderr, "%s: cannot create project %s\n", argv0, project_name);
	exit (1);
    }

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

    /* now ask for information about the process */
    sel_proc_id = ask_proc_id ();
    sel_lambda = ask_lambda ();

/* Now that we have all information we will try to make
** the directory. First of all ignore signals.
*/
#ifndef MSDOS
    signal (SIGHUP, SIG_IGN);
    signal (SIGINT, SIG_IGN);
    signal (SIGQUIT, SIG_IGN);
    signal (SIGTERM, SIG_IGN);
#endif

#if defined(OTHERCPU)		/* replace by name of CPU if there is
				   no mkdir system call */
				/* see also dmCheckOut() */
    if (dmrun ("/bin/mkdir", dir_path, NULL)) goto sys_err;
#else 	/* SYSV and BSD have it */
    if (mkdir (dir_path, 0777)) goto sys_err;
#endif


    /* now try to lock the new project immediately, to prevent
     * that others try to use it right away.
     * We did not use dmOpenProject() for this purpose because:
     * (1) the lock of dmOpenProject() is the single-user lock of release 2,
     *     which can be omitted in a multi-user implementation.
     * (2) dmOpenProject() verifies the legality of the project by searching
     *     for the file '.dmrc' which we still have to create.
     */

    if (_dmLockProject (dir_path) == -1) {
	/* this should of course never happen in this new directory */
	exit (1);
    }

    init_proj (dir_path, sel_proc_id, sel_lambda);

    _dmUnlockProject (dir_path);

    dmQuit ();
    printf ("%s: -- project created --\n", argv0);
    exit (0);

sys_err:
    dmerrno = DME_SYS;
    dmError2 ("can't mkdir", dir_path);
    exit (1);
}

init_proj (project, sel_proc_id, sel_lambda)
char   *project;
int     sel_proc_id;
double  sel_lambda;
/* 
** Creates the files '.dmrc' and 'projlist' in the newly created directory.
** Furthermore it creates the view directories with the empty files
** 'celllist' and 'impcelllist' in it.
*/
{
    char    path[MAXLINE];
    FILE   *fp;
    int     fd;
    int     i;
    static char *view_str[] = {
	LAYOUT,
	CIRCUIT,
#ifndef ESE
	FLOORPLAN
#endif
    };

    for (i = 0; i < sizeof (view_str)/sizeof (char *); i++) {
	_dmSprintf (path, "%s/%s", project, view_str[i]);

#if defined(OTHERCPU)
	if (dmrun ("/bin/mkdir", path, NULL)) {
	    dmerrno = DME_SYS;
	    dmError2 ("can't mkdir", path);
	    exit (1);
	}
#else 	/* SYSV and BSD have it */
	if (mkdir (path, 0777)) {
	    dmerrno = DME_SYS;
	    dmError2 ("can't mkdir", path);
	    exit (1);
	}
#endif

	/* create an empty celllist in the view directory: every legal
	 * project should have celllists in its view-dirs from now on.
	 */
	_dmSprintf (path, "%s/%s/celllist", project, view_str[i]);
	if ((fd = creat (path, 0666)) == -1) {
	    fprintf (stderr, "%s: cannot create %s\n", argv0, path);
	    exit (1);
	}
	close (fd);

	/* create an empty impcelllist in the view directory.
	 */
#ifdef MSDOS
	_dmSprintf (path, "%s/%s/impclist", project, view_str[i]);
#else
	_dmSprintf (path, "%s/%s/impcelllist", project, view_str[i]);
#endif
	if ((fd = creat (path, 0666)) == -1) {
	    fprintf (stderr, "%s: cannot create %s\n", argv0, path);
	    exit (1);
	}
	close (fd);
    }

    /* create an empty projlist
     */
    _dmSprintf (path, "%s/projlist", project);
    if ((fd = creat (path, 0666)) == -1) {
	fprintf (stderr, "%s: cannot create %s\n", argv0, path);
	exit (1);
    }
    close (fd);

    /* create and initialize the file '.dmrc'.
     */
#ifdef MSDOS
    _dmSprintf (path, "%s/dmrc.dot", project);
#else
    _dmSprintf (path, "%s/.dmrc", project);
#endif
    if ((fp = fopen (path, "w")) == NULL) {
	fprintf (stderr, "%s: cannot fopen %s\n", argv0, path);
	exit (1);
    }

#ifdef PACK
    fprintf (fp, "%d\n", RELEASE_NUM);
#else /* UnPACKed */
    fprintf (fp, "%dU\n", RELEASE_NUM);
#endif /* PACK */
    fprintf (fp, "%d\n", sel_proc_id);
    fprintf (fp, "%.3f\n", sel_lambda);
    fprintf (fp, "%d\n", DEF_N_SAMP);

    fclose (fp);
}

#define IS_PROC	1
#define	NO_PROC 0

ask_proc_id ()
{
    FILE  *proc_fp;
    char   proc_id[MAXLINE];
    char   proc_name[MAXLINE];
    char  *hp;
    int	   pr_id_arr[DM_MAXPROCESSES];
    int    i;
    int    maxprocid;
    int    sel_id;
    int    count;

    char   *index ();

#ifdef MSDOS
    _dmSprintf (buf, "%s/lib/process/process.lst", icdpath);
#else
    _dmSprintf (buf, "%s/lib/process/processlist", icdpath);
#endif
    if ((proc_fp = fopen (buf, "r")) == NULL) {
	fprintf (stderr, "%s: cannot fopen %s\n", argv0, buf);
	exit (1);
    }

    for (i = 0; i < DM_MAXPROCESSES; i++) {
	pr_id_arr[i] = NO_PROC;
    }

    if (isatty (fileno (stdin))) {
	printf ("available processes:\n");
	printf ("process id       process name\n");
    }

    maxprocid = 0;

    /* read process id's and process names of the supported processes
     */
    while (fgets (buf, MAXLINE, proc_fp) != NULL) {
	if (sscanf (buf, "%s%s", proc_id, proc_name) != 2) continue;
	if (*proc_id == '#') continue;

	if ((hp = index (proc_name, '#')))
	    *hp = '\0';		/* strip comment right after proc_name */

	i = atoi (proc_id);

	/* If process does not exist, is unreadable etc, skip it */
	if (_dmDoGetProcess (i) == NULL)
            continue;

	/* Process id must be in range [0 - (DM_MAXPROCESSES - 1)] */
	if (i < 0 || i >= DM_MAXPROCESSES) {
	    dmerrno = DME_PRREAD;
	    dmError2 ("illegal process-id", proc_id);
	    exit (1);
	}

	pr_id_arr[i] = IS_PROC;

	if (i > maxprocid) maxprocid = i;

	if (isatty (fileno (stdin)))
	    printf ("%10d       %s\n", i, proc_name);
    }
    fclose (proc_fp);

    count = 0;
    for (;;) {
	if (isatty (fileno (stdin)))
	    printf ("select process id (1 - %d): ", maxprocid);
	if (gets (buf) && sscanf (buf, "%d", &sel_id) == 1) {
	    if (sel_id >= DM_MAXPROCESSES || pr_id_arr[sel_id] == NO_PROC) {
		if (++count > 1) {
		    printf ("wrong again, sorry program aborted.\n");
		    exit (1);
		}
		printf ("no valid process id, try once more!\n");
	    }
	    else break;
	}
        if (++count > 5) {
	    printf ("invalid input, program aborted.\n");
	    exit (1);
	}
    }
    return (sel_id);
}

double ask_lambda ()
{
    double sel_lambda;
    int count = 0;

    for (;;) {
	if (isatty (fileno (stdin)))
	    printf ("enter lambda in microns (>= 0.001): ");
	if (gets (buf) && sscanf (buf, "%lf", &sel_lambda) == 1) {
	    if (sel_lambda < 0.001) {
		if (++count > 1) {
		    printf ("wrong again, sorry program aborted.\n");
		    exit (1);
		}
		printf ("not a legal value, try once more!\n");
	    }
	    else break;
	}
    }
    return (sel_lambda);
}

dmError (s)
char *s;
{
    if (dmerrno == DME_PRDATA)
        return;

    fprintf (stderr, "%s: ", argv0);
    dmPerror (s);
}
