#ifndef lint
static char *SccsId = "@(#)cig.c 4.21 (TU-Delft) 06/12/92";
#endif
/**********************************************************

Name/Version      : cig/4.21

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

Author(s)         : R. Paulussen
Creation date     : 16-Nov-1987
Modified by       : S. de Graaf
Modification date : 06-Apr-1988
Modification date : 22-Aug-1988
Modification date : 08-Dec-1988
Modification date : 17-Oct-1989
Modification date : 17-Dec-1990 (4.08)
Modification date : 05-Feb-1991 (4.10)
Modification date : 27-Feb-1991 (4.14)
Modification date : 12-Jun-1991 (4.15)
Modification date : 11-Dec-1991 (4.17)
Modification date : 16-Jan-1992 (4.18)
Modification date : 20-Jan-1992 (4.19)
Modification date : 22-Jan-1992 (4.20)
Modification date : 12-Jun-1992 (4.21)


        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) 1988, All rights reserved
***********************************************************
MADE BY:
	Delft University of Technology
	Department of Submicron Technology
	Lorentzweg 1
	DELFT
	The Netherlands
***********************************************************
    Program CIG (Convert ICD-data to GDS II-data)
===========================================================
    This program reads data form the ICD database and put
    it into a GDS II-formatted file.

    CIG input files are:    info file
			    term file
			    box file
			    mc file
			    nor file

    The output file(s): <maincell>.gds.<vol_nr>  (binary)

**********************************************************/
#include <time.h>
#include "stdio.h"
#include "signal.h"
#include "ctype.h"
#include "dmincl.h"
#include "defs.h"
#ifdef ESE
#include "eseOption.h"
#include "tversion.h"
#endif

#define PUTBYTE(c) putc ((char)(c), fpdata)

#define ALLOC(ptr,type) { \
    if(!(ptr=(struct type *)malloc(sizeof(struct type)))) \
	pr_exit (A, 14, 0); }

struct c_elmt {
    char          *c_name;
    char          *r_name;
    DM_CELL       *c_key;
    DM_PROJECT    *p_key;
    struct c_elmt *c_hash;
    struct c_elmt *c_next;
};

#define HASHSIZE 100
struct c_elmt *hashlist[HASHSIZE];
struct c_elmt *gen_list;
struct c_elmt *gll;

char    file_name[BUFLEN],	/* file name    */
       *libname,		/* libname      */
       *cellname,		/* name of the current cell */
       *ref_name,		/* ref name of current ref. */
       *strcpy (),		/* system routine */
       *malloc (),		/* system routine */
      **maskname,
        buf[BUFLEN],
        buf1[DM_MAXLAY + 1],
        buf2[DM_MAXLAY + 1];

int     byte_cnt = 0;
int     take_cnt = 0;
int     mf = 1000;		/* multiplication factor */
int     min_long;		/* minimum value (before mf) */
int     max_long;		/* maximum value (before mf) */
int     maskcode;		/* layer number */
int     masknr[DM_MAXNOMASKS];	/* mask numbers */
int     uppercase = 0;		/* strings to uppercase */
char   *bmlist = 0;
int     verbose = 0;
int     aflag = 0;		/* if true, no arefs */
int     iflag = 0;		/* if true, process imp. cells */
int     rflag = 0;		/* if true, use remote names */

#ifndef ESE
char   *argv0 = "cig";		/* Program Name */
char   *use_msg =		/* Command Line */
"\nUsage: %s maincell [-a] [-f mf] [-i] [-r] [-s] [-t tapelength] [-d density] [-m mlist] [-u] [-v]\n\n";
#else
char   *argv0 = "getgds2";	/* Program Name */
char   *use_msg =		/* Command Line */
        "\nUsage: %s [options] cell\n\n";
#endif

int     byteswap,
	angle,
	check_flag = 0,		/* value checking needed   */
	magnify,		/* magnification factor    */
        refl,			/* rotation and reflection */
        file_len,		/* length of GDS file      */
        label_len,		/* length of GDS label     */
        vol_nr = 0,		/* volume number           */
        tape_len = 2400,
        density = 1600,		/* tape parameters         */
        year, mon, day, hour, min, sec,	/* time specification  */
        TAPE_LEN_VL = 0,	/* tapelength specified flag   */
        TAPE_DEN_VL = 0,	/* tape density specified flag */
        TAPE_MUL_VL = 0;	/* split file in volumes if nessasary */

struct tm  *timeptr, *localtime ();

long    cell_nrmc,		/* number of cell-calls in a cell */
        t_code[3],		/* unique tape code */
        timeval,
        time (),		/* time spec. */
        round ();

#ifdef ESE
OptionSpec optionSpecs[] = {
    { "", NO, eseHelp, (void *) optionSpecs,
            "usage:     getgds2 [options] cell\nOptions (may be abbreviated) are:"},
    { "%etext", NO, eseText, (void *) NULL,
            "    -%etext:              print the '(int) &etext' number" },
    { "%help", NO, eseHelpAll, (void *) optionSpecs,
            "    -%help:               print this list" },
    { "release", NO, esePrintString, (void *) TOOLVERSION,
            "    -release:             print the release number of this tool"},
    { "help", NO, eseHelp, (void *) optionSpecs,
            "    -help:                print this list" },
    { "noarefs", NO, eseTurnOn, (void *) & aflag,
            "    -noarefs:             expand arefs to srefs" },
    { "impcells", NO, eseTurnOn, (void *) & iflag,
            "    -impcells:            processing of imported cells" },
    { "remnames", NO, eseTurnOn, (void *) & rflag,
            "    -remnames:            use remote names" },
    { "split", NO, eseTurnOn, (void *) & TAPE_MUL_VL,
            "    -split:               create multi-reel files" },
    { "mulfac", YES, eseAssignIntArgument, (void *) &mf,
            "    -mulfac value:        specify multipl.factor (>= 1, def: 1000)" },
    { "length", YES, eseAssignIntArgument, (void *) &tape_len,
            "    -length value:        specify tape-length (600, 1200 or 2400 ft., def: 2400)" },
    { "density", YES, eseAssignIntArgument, (void *) &density,
            "    -density value:       specify tape-density (800 or 1600 bpi, def: 1600)" },
    { "masklist", YES, eseAssignArgument, (void *) &bmlist,
            "    -masklist file:       use 'file' as basic masklist instead of default" },
    { "uppercase", NO, eseTurnOn, (void *) &uppercase,
            "    -uppercase:           convert cell names to upper case" },
    { "verbose", NO, eseTurnOn, (void *) &verbose,
            "    -verbose:             enable verbose mode" },
    { (char *) 0, (char) 0, (IFP) 0, (void *) 0, (char *) 0 },
};
#endif

double  poly_coor[MAX_COOR * 2 + 2];
double  unit;			/* GDS unit for GDS file */
double  cos (), sin (), atof ();

DM_PROJECT *default_project;	/* project of top cell */
DM_PROJECT *project;		/* current project */
DM_PROCDATA *process;
DM_CELL *mod_key;

FILE *fpdata;

main (argc, argv)
int   argc;
char *argv[];
{
    int     sig_handler ();	/* signal handler */
    int     atoi ();
    DM_STREAM *fp;
    FILE   *fp_bml;
    char   *bp;
    int     gds_laynr;
    int     nolays;		/* max. number of mask codes */
    register struct c_elmt *clp;
    register int    i, j;

    nolays = 1;
    bp = (char *) &nolays;
    byteswap = (*bp == 1 ? 1 : 0);

#ifdef ESE
    if (eseOptionHandler (argc, argv, optionSpecs, 1, &libname)) usage ();
    if (!libname) usage ();
#else
    if (argc < 2) usage ();
    if (*argv[1] == '-' || *argv[1] == '\0') {
	PE "%s: error: illegal maincell name '%s'\n", argv0, argv[1]);
	usage ();
    }

    if (strlen (argv[1]) > DM_MAXNAME) {
	PE "%s: warning: maincell '%s' truncated to %d characters\n",
	    argv0, argv[1], DM_MAXNAME);
	argv[1][DM_MAXNAME] = '\0';
    }
    libname = argv[1];

    /*
    ** check/read options:
    */
    for (j = 2; j < argc; ++j) {

	if (argv[j][0] != '-') usage ();

	switch (argv[j][1]) {
	    case 'a': 		/* don't generate arefs */
		aflag = 1;
		break;
	    case 'f': 		/* multipl. factor specified */
		if (++j >= argc) usage ();
		mf = atoi (argv[j]);
		break;
	    case 'i': 		/* processing of imported cells */
		iflag = 1;
		break;
	    case 'r': 		/* use remote names */
		rflag = 1;
		break;
	    case 's': 		/* split in tape volumes */
		if (TAPE_MUL_VL) usage ();
		TAPE_MUL_VL = 1;
		break;
	    case 't': 		/* tape length specified */
		if (TAPE_LEN_VL) usage ();
		if (!TAPE_MUL_VL) break;
		TAPE_LEN_VL = 1;
		if (++j >= argc) usage ();
		tape_len = atoi (argv[j]);
		break;
	    case 'd': 		/* tape density specified */
		if (TAPE_DEN_VL) usage ();
		if (!TAPE_MUL_VL) break;
		TAPE_DEN_VL = 1;
		if (++j >= argc) usage ();
		density = atoi (argv[j]);
		break;
	    case 'm': 
		if (++j >= argc) usage ();
		bmlist = argv[j];
		break;
	    case 'u': 
		++uppercase;
		break;
	    case 'v': 
		++verbose;
		break;
	    default: 
		PE "%s: -%c: unknown option\n", argv0, argv[j][1]);
		usage ();
	}
    }
#endif
    if (bmlist) PE "%s: using '%s' as basic masklist\n", argv0, bmlist);
    if (aflag) PE "%s: no arefs (expanded to srefs)\n", argv0);
    if (iflag) PE "%s: processing of imported cells\n", argv0);
    if (rflag) PE "%s: try to use remote cell names\n", argv0);
    if (uppercase) PE "%s: uppercase mode\n", argv0);

    if (TAPE_MUL_VL) {
	PE "%s: splitting mode\n", argv0);

	if (density != 800 && density != 1600) {
	    density = 1600;
	    PE "%s: \7ILLEGAL density specified\n", argv0);
	}

	PE "%s: density set to %d bpi\n", argv0, density);

	if (tape_len != 600 && tape_len != 1200 && tape_len != 2400) {
	    tape_len = 2400;
	    PE "%s: \7ILLEGAL tape length specified\n", argv0);
	}

	PE "%s: tape length set to %d ft\n", argv0, tape_len);

	switch (tape_len) {
	    case 600: 
		if (density == 800)
		    file_len = NR_BLOCKS_L * BLOCKSIZE;
		else
		    file_len = NR_BLOCKS_H * BLOCKSIZE;

	    case 1200: 
		if (density == 800)
		    file_len = NR_BLOCKS_L * BLOCKSIZE;
		else
		    file_len = NR_BLOCKS_H * BLOCKSIZE;
		file_len *= 2;

	    case 2400: 
		if (density == 800)
		    file_len = NR_BLOCKS_L * BLOCKSIZE;
		else
		    file_len = NR_BLOCKS_H * BLOCKSIZE;
		file_len *= 4;
	}
	label_len = 3 * BASE_LEN + SHORT + 3 * LONG + strlen (libname);
	if (label_len % 2) ++label_len;
	file_len -= label_len;
    }
    else {
	if (verbose) PE "%s: no splitting mode\n", argv0);
	density = 0;
	tape_len = 0;
    }

    if ((i = sizeof (int)) != LONG) pr_exit (A, 13, 0);
    if (mf < 1) {
	mf = 1;
	PE "%s: illegal multipl. factor (set to 1)\n", argv0);
    }
    if (verbose) PE "%s: multipl. factor = %d\n", argv0, mf);
    min_long = MIN_LONG / mf;
    max_long = MAX_LONG / mf;

    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);

    dmInit (argv0);
    project = dmOpenProject (DEFAULT_PROJECT, PROJ_READ);
    process = (DM_PROCDATA *) dmGetMetaDesignData (PROCESS, project);
    unit = project -> lambda;

    default_project = project;
    maskname = process -> mask_name;
    nolays = process -> nomasks;

    if (!bmlist)
	bmlist = (char *) dmGetMetaDesignData (PROCPATH,
		project, "bmlist.gds");

    if (!(fp_bml = fopen (bmlist, "r"))) pr_exit (A, 1, bmlist);

    for (j = 0; j < nolays; ++j) masknr[j] = -1;

    while (fgets (buf, BUFLEN, fp_bml)) {
	if (*buf == '#') continue;
	if (sscanf (buf, "%s%s", buf1, buf2) != 2) pr_exit (A, 2, bmlist);
	for (i = 0; i < nolays; ++i)
	    if (strcmp (buf1, maskname[i]) == 0) break;
	if (i >= nolays) pr_exit (A, 3, buf1);
	if (masknr[i] >= 0) pr_exit (A, 4, buf1);
	gds_laynr = atoi (buf2);
	if (gds_laynr < 0 || gds_laynr > MAX_GDS_LAYNR) pr_exit (A, 5, buf2);
	for (j = 0; j < nolays; ++j)
	    if (masknr[j] == gds_laynr) pr_exit (W, 6, buf2);
	masknr[i] = gds_laynr;
    }

    fclose (fp_bml);

    if (verbose) {
	for (j = 0; j < nolays; ++j)
	    PE "%s: using GDS layer# %2d for mask '%s'\n",
		argv0, masknr[j], maskname[j]);
    }

    /* open first volume */
    open_write ();
    write_header ();

    mod_key = dmCheckOut (project, libname,
		    WORKING, DONTCARE, LAYOUT, READONLY);
    gmc.imported = 0;
    add_cell (libname);
    gen_list -> c_key = mod_key;

    fp = dmOpenStream (mod_key, "info", "r");
    if (dmGetDesignData (fp, GEO_INFO) <= 0) pr_exit (A, 10, libname);
    dmCloseStream (fp, COMPLETE);
    if (ginfo.bxl < min_long || ginfo.byb < min_long
	|| ginfo.bxr > max_long || ginfo.byt > max_long) ++check_flag;

    /* init hashlist with local cell names */
    for (clp = gen_list;;) {
	if (check_flag) check_elmt_bbox (clp -> c_name);
	fp = dmOpenStream (mod_key, "mc", "r");
	while (dmGetDesignData (fp, GEO_MC) > 0)
	    if (!gmc.imported) add_cell (gmc.cell_name);
	dmCloseStream (fp, COMPLETE);
	if (clp = clp -> c_next) {
	    mod_key = dmCheckOut (project, clp -> c_name,
			    ACTUAL, DONTCARE, LAYOUT, READONLY);
	    clp -> c_key = mod_key;
	}
	else break;
    }

    for (clp = gen_list; clp; clp = clp -> c_next) {
	project  = clp -> p_key;
	cellname = clp -> r_name;
	if (verbose) PE "%s: *** processing cell '%s'\n", argv0, cellname);
	if (!(mod_key = clp -> c_key)) {
	    mod_key = dmCheckOut (project, clp -> c_name,
			    ACTUAL, DONTCARE, LAYOUT, READONLY);
	    if (check_flag) check_elmt_bbox (cellname);
	}
	write_bgn (BGNSTR);
	write_string (cellname, STRNAME);
	get_term ();
	get_box ();
	get_nor ();
	get_mc ();
	write_nodata (ENDSTR);
	dmCheckIn (mod_key, COMPLETE);
    }

    write_nodata (ENDLIB);
    close_write ();

    dmQuit ();
    PE "%s: -- program finished --\n", argv0);
    exit (0);
}

usage ()
{
    PE use_msg, argv0);
    exit (1);
}

/* BEGIN OF FILLING CELL GENERATION LIST SECTION
**
** In uppercase mode, the cell names must be unique!
** A special string compare function StrCmp() is used
** for this purpose.
** Note that in uppercase mode the same hash index is
** used for upper- and lowercase strings (to find them).
*/
add_cell (name)
char *name;
{
    register char *s;
    register DM_PROJECT *proj;
    register struct c_elmt *hp;
    register int  hv = 0;
    register int  nr = 0;
    char *a = name;

    if (gmc.imported && (iflag || rflag)) {
	proj = dmFindProjKey (IMPORTED, a, project, &name, LAYOUT);
    }
    else { /* local */
	proj = project;
    }

    s = name - 1;
    if (uppercase) {
	while (*++s) {
	    hv += *s;
	    if (islower (*s)) ++nr;
	}
	hv -= nr * 32;
    }
    else {
	while (*++s) hv += *s;
    }
    hv %= HASHSIZE;
    for (hp = hashlist[hv]; hp; hp = hp -> c_hash) {
	if (hp -> p_key == proj && !strcmp (hp -> c_name, name)) {
	    ref_name = hp -> r_name;
	    return; /* found */
	}
    }

    ref_name = name;
    if (*name == 'A' && s - name == 8 && name[1] == '_' && name[7] == '_') {
	goto use_another;
    }
    if (proj != default_project && !rflag) {
	goto use_another;
    }
    if (rflag || nr) /* uppercase mode and lowercase letter(s) found */
    for (hp = hashlist[hv]; hp; hp = hp -> c_hash) {
	if (StrCmp (hp -> c_name, name)) {
use_another:
	    sprintf (buf, "A_%05d_", ++take_cnt);
	    if (proj != default_project)
		PE "%s: using name '%s' for '%s' of '%s'\n",
		    argv0, buf, name, proj -> dmpath);
	    else
		PE "%s: using name '%s' for '%s'\n",
		    argv0, buf, name);
	    ref_name = buf;
	    break;
	}
    }
    ALLOC (hp, c_elmt);
    if (!(s = malloc (++s - name))) pr_exit (A, 14, 0);
    else strcpy (s, name);
    hp -> c_name = s;
    if (ref_name == buf) {
	if (!(s = malloc (10))) pr_exit (A, 14, 0);
	else strcpy (s, ref_name);
    }
    hp -> r_name = s;
    hp -> c_key  = 0;
    hp -> p_key  = proj;
    hp -> c_hash = hashlist[hv];
    hashlist[hv] = hp;

    if (!gmc.imported || iflag) { /* add to gen_list */
	hp -> c_next = 0;
	if (!gll) gen_list = hp;
	else gll -> c_next = hp;
	gll = hp;
    }
    else {
	PE "%s: warning: '%s'", argv0, a);
	if (a != ref_name) PE "(%s)", ref_name);
	PE " is reference of imported cell!\n");
    }
}

int
StrCmp (S1, S2)
char *S1, *S2;
{
    register char *s1 = S1;
    register char *s2 = S2;
    while (*s1 && *s2) {
	if (islower (*s1)) {
	    if (islower (*s2)) {
		if (*s1 != *s2) return (0);
	    }
	    else
		if (*s1 - 32 != *s2) return (0);
	}
	else if (islower (*s2)) {
	    if (*s1 != *s2 - 32) return (0);
	}
	else if (*s1 != *s2) return (0);
	++s1; ++s2;
    }
    if (*s1 || *s2) return (0);
    return (1);
}
/* END OF FILLING CELL GENERATION LIST SECTION */

/* BEGIN OF GET INFORMATION SECTION
**
** These procedures extract information out of the
** input_files, one line each time.
** Since the EBPG has a restriction on the size of some
** of its parameters (< 2**15), and ICD not, they are tested.
*/
get_term ()
{
    DM_STREAM *fpterm;

    fpterm = dmOpenStream (mod_key, "term", "r");
    while (dmGetDesignData (fpterm, GEO_TERM) > 0) {
	if ((maskcode = masknr[gterm.layer_no]) < 0) {
	    if (maskcode == -1) {
		masknr[gterm.layer_no] = -2;
		pr_exit (W, 7, maskname[gterm.layer_no]);
	    }
	    continue;
	}
	term_processing (gterm.term_name);
    }
    dmCloseStream (fpterm, COMPLETE);
}

#define	PROP_TERMSIDE	59
#define	PROP_TERMLAY	60
#define	PROP_INSTANCE	61
#define	PROP_TERMINAL	62

/****
Properties PROP_TERMSIDE PROP_TERMLAY worden gevolgt door een integer
Properties PROP_INSTANCE PROP_TERMINAL worden gevolgt door een string

De terminal richting mag zijn:
****/

#define BOTTOM	0
#define RIGHT	1
#define	TOP	2
#define	LEFT	3
#define	ANY_DIRECTION	99

term_processing (name)
char *name;
{
    register int i, j;
    register long dx, dy, xl, xr, yb, yt;
    char mask[32];
    char new_name[128];

    dx = (gterm.nx > 0) ? gterm.dx * mf : 0;
    dy = (gterm.ny > 0) ? gterm.dy * mf : 0;
    xl = gterm.xl * mf;
    xr = gterm.xr * mf;
    yb = gterm.yb * mf;
    yt = gterm.yt * mf;

    for (i = 0; i <= gterm.nx; ++i) {
	for (j = 0; j <= gterm.ny; ++j) {
	    write_box (xl, xr, yb + j * dy, yt + j * dy);

	    write_short (PROPATTR, PROP_TERMLAY);
	    sprintf (mask, "%d", maskcode);
	    write_string (mask, PROPVALUE);

	    write_short (PROPATTR, PROP_TERMSIDE);
	    sprintf (mask, "%d", ANY_DIRECTION);
	    write_string (mask, PROPVALUE);

	    write_short (PROPATTR, PROP_TERMINAL);
	    if (gterm.nx == 0 && gterm.ny == 0)
		strcpy (new_name, name);
	    else if (gterm.nx == 0)
		sprintf (new_name, "%s_%d", name, j);
	    else if (gterm.ny == 0)
		sprintf (new_name, "%s_%d", name, i);
	    else
		sprintf (new_name, "%s_%d_%d", name, i, j);
	    write_string (new_name, PROPVALUE);

	    write_nodata (ENDEL);
	}
	xl += dx; xr += dx;
    }
}

get_box ()
{
    DM_STREAM *fpbox;

    fpbox = dmOpenStream (mod_key, "box", "r");
    while (dmGetDesignData (fpbox, GEO_BOX) > 0) {
	if ((maskcode = masknr[gbox.layer_no]) < 0) {
	    if (maskcode == -1) {
		masknr[gbox.layer_no] = -2;
		pr_exit (W, 8, maskname[gbox.layer_no]);
	    }
	    continue;
	}
	box_processing ();
    }
    dmCloseStream (fpbox, COMPLETE);
}

get_nor ()
{
    DM_STREAM *fpnor;
    register int i, nrcoor;
    double  x1, y1, x2, y2, dd;

    fpnor = dmOpenStream (mod_key, "nor", "r");
    while (dmGetDesignData (fpnor, GEO_NOR_INI) > 0) {
	if ((maskcode = masknr[gnor_ini.layer_no]) < 0) {
	    if (maskcode == -1) {
		masknr[gnor_ini.layer_no] = -2;
		pr_exit (W, 9, maskname[gnor_ini.layer_no]);
	    }
	    for (i = 0; i < gnor_ini.no_xy; ++i)
		dmGetDesignData (fpnor, GEO_NOR_XY);
	    continue;
	}

	if (gnor_ini.no_xy > MAX_COOR) {
	    PE "%s: number of co-ordinate pairs in a polygon\n", argv0);
	    PE "of cell '%s' is greater than %d\n", cellname, MAX_COOR);
	    die ();
	}

	nrcoor = 0;
	switch (gnor_ini.elmt) {
	    case SBOX_NOR: 
		dmGetDesignData (fpnor, GEO_NOR_XY);
		x1 = gnor_xy.x;
		y1 = gnor_xy.y;
		dmGetDesignData (fpnor, GEO_NOR_XY);
		x2 = gnor_xy.x;
		y2 = gnor_xy.y;
		dd = ((x2 - x1) + (y2 - y1)) / 2;
		poly_coor[nrcoor++] = round (x1);
		poly_coor[nrcoor++] = round (y1);
		poly_coor[nrcoor++] = round (x1 + dd);
		poly_coor[nrcoor++] = round (y1 + dd);
		poly_coor[nrcoor++] = round (x2);
		poly_coor[nrcoor++] = round (y2);
		poly_coor[nrcoor++] = round (x2 - dd);
		poly_coor[nrcoor++] = round (y2 - dd);
		poly_processing (nrcoor);
		break;
	    case RECT_NOR: 
	    case POLY_NOR: 
		for (i = 0; i < gnor_ini.no_xy; ++i) {
		    dmGetDesignData (fpnor, GEO_NOR_XY);
		    poly_coor[nrcoor++] = round (gnor_xy.x);
		    poly_coor[nrcoor++] = round (gnor_xy.y);
		}
		poly_processing (nrcoor);
		break;
	    case WIRE_NOR: 
		dmGetDesignData (fpnor, GEO_NOR_XY);
		dd = gnor_xy.x;
		x1 = y1 = 0;
		for (i = 1; i < gnor_ini.no_xy; ++i) {
		    dmGetDesignData (fpnor, GEO_NOR_XY);
		    x1 += gnor_xy.x;
		    y1 += gnor_xy.y;
		    poly_coor[nrcoor++] = round (x1);
		    poly_coor[nrcoor++] = round (y1);
		}
		write_path (poly_coor, nrcoor, round (dd));
		break;
	    case CIRCLE_NOR: 
		poly_coor[4] = 0;
		poly_coor[6] = 0;
		poly_coor[7] = 0;
		for (i = 0; i < gnor_ini.no_xy; ++i) {
		    dmGetDesignData (fpnor, GEO_NOR_XY);
		    poly_coor[nrcoor++] = gnor_xy.x;
		    poly_coor[nrcoor++] = gnor_xy.y;
		}
		circle_processing ();
		break;
	    default: 
		PE "%s: element of cell '%s' is not a polygon or circle\n",
		    argv0, cellname);
		die ();
	}
    }
    dmCloseStream (fpnor, COMPLETE);
}

get_mc ()
{
    DM_STREAM *fpmc;

    cell_nrmc = 0;
    fpmc = dmOpenStream (mod_key, "mc", "r");
    while (dmGetDesignData (fpmc, GEO_MC) > 0) mc_processing ();
    dmCloseStream (fpmc, COMPLETE);
}
/* END OF GET INFORMATION SECTION */

/* BEGIN OF PROCESSING SECTION
**
** This section contains the various procedures
** that converts the data obtained in
** the "get information" section.
*/
box_processing ()
{
    register int i, j;
    register long dx, dy, xl, xr, yb, yt;

    dx = (gbox.nx > 0) ? gbox.dx * mf : 0;
    dy = (gbox.ny > 0) ? gbox.dy * mf : 0;
    xl = gbox.xl * mf;
    xr = gbox.xr * mf;
    yb = gbox.yb * mf;
    yt = gbox.yt * mf;

    for (i = 0; i <= gbox.nx; ++i) {
	for (j = 0; j <= gbox.ny; ++j) {
	    write_box (xl, xr, yb + j * dy, yt + j * dy);
	    write_nodata (ENDEL);
	}
	xl += dx; xr += dx;
    }
}

poly_processing (nr)
int nr;
{
    double  fx (), fy ();
    double  x0, y0, x1, y1, x, y, xa, ya, xb, yb;
    int     x0_xaold, x0_xanew, fxyold, fxynew, richting;
    register int i;

    x0 = poly_coor[0];
    y0 = poly_coor[1];
    x1 = poly_coor[2];
    y1 = poly_coor[3];
    x = poly_coor[4];
    y = poly_coor[5];

    if (x0 < x1)
	richting = 1;
    else
	if (x0 > x1)
	    richting = 2;
	else
	    if (y0 < y1)
		richting = 3;
	    else
		richting = 4;

    if (richting == 3 || richting == 4) {
	x0_xaold = fx0_min_xa (x0, x);
	for (i = 6; i < nr - 1; i += 2) {
	    xa = poly_coor[i];
	    ya = poly_coor[i + 1];
	    x0_xanew = fx0_min_xa (x0, xa);
	    if (x0_xanew != x0_xaold && x0_xanew != 0) {
		x0_xaold = x0_xanew;
		xb = poly_coor[i - 2];
		yb = poly_coor[i - 1];
		y = fy (xa, ya, xb, yb, x0);
	    }
	}
    }
    else {
	fxyold = fxy (x0, y0, x1, y1, x, y);
	for (i = 6; i < nr - 1; i += 2) {
	    xa = poly_coor[i];
	    ya = poly_coor[i + 1];
	    fxynew = fxy (x0, y0, x1, y1, xa, ya);
	    if (fxynew != fxyold && fxynew != 0) {
		fxyold = fxynew;
		xb = poly_coor[i - 2];
		yb = poly_coor[i - 1];
		x = fx (x0, y0, x1, y1, xa, ya, xb, yb);
	    }
	}
    }

    write_bound (poly_coor, nr);
}

/*
** Circle_processing converts a circle from ICD
** to one or more polygons.
*/
circle_processing ()
{
    double *poly_co;
    double  cx, cy, ri, ro, aa, sa, la, st;
    int     xpo, ypo, xpi, ypi, xpo_old, ypo_old, xpi_old, ypi_old;
    int     dxo, dyo, dxi, dyi, dxo_old, dyo_old, dxi_old, dyi_old;
    int     ready = 0;
    register int ti, to;

    cx = poly_coor[0]; /* center x */
    cy = poly_coor[1]; /* center y */
    ro = poly_coor[2]; /* radius outside */
    ri = poly_coor[3]; /* radius inside */
    if (ri < 0 || ro <= ri) {
	PE "%s: in cell '%s', illegal circle radius (ro = %.3f, ri = %.3f)\n",
	    argv0, cellname, ro, ri);
	die ();
    }

    sa = poly_coor[6]; /* start angle */
    if ((la = poly_coor[7]) == 0) la = 360; /* last angle */
    if (sa < 0 || sa >= 360 || la <= 0 || la > 360 || la == sa) {
	PE "%s: in cell '%s', illegal circle angles (sa = %.3f, la = %.3f)\n",
	    argv0, cellname, sa, la);
	die ();
    }
    if (sa >= la) sa -= 360; /* la > sa */

    if ((st = poly_coor[4]) == 0) st = 0.01; /* minimum step angle */
    else if (st > 0) {
	if (st < 8) ti = 360.0 / st;
	else {
	    ti = st;
	    if (ti % 8) ti = 360.0 / st;
	}
	if (ti > 8) {
	    ti /= 8; ti *= 8; /* make it modulo 8 */
	}
	st = 360.0 / ti;
    }
    if (st < 0.01 || st > 45) {
	PE "%s: in cell '%s', illegal circle step angle (st = %.3f)\n",
	    argv0, cellname, st);
	die ();
    }

    for (aa = 270; aa > sa;) aa -= 90;
    while (aa <= sa) aa += st;
    st *= (3.14159265 / 180); /* step angle */
    aa *= (3.14159265 / 180); /* first start angle > sa */
    sa *= (3.14159265 / 180); /* start angle */
    la *= (3.14159265 / 180); /* last angle */

    xpi_old = round (cx + ri * cos (sa));
    ypi_old = round (cy + ri * sin (sa));
    xpo_old = round (cx + ro * cos (sa));
    ypo_old = round (cy + ro * sin (sa));

    while (!ready) {
	poly_co = poly_coor;
	poly_coor[0] = xpi_old;
	poly_coor[1] = ypi_old;
	poly_coor[2] = xpo_old;
	poly_coor[3] = ypo_old;
	dxo_old = dyo_old = dxi_old = dyi_old = 0;
	to = 4;     /* first free x position (outside) */
	ti = MAX_COOR * 2 - 1; /* y position (inside)  */

	while (aa < la && ti > to + 2) {
	    xpo = round (cx + ro * cos (aa));
	    ypo = round (cy + ro * sin (aa));
	    if (xpo != xpo_old || ypo != ypo_old) {
		dxo = xpo_old - xpo;
		dyo = ypo_old - ypo;
		if (dxo == dxo_old && dyo == dyo_old) to -= 2;
		else { dxo_old = dxo; dyo_old = dyo; }
		poly_coor[to++] = xpo_old = xpo;
		poly_coor[to++] = ypo_old = ypo;
	    }
	    if (ri > 0) {
		xpi = round (cx + ri * cos (aa));
		ypi = round (cy + ri * sin (aa));
		if (xpi != xpi_old || ypi != ypi_old) {
		    dxi = xpi_old - xpi;
		    dyi = ypi_old - ypi;
		    if (dxi == dxi_old && dyi == dyi_old) ti += 2;
		    else { dxi_old = dxi; dyi_old = dyi; }
		    poly_coor[ti--] = ypi_old = ypi;
		    poly_coor[ti--] = xpi_old = xpi;
		}
	    }
	    aa += st;
	}

	if (aa >= la) { /* set last point exact on position */
	    ready = 1;
	    xpo = round (cx + ro * cos (la));
	    ypo = round (cy + ro * sin (la));
	    if (xpo != xpo_old || ypo != ypo_old) {
		dxo = xpo_old - xpo;
		dyo = ypo_old - ypo;
		if (dxo == dxo_old && dyo == dyo_old) to -= 2;
		poly_coor[to++] = xpo;
		poly_coor[to++] = ypo;
	    }
	    if (ri > 0) {
		xpi = round (cx + ri * cos (la));
		ypi = round (cy + ri * sin (la));
		if (xpi != xpi_old || ypi != ypi_old) {
		    dxi = xpi_old - xpi;
		    dyi = ypi_old - ypi;
		    if (dxi == dxi_old && dyi == dyi_old) ti += 2;
		    poly_coor[ti--] = ypi;
		    poly_coor[ti--] = xpi;
		}
	    }
	    else {
		if (poly_coor[2] == xpo && poly_coor[3] == ypo) {
		    to -= 4;
		    poly_co += 2;
		    if (poly_co[0] == poly_co[2] &&
			poly_co[0] == poly_co[to - 2]) {
			poly_co += 2;
			to -= 2;
		    }
		}
		else {
		    dxo_old = poly_coor[2] - poly_coor[0];
		    dyo_old = poly_coor[3] - poly_coor[1];
		    dxo = poly_coor[0] - xpo;
		    dyo = poly_coor[1] - ypo;
		    if (dxo == dxo_old && dyo == dyo_old) {
			poly_co += 2;
			to -= 2;
		    }
		}
	    }
	}

	if (to < ti) { /* place ti- after to-points */
	    if (ti + 1 < MAX_COOR * 2)
	    while (++ti < MAX_COOR * 2) { /* x position */
		poly_coor[to++] = poly_coor[ti++]; /* copy_x */
		poly_coor[to++] = poly_coor[ti];   /* copy_y */
	    }
	}
	else to = MAX_COOR * 2;

	write_bound (poly_co, to);
    }
}

long
round (real)
double  real;
{
    return (long) ((real > 0) ? (real * mf + 0.5) : (real * mf - 0.5));
}

fxy (x0, y0, x1, y1, x, y)
double  x0, y0, x1, y1, x, y;
{
/* This function decides of the point (x,y) is above,
** under or on the line, which is described by the
** points (x0,y0) and (x1,y1).
*/
    double  tus_res;
    tus_res = (double) (y0 - y1) / (x0 - x1);
    tus_res = y - y1 - tus_res * (x - x1);
    if (tus_res > 0) return (1);
    if (tus_res < 0) return (-1);
    return (0);
}

fx0_min_xa (x0, xa)
double  x0, xa;
{
/* This function tests for the maximum of two numbers
** and returns 1 when the second variable the largest is.
*/
    if (xa > x0) return (1);
    if (xa < x0) return (-1);
    return (0);
}

double
fy (xa, ya, xb, yb, x0)
double  xa, ya, xb, yb, x0;
{
/* This function returns the y-value of the intersection
** of the line, which is described by the points (xa,ya)
** and (xb,yb) with the line x = x0.
*/
    double  tus_res;
    tus_res = (double) (ya - yb) / (xa - xb);
    return (x0 * tus_res + yb - xb * tus_res);
}

double
fx (x0, y0, x1, y1, xa, ya, xb, yb)
double  x0, y0, x1, y1, xa, ya, xb, yb;
{
/* This function returns the x-value of the intersection
** of the line, which is described by the points (x0,y0)
** and (x1,y1) with the line, which is described by the
** points (xa,ya) and (xb,yb).
*/
    double  tus_res1, tus_res2;
    if (xa == xb) return (xa);
    tus_res1 = (double) (y0 - y1) / (x0 - x1);
    tus_res2 = (double) (ya - yb) / (xa - xb);
    return ((y1 - x1 * tus_res1 - yb + xb * tus_res2) / (tus_res2 - tus_res1));
}

mc_processing ()
{
    long    sfx, sfy;

    add_cell (gmc.cell_name);

    ++cell_nrmc;
    if (gmc.mtx[0] == 0) {
	if (gmc.mtx[3] > 0) {
	    angle = 90;
	    if (gmc.mtx[1] > 0) {
		refl = 1;	/* MX+R90 */
		sfy = gmc.mtx[1];
	    }
	    else {
		refl = 0;	/* R90 */
		sfy = -gmc.mtx[1];
	    }
	    sfx = gmc.mtx[3];
	}
	else {
	    angle = 270;
	    if (gmc.mtx[1] > 0) {
		refl = 0;	/* R270 */
		sfy = gmc.mtx[1];
	    }
	    else {
		refl = 1;	/* MX+R270 */
		sfy = -gmc.mtx[1];
	    }
	    sfx = -gmc.mtx[3];
	}
    }
    else {
	if (gmc.mtx[0] > 0) {
	    angle = 0;
	    if (gmc.mtx[4] > 0) {
		refl = 0;	/* R0 */
		sfy = gmc.mtx[4];
	    }
	    else {
		refl = 1;	/* MX+R0 */
		sfy = -gmc.mtx[4];
	    }
	    sfx = gmc.mtx[0];
	}
	else {
	    angle = 180;
	    if (gmc.mtx[4] > 0) {
		refl = 1;	/* MX+R180 */
		sfy = gmc.mtx[4];
	    }
	    else {
		refl = 0;	/* R180 */
		sfy = -gmc.mtx[4];
	    }
	    sfx = -gmc.mtx[0];
	}
    }

    if (sfx != 1 || sfy != 1) {
	if (sfx != sfy || sfx < 2) {
	    PE "%s: sfx/sfy error in %ld-th cell-call of cell '%s'\n",
		argv0, cell_nrmc, cellname);
	    die ();
	}
	magnify = sfx;
    }
    else magnify = 0;

    if (gmc.nx == 0 && gmc.ny == 0)
	write_sref (gmc.mtx[2], gmc.mtx[5]);
    else
	write_aref ();
}
/* END OF PROCESSING SECTION */

/* BEGIN OF WRITING SECTION */
/*
** Write box-record to the outputfile.
*/
write_box (xl, xr, yb, yt)
long xl, xr, yb, yt;
{
    write_nodata (BOUNDARY);
    write_short (LAYER, maskcode);
    write_short (DATATYPE, 1);
    write_rec_header (10 * LONG, XY);
    print_long (xl); print_long (yb);
    print_long (xr); print_long (yb);
    print_long (xr); print_long (yt);
    print_long (xl); print_long (yt);
    print_long (xl); print_long (yb);
}

/*
** Write HEADER, BGNLIB, LIBNAME, UNITS.
*/
write_header ()
{
    timeval = time (0L);
    timeptr = localtime (&timeval);
    year = 1900 + timeptr -> tm_year;
    mon = 1 + timeptr -> tm_mon;
    day = timeptr -> tm_mday;
    hour = timeptr -> tm_hour;
    min = timeptr -> tm_min;
    sec = timeptr -> tm_sec;

    /* Calculate the tape-code.
    ** This must be an unique value for each library.
    */
    t_code[0] = year + mon + day;
    t_code[1] = day + hour + min;
    t_code[2] = min + sec;

    write_short (HEADER, VERSION);
    write_bgn (BGNLIB);
    write_string (libname, LIBNAME);
    write_rec_header (2 * DOUBLE, UNITS);
    print_double (1.0 / (double)mf);
    print_double (unit * 1E-6 / (double)mf);
}

/*
** Write a No_Data_Present record to the outputfile.
*/
write_nodata (type)
int type;
{
    if ((type & 0XFF) != 0) pr_exit (A, 11, "write_nodata");
    write_rec_header (0, type);
}

/*
** Write ONE Two-Byte_Integer record to the outputfile.
*/
write_short (type, value)
int type, value;
{
    int dt = type & 0XFF;
    if (dt != 1 && dt != 2) pr_exit (A, 11, "write_short");
    write_rec_header (SHORT, type);
    print_short (value);
}

/*
** Write a ASCII_String record to the outputfile.
*/
write_string (str, type)
char *str;
int   type;
{
    register int len, c;

    if ((type & 0XFF) != 6) pr_exit (A, 11, "write_string");
    if ((len = strlen (str)) > MAX_STRLEN) pr_exit (A, 12, "512");
    write_rec_header (len + len % 2, type);
    while (c = *str++) {
	if (uppercase && islower (c)) c -= 32;
	PUTBYTE (c);
    }
    if (len % 2) PUTBYTE (c);
}

write_rec_header (len, type)
int len, type;
{
    len += BASE_LEN;
    if (TAPE_MUL_VL) if (byte_cnt + len > file_len) next_volume ();
    byte_cnt += len;
    PUTBYTE (len >> 8);
    PUTBYTE (len);
    PUTBYTE (type >> 8);
    PUTBYTE (type);
}

/*
** Write a LIBBGN or STRBGN-record to the outputfile.
*/
write_bgn (type)
int type;
{
    timeval = time (0L);
    timeptr = localtime (&timeval);

    write_rec_header (12 * SHORT, type);
    print_short (year);
    print_short (mon);
    print_short (day);
    print_short (hour);
    print_short (min);
    print_short (sec);
    print_short (timeptr -> tm_year + 1900);
    print_short (timeptr -> tm_mon + 1);
    print_short (timeptr -> tm_mday);
    print_short (timeptr -> tm_hour);
    print_short (timeptr -> tm_min);
    print_short (timeptr -> tm_sec);
}

/*
** Write a BOUNDARY-record to the outputfile.
*/
write_bound (poly_co, nr)
double *poly_co;
int nr;
{
    register int i, j, k;
    double dx, dy;

    dx = (gnor_ini.nx > 0) ? round (gnor_ini.dx) : 0;
    dy = (gnor_ini.ny > 0) ? round (gnor_ini.dy) : 0;

    poly_co[nr++] = poly_co[0];
    poly_co[nr++] = poly_co[1];

    for (i = 0; i <= gnor_ini.nx; ++i) {
	for (j = 0; j <= gnor_ini.ny; ++j) {
	    write_nodata (BOUNDARY);
	    write_short (LAYER, maskcode);
	    write_short (DATATYPE, 1);
	    write_rec_header (nr * LONG, XY);
	    for (k = 0; k < nr;) {
		print_long ((long) (poly_co[k++] + i * dx));
		print_long ((long) (poly_co[k++] + j * dy));
	    }
	    write_nodata (ENDEL);
	}
    }
}

/*
** Write a PATH-record to the outputfile.
*/
write_path (poly_co, nr, width)
double *poly_co;
int  nr;
long width;
{
    register int i, j, k;
    double dx, dy;

    dx = (gnor_ini.nx > 0) ? round (gnor_ini.dx) : 0;
    dy = (gnor_ini.ny > 0) ? round (gnor_ini.dy) : 0;

    for (i = 0; i <= gnor_ini.nx; ++i) {
	for (j = 0; j <= gnor_ini.ny; ++j) {
	    write_nodata (PATH);
	    write_short (LAYER, maskcode);
	    write_short (DATATYPE, 1);
	    write_rec_header (LONG, WIDTH);
	    print_long (width);
	    write_rec_header (nr * LONG, XY);
	    for (k = 0; k < nr;) {
		print_long ((long) (poly_co[k++] + i * dx));
		print_long ((long) (poly_co[k++] + j * dy));
	    }
	    write_nodata (ENDEL);
	}
    }
}

/*
** Write a Structure Ref. record to the outputfile.
*/
write_sref (tx, ty)
long tx, ty;
{
    write_nodata (SREF);
    write_string (ref_name, SNAME);
    if (refl || magnify || angle) {
	write_short (STRANS, refl << 15);
	if (magnify) {
	    write_rec_header (DOUBLE, MAG);
	    print_double ((double) magnify);
	}
	if (angle) {
	    write_rec_header (DOUBLE, ANGLE);
	    print_double ((double) angle);
	}
    }
    write_rec_header (2 * LONG, XY);
    if (check_flag) {
	if (tx < min_long || tx > max_long) ref_error ("tx", tx);
	if (ty < min_long || ty > max_long) ref_error ("ty", ty);
    }
    print_long (tx * mf);
    print_long (ty * mf);

    write_short (PROPATTR, PROP_INSTANCE);
    write_string (gmc.inst_name, PROPVALUE);
    write_nodata (ENDEL);
}

/*
** Write an Array Ref. record to the outputfile.
*/
write_aref ()
{
    long x1, x2, x3, y1, y2, y3, dx, dy;
    register int col, row;

    x1 = gmc.mtx[2];
    y1 = gmc.mtx[5];
    dx = gmc.dx;
    dy = gmc.dy;

    if (aflag) { /* don't generate arefs */
	for (row = 0; row <= gmc.ny; ++row)
	    for (col = 0; col <= gmc.nx; ++col) {
		write_sref (x1 + col * dx, y1 + row * dy);
	    }
	return;
    }

    write_nodata (AREF);
    write_string (ref_name, SNAME);
    if (refl || magnify || angle) {
	write_short (STRANS, refl << 15);
	if (magnify) {
	    write_rec_header (DOUBLE, MAG);
	    print_double ((double) magnify);
	}
	if (angle) {
	    write_rec_header (DOUBLE, ANGLE);
	    print_double ((double) angle);
	}
    }

    col = gmc.nx + 1;
    row = gmc.ny + 1;
    if (dx < 0) { dx = -dx; x1 -= gmc.nx * dx; }
    if (dy < 0) { dy = -dy; y1 -= gmc.ny * dy; }
    dx = (col > 1 ? col * dx : 0);
    dy = (row > 1 ? row * dy : 0);

    if (angle == 0 || angle == 180) {
	if (angle == 0) {
	    if (dy && refl)  y1 += dy - gmc.dy;
	}
	else { /* angle == 180 */
	    if (dx)          x1 += dx - gmc.dx;
	    if (dy && !refl) y1 += dy - gmc.dy;
	}
	x2 = x1 + dx;
	y3 = y1 + dy;
	y2 = y1;
	x3 = x1;
	if (check_flag) {
	    if (x1 < min_long || x1 > max_long) ref_error ("tx", x1);
	    if (y1 < min_long || y1 > max_long) ref_error ("ty", y1);
	    if (dx && x2 > max_long) ref_error ("tx", x2);
	    if (dy && y3 > max_long) ref_error ("ty", y3);
	}
    }
    else {
	col = gmc.ny + 1;
	row = gmc.nx + 1;
	if (angle == 90) {
	    if (dx && !refl) x1 += dx - gmc.dx;
	}
	else { /* angle == 270 */
	    if (dx && refl)  x1 += dx - gmc.dx;
	    if (dy)          y1 += dy - gmc.dy;
	}
	x3 = x1 + dx;
	y2 = y1 + dy;
	x2 = x1;
	y3 = y1;
	if (check_flag) {
	    if (x1 < min_long || x1 > max_long) ref_error ("tx", x1);
	    if (y1 < min_long || y1 > max_long) ref_error ("ty", y1);
	    if (dx && x3 > max_long) ref_error ("tx", x3);
	    if (dy && y2 > max_long) ref_error ("ty", y2);
	}
    }

    write_rec_header (2 * SHORT, COLROW);
    print_short (col);
    print_short (row);
    write_rec_header (6 * LONG, XY);
    print_long (x1 * mf);
    print_long (y1 * mf);
    print_long (x2 * mf);
    print_long (y2 * mf);
    print_long (x3 * mf);
    print_long (y3 * mf);
    write_short (PROPATTR, PROP_INSTANCE);
    write_string (gmc.inst_name, PROPVALUE);
    write_nodata (ENDEL);
}

/*
** Write TAPENUM-, TAPECODE- and LIBNAME-record.
*/
write_label ()
{
    write_short (TAPENUM, vol_nr);
    write_rec_header (3 * LONG, TAPECODE);
    print_long (t_code[0]);
    print_long (t_code[1]);
    print_long (t_code[2]);
    write_string (libname, LIBNAME);
}

/*
** Go to the next volume.
*/
next_volume ()
{
    TAPE_MUL_VL = 0;
    if (vol_nr == 1) {
	write_label ();
	file_len += label_len - 3 * SHORT;
    }
    else {
	write_short (TAPENUM, vol_nr);
    }
    close_write ();
    open_write ();
    write_label ();
    TAPE_MUL_VL = 1;
}

/*
** Open GDS output file.
*/
open_write ()
{
    sprintf (file_name, "%s.gds.%d", libname, ++vol_nr);
    fpdata = fopen (file_name, "w");
    if (!fpdata) pr_exit (A, 0, file_name);
    byte_cnt = 0;
}

/*
** Fill the last block with 0000 and close output.
*/
close_write ()
{
    register int len, j;

    if ((len = byte_cnt % BLOCKSIZE) > 0) {
	len = BLOCKSIZE - len;
	for (j = 0; j < len; ++j) PUTBYTE (0);
    }
    fclose (fpdata);
}

/*
** Convert a double to eight-byte real number in GDS format.
*/
print_double (ln)
double ln;
{
    double  xx, ldexp (), frexp ();
    register char *bp;
    unsigned long m_r, m_l, m_h;
    int     e_h, expo;

    if (ln != 0) {
	xx = (ln < 0 ? -ln : ln);
	xx = frexp (xx, &expo);
	xx = ldexp (xx, 28 + expo % 4);
	m_l = (unsigned long) xx;
	m_r = (unsigned long) ldexp (xx - m_l, 32);
	if (m_l & 0XF0000000) {
	    e_h = 65 + expo / 4;
	    m_h = (m_l >> 8);
	    m_l = (m_l << 24) | (m_r >> 8);
	}
	else {
	    e_h = 64 + expo / 4;
	    m_h = (m_l >> 4);
	    m_l = (m_l << 28) | (m_r >> 4);
	}
	if (e_h > 127) { /* overflow */
	    pr_exit (A, 15, 0);
	    e_h = 127;
	    m_h = 0X00FFFFFF;
	    m_l = 0XFFFFFFFF;
	}
	else if (e_h < 0) { /* underflow */
	    goto zero;
	}
	if (ln < 0) e_h |= 0X80;
	m_h |= (e_h << 24);
    }
    else { /* zero value */
zero:
	m_l = m_h = 0;
    }

    bp = (char *) &m_h;
    if (byteswap) { /* sun386 mode (byte swap) */
	bp += 3;
	PUTBYTE (*bp--);
	PUTBYTE (*bp--);
	PUTBYTE (*bp--);
	PUTBYTE (*bp);
	bp = (char *) &m_l;
	bp += 3;
	PUTBYTE (*bp--);
	PUTBYTE (*bp--);
	PUTBYTE (*bp--);
	PUTBYTE (*bp);
    }
    else {
	PUTBYTE (*bp++);
	PUTBYTE (*bp++);
	PUTBYTE (*bp++);
	PUTBYTE (*bp);
	bp = (char *) &m_l;
	PUTBYTE (*bp++);
	PUTBYTE (*bp++);
	PUTBYTE (*bp++);
	PUTBYTE (*bp);
    }
}

/*
** Convert a int to two-byte integer number in GDS format.
*/
print_short (ln)
int ln;
{
    PUTBYTE (ln >> 8);
    PUTBYTE (ln);
}

/*
** Convert a int to four-byte integer number in GDS format.
*/
print_long (ln)
long ln;
{
    PUTBYTE (ln >> 24);
    PUTBYTE (ln >> 16);
    PUTBYTE (ln >>  8);
    PUTBYTE (ln);
}
/* END OF WRITING SECTION */

/* BEGIN OF MISCELLANEOUS SECTION */
check_elmt_bbox (cell)
char *cell;
{
    DM_STREAM *fp;
    fp = dmOpenStream (mod_key, "info", "r");
    if (dmGetDesignData (fp, GEO_INFO) <= 0) pr_exit (A, 10, cell);
    if (dmGetDesignData (fp, GEO_INFO) <= 0) pr_exit (A, 10, cell);
    if (dmGetDesignData (fp, GEO_INFO) <= 0) pr_exit (A, 10, cell);
    dmCloseStream (fp, COMPLETE);
    if (ginfo.bxl < min_long) cell_error ("xl", cell, ginfo.bxl);
    if (ginfo.byb < min_long) cell_error ("yb", cell, ginfo.byb);
    if (ginfo.bxr > max_long) cell_error ("xr", cell, ginfo.bxr);
    if (ginfo.byt > max_long) cell_error ("yt", cell, ginfo.byt);
}

cell_error (s, cell, value)
char *s;
char *cell;
long  value;
{
    PE "%s: element b%s of cell '%s' out of limit (= %ld)\n",
	argv0, s, cell, value);
    exceeds_msg ();
}

ref_error (s, value)
char *s;
long  value;
{
    PE "%s: %s out of limit in %ld-th cell-call of cell '%s' (= %ld)\n",
	argv0, s, cell_nrmc, cellname, value);
    exceeds_msg ();
}

exceeds_msg ()
{
    PE "%s: exceeds min/max value after multip. with %d\n", argv0, mf);
    PE "%s: minimum and maximum values are: %ld, %ld\n",
	argv0, min_long, max_long);
    PE "%s: choice a smaller multiplication factor (option -f)\n", argv0);
    die ();
}

dmError (s)
char *s;
{
    dmPerror (s);
    PE "%s: error in DMI function\n", argv0);
    die ();
}

sig_handler (sig)		/* signal handler */
int     sig;
{
    signal (sig, SIG_IGN);	/* ignore signal */
    PE "%s: recieved signal: %d\n", argv0, sig);
    die ();
}

static				/* error messages */
char   *err_list[] =
{
     /*  0 */ "cannot create file '%s'",
     /*  1 */ "cannot read file '%s'",
     /*  2 */ "after read of file '%s'",
     /*  3 */ "unknown mask '%s' in bmlist-file",
     /*  4 */ "already used mask '%s' in bmlist-file",
     /*  5 */ "illegal GDS masknr '%s' in bmlist-file",
     /*  6 */ "already used GDS masknr '%s' in bmlist-file",
     /*  7 */ "terminal layer '%s' skipped",
     /*  8 */ "box layer '%s' skipped",
     /*  9 */ "nor layer '%s' skipped",
     /* 10 */ "cannot read bounding box of cell '%s'",
     /* 11 */ "%s: illegal data type",
     /* 12 */ "too long string (> %s)",
     /* 13 */ "sizeof int not equal to 4 bytes",
     /* 14 */ "cannot alloc",
     /* 15 */ "floating point overflow",
};

/*
** Give a error message (mode=A(bort)) or a warning (mode=W(arning))
** When the mode is E(xit), the program exits without error message.
** When the mode is W or I(nfo), the program does not an exit.
*/
pr_exit (mode, errno, cs)
int mode, errno;
char *cs;
{
    PE "%s: ", argv0);
    if (mode == W) PE "warning: ");
    if (mode == A) PE "error: ");

    if (errno < 0 || errno >= sizeof (err_list) / sizeof (char *)) {
	PE "due to number '%d'", errno);
	if (cs && *cs) PE ", %s", cs);
    }
    else
	PE err_list[errno], cs);
    PE "\n");

    if (mode == A) die ();
}

die ()
{
    if (fpdata) unlink (file_name);
    dmQuit ();
    PE "%s: -- program aborted --\n", argv0);
    exit (1);
}
/* END OF MISCELLANEOUS SECTION */
