#ifndef lint
static char *SccsId = "@(#)cgi.c 4.27 (TU-Delft) 12/11/91";
#endif
/**********************************************************

Name/Version      : cgi/4.27

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

Author(s)         : R. Paulussen
Creation date     : 04-Jan-1988
Modified by       : S. de Graaf
Modification date : 25-Mar-1988
Modification date : 20-May-1988
Modification date : 22-Aug-1988
Modification date : 19-Sep-1988
Modification date : 08-Dec-1988
Modification date : 20-Apr-1990 (3.13)
Modification date : 25-Jun-1990 (4.03)
Modification date : 22-Nov-1990 (4.12)
Modification date : 11-Jan-1991 (4.14)
Modification date : 25-Feb-1991 (4.23)
Modification date : 12-Jun-1991 (4.25)
Modification date : 11-Dec-1991 (4.27)


        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
***********************************************************
PROCESS DESCRIPTION:
	Convert a GDS II file to the ICD database.
**********************************************************/
#include "stdio.h"
#include "incl.h"
#ifdef ESE
#include "eseOption.h"
#include "tversion.h"
#endif

#define AppendCell(cell) append_tree (cell, &mod_tree)
#define CheckCell(cell)  check_tree (cell, mod_tree)

#define Expect(id) {if (debug && rec_id != id) \
    PE "Expect '%s', got '%s'\n", \
    recordName (id), recordName (rec_id));}

#define Expect2(id1, id2) {if (debug && rec_id != id1 && rec_id != id2) \
    PE "Expect '%s' or '%s', got '%s'\n", \
    recordName (id1), recordName (id2), recordName (rec_id));}

FILE   *fp_gds;			/* GDS filepointer               */

int     pass,			/* number of the pass (1 of 2)   */
        nr_files = 0,		/* number of GDS files           */
        vol_nr,			/* volume number                 */
        t_code[3],		/* unique tape code              */
        Index,			/* record position in buffer     */
        rec_len,		/* record length                 */
        rec_id,			/* record id                     */
        rec_nr = 1,		/* record number                 */
        nr_coor,		/* number of co-ordinaten        */
        N_VOL,			/* flag: next_volume procedure   */
        width,			/* width of path                 */
        pathtype,		/* path type                     */
        magnify,		/* magnification factor		 */
        mir,			/* mirroring                     */
        mode45,			/* 45 degree test mode           */
        layset[DM_MAXNOMASKS];

double  double_val[MAX_DOUBLE],	/* array reals           */
        unit,			/* DB unit in micron     */
        resolution,		/* lambda/GDS unit       */
        angle;			/* angle                 */

char    file_name[BUFLEN],	/* name of GDS file      */
      **maskname,
      **vector,			/* pointer to GDS files  */
        nbuf[16],		/* temp. number buffer   */
        libname[DM_MAXNAME + 1],/* name of library       */
        gds_name[MAX_STRLEN + 1],/* names from GDS file  */
        buf[BUFLEN],
        buf1[DM_MAXLAY + 1],
        buf2[DM_MAXLAY + 1];

int     masknr[MAX_GDS_LAYNR + 1];/* mask numbers */
int     verbose = 0;
int     nr_of_cells = 0;

static				/* error messages */
char   *err_list[] =
{
     /*  0 */ "-- program finished --",
     /*  1 */ "missing GDS file(s)",
     /*  2 */ "cannot open GDS file '%s'",
     /*  3 */ "cannot read tape block from GDS file '%s'",
     /*  4 */ "last co-ordinates not equal to first pair",
     /*  5 */ "illegal %s",
     /*  6 */ "absolute %s not supported",
     /*  7 */ "too less co-ordinates",
     /*  8 */ "structure name too long, truncated!",
     /*  9 */ "cell '%s' not found",
     /* 10 */ "intersecting lines not allowed",
     /* 11 */ "illegal direction change in co-ordinates",
     /* 12 */ "equal co-ordinates not allowed",
     /* 13 */ "odd number of co-ordinates",
     /* 14 */ "lines (partly) overlap",
     /* 15 */ "too big direction change",
     /* 16 */ "cannot read info of cell '%s'",
     /* 17 */ "cannot allocate enough memory",
     /* 18 */ "cell '%s' not placed!",
     /* 19 */ "cell '%s' already exists, not overwritten!",
     /* 20 */ "interrupted due to signal '%s'",
     /* 21 */ "%s",
     /* 22 */ "in DMI function",
     /* 23 */ "multiple entry of cell '%s'",
     /* 24 */ "illegal increment (0 value(s))",
     /* 25 */ "illegal co-ordinates (%s)",
     /* 26 */ "empty cell '%s'",
     /* 27 */ "GDS unit < lambda, not allowed!",
     /* 28 */ "illegal layer number '%s'",
     /* 29 */ "too many co-ordinates",
     /* 30 */ "end of file detected",
     /* 31 */ "GDS unit changed into lambda value of %s micron",
     /* 32 */ "element '%s' skipped!",
     /* 33 */ "cell definition of cell '%s' had error",
     /* 34 */ "pathtype %s not supported (set to 0)",
     /* 35 */ "non 45 degree feature(s)",
     /* 36 */ "GDS file '%s' is not the correct volume",
     /* 37 */ "cannot access GDS file '%s'",
     /* 38 */ "file '%s' is not a regular file",
     /* 39 */ "illegal GDS masknr '%s' in bmlist file",
     /* 40 */ "already used GDS masknr '%s' in bmlist file",
     /* 41 */ "cannot read file '%s'",
     /* 42 */ "after read in file '%s'",
     /* 43 */ "unknown mask '%s' in bmlist file",
     /* 44 */ "already used mask '%s' in bmlist file",
     /* 45 */ "unsupported record type '%s', skipped!",
     /* 46 */ "unexpected record type '%s', skipped?",
     /* 47 */ "cannot find record type '%s' in GDS file",
     /* 48 */ "property attribute expected, found '%s'",
};

#ifndef ESE
char   *argv0 = "cgi";		/* Program Name */
char   *use_msg =		/* Command Line */
        "\nUsage: %s [-m mlist] [-4] [-v] [-d] GDS-file ...\n\n";
#else
char   *argv0 = "putgds2";	/* Program Name */
char   *use_msg =		/* Command Line */
        "\nUsage: %s [options] GDS-file ...\n\n";
#endif

int     debug = 0;
#define DEBUG  if(debug) fprintf(stderr,

struct cell {
    DM_CELL *ckey;
    int     defined;
    int     father;
    int     rec_no;
    long    offset;
    char   *cell_name;
    struct ref *son;
    struct cell *next;
};

struct ref {
    char   *refname;
    struct cell *father;
    struct cell *origin;
    struct ref *next;
};

struct cell *cell_start = NULL;
struct cell *cell_last  = NULL;
char   *bmlist = 0;

struct property {
    int     attr;
    char    value[DM_MAXNAME + 1];
} property[25];

#ifdef ESE
OptionSpec optionSpecs[] = {
    {
	"usage", NO, eseHelp, (void *) optionSpecs,
	    "-usage:     putgds2 [options] [GDS-file ...]\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"
    },
    {
	"help", NO, eseHelp, (void *) optionSpecs,
	    "    -help:                print this list"
    },
    {
	"release", NO, esePrintString, (void *) TOOLVERSION,
	    "    -release:             print the release number of this tool"
    },
    {
	"masklist", YES, eseAssignArgument, (void *) & bmlist,
	    "    -masklist  maskfile:  use 'maskfile' instead of the default one"
    },
    {
	"slant", NO, eseTurnOn, (void *) & mode45,
	    "    -slant:               check if only rectangular and 45 degree angles occur"
    },
    {
	"verbose", NO, eseTurnOn, (void *) & verbose,
	    "    -verbose:             print run-time information"
    },
    {
	"%debug", NO, eseTurnOn, (void *) & debug,
	    "    -%debug:              print debugging information"
    },
    {
	(char *) 0, (char) 0, (IFP) 0, (void *) 0, (char *) 0
    },
};
#endif

main (argc, argv)
int   argc;
char *argv[];
{
    int     sig_handler ();
    int     atoi ();
    FILE * fp_bml;
    int     gds_laynr;
    int     usage = 0;
    int     nolays;		/* max. number of mask codes */
    register int    i, j, iarg;

#ifdef ESE
    vector = (char **) calloc (argc, sizeof (char *));
    usage = eseOptionHandler (argc, argv, optionSpecs, argc, vector);
    if (!vector[0]) ++usage;
    for (nr_files = 0; vector[nr_files]; ++nr_files) ;
    --vector;
#else NOT ESE
    for (iarg = 1; iarg < argc && argv[iarg][0] == '-'; ++iarg) {
	j = iarg;
	for (i = 1; argv[j][i] != '\0'; ++i) {
	    switch (argv[j][i]) {
		case 'm':
		    bmlist = argv[++iarg];
		    break;
		case 'v':
		    ++verbose;
		    break;
		case 'd':
		    ++debug;
		    break;
		case '4':
		    ++mode45;
		    break;
		default:
		    ++usage;
		    PE "%s: -%c: unknown option\n", argv0, argv[j][i]);
	    }
	}
    }

    if (argc <= iarg) {
	PE "%s: no GDS file given\n", argv0);
	++usage;
    }

    vector = &argv[iarg - 1];
    nr_files = argc - iarg;
#endif
    if (usage) {
	PE use_msg, argv0);
	exit (1);
    }

    if ((i = sizeof (int)) != LONG) {
	pr_exit (A, 21, "sizeof int not equal to 4 bytes");
    }

    vol_nr = 1;
    open_gds ();

    signal (SIGHUP, SIG_IGN);	/* ignore hangup signal */
    if (signal (SIGINT, SIG_IGN) != SIG_IGN)
	signal (SIGINT, sig_handler);
    if (signal (SIGQUIT, SIG_IGN) != SIG_IGN)
	signal (SIGQUIT, sig_handler);
    if (signal (SIGTERM, SIG_IGN) != SIG_IGN)
	signal (SIGTERM, sig_handler);

    signal (SIGILL, sig_handler);
    signal (SIGFPE, sig_handler);
#ifdef SIGBUS /* [ps] */
    signal (SIGBUS, sig_handler);
#endif
    signal (SIGSEGV, sig_handler);

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

    for (i = 0; i <= MAX_GDS_LAYNR; ++i) masknr[i] = -1;

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

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

    while (fgets (buf, BUFLEN, fp_bml)) {
	if (*buf == '#') continue;
	if (sscanf (buf, "%s%s", buf1, buf2) != 2) pr_exit (A, 42, bmlist);
	for (i = 0; i < nolays; ++i) {
	    if (strcmp (buf1, maskname[i]) == 0) break;
	}
	if (i >= nolays) pr_exit (A, 43, buf1);
	if (layset[i]) pr_exit (W, 44, buf1);
	layset[i] = 1;
	gds_laynr = atoi (buf2);
	if (gds_laynr < 0 || gds_laynr > MAX_GDS_LAYNR) pr_exit (A, 39, buf2);
	if (masknr[gds_laynr] != -1) pr_exit (A, 40, buf2);
	masknr[gds_laynr] = i;
    }

    fclose (fp_bml);

    ini_modtree ();

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

    /*
    ** A GDS file may define cells (structures) before they
    ** are used.
    ** Therefore, make two passes, on first pass skip the hierarchical
    ** references and on the second pass, skip the primitives
    */
    gnor_ini.dx = gnor_ini.dy = 0;
    gnor_ini.nx = gnor_ini.ny = 0;
    gbox.dx = gbox.dy = gterm.dx = gterm.dy = 0;
    gbox.nx = gbox.ny = gterm.nx = gterm.ny = 0;

    /* first pass */
    pass = 1;
    read_gds ();
    fclose (fp_gds);

    if (verbose) dump_data ();

    /* second pass */
    vol_nr = 1;
    open_gds ();
    pass = 2;
    read_gds ();
    fclose (fp_gds);

    dmCkinAll (COMPLETE);
    dmCloseProject (dmproject, COMPLETE);
    dmQuit ();
    pr_exit (E, 0, 0);
}

/* For deleting buffer and fread call */
int
mygetc ()
{
    int  c;
    if ((c = fgetc (fp_gds)) == EOF) {
	pr_exit (I, 30, 0);
	pr_exit (A, 3, file_name);
    }
    ++Index;
    return (c);
}

/* Replacement of old NEXT_RECORD define */
next_rec ()
{
    while (Index++ < rec_len) fgetc (fp_gds);
    ++rec_nr;
}

/*
** Read GDS file.
*/
read_gds ()
{
    char    rbuf[20];
    long    offset;

    rec_nr = 1;

    if (pass > 1) {      /**** pass TWO ****/
	search_id (UNITS);
	NEXT_RECORD;
	jump_file ();
	return;
    }

    /**** pass ONE ****/
    check_rec_id (0, HEADER);
    NEXT_RECORD;
    check_rec_id (0, BGNLIB);
    NEXT_RECORD;
    check_rec_id (0, LIBNAME);
    read_rec_contents ();
    strcpy (libname, gds_name);
    if (verbose) PE "%s: GDS library name is '%s'\n", argv0, libname);
    search_id (UNITS);
    read_rec_contents ();
    if (double_val[1] > 0.125) pr_exit (A, 5, "unit");
    unit = ((double) ((long) (double_val[1] * 1E12 + 0.5))) * 1E-6;
    if (verbose) PE "%s: GDS database unit is %g micron\n", argv0, unit);
    resolution = 1.0;
    if (unit != dmproject -> lambda) {
	resolution = unit / dmproject -> lambda;
	sprintf (rbuf, "%.3f", dmproject -> lambda);
	pr_exit (I, 31, rbuf);
	PE "%s: GDS values multiplied by %g\n", argv0, resolution);
    }

    do {
	offset = ftell (fp_gds);
	read_rec_header ();
	NEXT_RECORD;
	switch (rec_id) {
	    case BGNSTR:
		read_str (offset, (struct cell *)NULL);
		break;
	    case ENDLIB:
		break;
	    default:
		Expect2 (BGNSTR, ENDLIB);
		pr_exit (A+R, 5, "syntax");
	}
    } while (rec_id != ENDLIB);
}

/* Walk in "right" order trough the input file */
jump_file ()
{
    register struct cell *cell_P, *help_P;
    register struct ref  *ref_P;

    /* Determine if cell has father or not */
    for (cell_P = cell_start; cell_P; cell_P = cell_P -> next) {
	for (ref_P = cell_P -> son; ref_P; ref_P = ref_P -> next) {
	    for (help_P = cell_start; help_P; help_P = help_P -> next) {
		if (strcmp (help_P -> cell_name, ref_P -> refname) == 0) {
		    help_P -> father = 1;
		    ref_P -> origin = help_P;
		    break;
		}
	    }
	}
    }

    /* For all ROOT cells, do define_cell () */
    for (cell_P = cell_start; cell_P; cell_P = cell_P -> next) {
	if (!cell_P -> father) define_cell (cell_P, 0);
    }

    /* For all NOT DEFINED cells, give message */
    for (cell_P = cell_start; cell_P; cell_P = cell_P -> next) {
	if (!cell_P -> defined) {
	    rec_nr = cell_P -> rec_no;
	    pr_exit (W+R, 18, cell_P -> cell_name);
	    dmCheckIn (cell_P -> ckey, QUIT);
	}
    }
}

/* Process cells in correct order */
define_cell (cell_P, level)
struct cell *cell_P;
int level;
{
    struct ref *ref_P;

    if (!cell_P || cell_P -> defined) return (0);
    if (level > nr_of_cells) {
	pr_exit (W, 21, "cell recursion!");
	return (1);
    }

    for (ref_P = cell_P -> son; ref_P; ref_P = ref_P -> next) {
	if (define_cell (ref_P -> origin, ++level)) return (1);
    }

    if (verbose) {
	PE "%s: *** Pass 2 busy with cell: %s\n", argv0, cell_P -> cell_name);
    }
    fseek (fp_gds, cell_P -> offset, 0);
    read_rec_header ();
    NEXT_RECORD;
    read_str (0L, cell_P);
    cell_P -> defined = 1;
    return (0);
}

/* Dump the data structures */
dump_data ()
{
    register struct cell *cell_P;
    register struct ref  *ref_P;

    for (cell_P = cell_start; cell_P; cell_P = cell_P -> next) {
	PE "\n%s: %s calls: ", argv0, cell_P -> cell_name);
	for (ref_P = cell_P -> son; ref_P; ref_P = ref_P -> next) {
	    PE "%s, ", ref_P -> refname);
	}
    }
    PE "\n");
}

/* Add reference to a cell */
add_mc (cell_P, cell_name)
struct cell *cell_P;
char   *cell_name;
{
    register struct ref *ref_P;

    if (CheckCell (cell_name)) { /* found */
	if (tree_ptr -> status) {
	    return;	/* Cell already in DB */
	}
	/* Cell already in FILE */
    }
    /*else Cell not yet in FILE */

    for (ref_P = cell_P -> son; ref_P; ref_P = ref_P -> next) {
	if (strcmp (cell_name, ref_P -> refname) == 0)
	    return;		/* Cell already refered */
    }
    ALLOC (ref_P, ref);
    ref_P -> refname = strsave (cell_name);
    ref_P -> father  = cell_P;
    ref_P -> origin  = NULL;
    ref_P -> next = cell_P -> son;
    cell_P -> son = ref_P;
}

/* Add cell to the cell list */
struct cell *
add_cell (offset, cell_name)
long    offset;
char   *cell_name;
{
    register struct cell *cell_P;

    ++nr_of_cells;
    ALLOC (cell_P, cell);
    if (!cell_last) {
	cell_start = cell_P;
	cell_last = cell_P;
    }
    else {
	cell_last -> next = cell_P;
	cell_last = cell_P;
    }
    cell_P -> defined = 0;
    cell_P -> father  = 0;
    cell_P -> offset  = offset;
    cell_P -> cell_name = strsave (cell_name);
    cell_P -> son  = NULL;
    cell_P -> next = NULL;
    cell_P -> ckey = NULL;
    return (cell_P);
}

char *
strsave (str)
char *str;
{
    char *cp = malloc (strlen (str) + 1);
    if (!cp) pr_exit (A, 17, 0);
    else strcpy (cp, str);
    return (cp);
}

/*
** Read a structure.
*/
read_str (offset, cell_P)
long  offset;
struct cell *cell_P;
{
    int MC;

    check_rec_id (0, STRNAME);
    read_rec_contents ();
    strcpy (ms_name, gds_name);

    if (pass == 2) rec_nr = cell_P -> rec_no;

    if (CheckCell (ms_name)) { /* found */
	if (pass == 1) {
	    pr_exit (W, 19, ms_name); /* already exist */
	    goto skip;
	}
	if (tree_ptr -> status) goto skip;
    }
    else { /* not found */
	if (pass == 2) {
	    pr_exit (A, 9, ms_name);
	    goto skip;
	}
    }

    DEBUG "Beginning '%s', pass %d\n", ms_name, pass);

    if (pass == 1) {
	cell_P = add_cell (offset, ms_name);
	cell_P -> rec_no = rec_nr;
	mod_key = dmCheckOut (dmproject, ms_name,
		WORKING, DONTCARE, LAYOUT, UPDATE);
	ini_bbbox = 1;
	MC = FALSE;
	fp_box = dmOpenStream (mod_key, "box", "w");
	fp_nor = dmOpenStream (mod_key, "nor", "w");
	fp_term = dmOpenStream (mod_key, "term", "w");
    }
    else { /* pass TWO */
	mod_key = cell_P -> ckey;
	ini_mcbbox = 1;
	fp_mc = dmOpenStream (mod_key, "mc", "w");
	fp_info = dmOpenStream (mod_key, "info", "w");
    }
    DEBUG "%s %x\n", ms_name, mod_key);

    read_rec_header ();
    NEXT_RECORD;
    if (rec_id == STRCLASS) {
	pr_exit (W+R, 45, recordName (rec_id));
	read_rec_header ();
	NEXT_RECORD;
    }

    while (rec_id != ENDSTR) {
	switch (rec_id) {
	    case BOUNDARY:
		if (pass == 2)  {
	    		search_id (ENDEL);
			break;
		}
		read_boundary ();
	    	check_for_prop ();
		break;
	    case PATH:
		if (pass == 2) {
	    		search_id (ENDEL);
			break;
		}
		read_path ();
	    	check_for_prop ();
		break;
	    case AREF:
	    case SREF:
		if (pass == 1) {
		    MC = TRUE;
		    check_rec_id (2, SNAME);
		    read_rec_contents ();
		    add_mc (cell_P, gds_name);
	    	    search_id (ENDEL);
		}
		else { /* pass TWO */
		    read_ref (rec_id);
			/* check_for_prop implied by read_ref */
		}
		break;
	    case TEXT:
	    case NODE:
	    case BOX:
		if (pass == 2)  {
	    		search_id (ENDEL);
			break;
		}
		pr_exit (W+R, 45, recordName (rec_id));
	    	search_id (ENDEL);
		break;
	    default:
		/* unexpected record type */
		pr_exit (W+R, 46, recordName (rec_id));
	    	search_id (ENDEL);
		break;
	}

	NEXT_RECORD;
	read_rec_header ();
	NEXT_RECORD;
    }

    AppendCell (ms_name);

    if (pass == 1) {
	tree_ptr -> status = FALSE;
	cell_P -> ckey = mod_key;
	if (ini_bbbox) {
	    if (!MC) pr_exit (W+R, 26, ms_name); /* empty */
	}
	else {
	    ALLOC (tree_ptr -> bbox, mod_bbox);
	    tree_ptr -> bbox -> xl = bbnd_xl;
	    tree_ptr -> bbox -> xr = bbnd_xr;
	    tree_ptr -> bbox -> yb = bbnd_yb;
	    tree_ptr -> bbox -> yt = bbnd_yt;
	}
	dmCloseStream (fp_box, COMPLETE);
	dmCloseStream (fp_nor, COMPLETE);
	dmCloseStream (fp_term, COMPLETE);
    }
    else { /* pass TWO */
	write_info ();
	dmCloseStream (fp_info, COMPLETE);
	dmCloseStream (fp_mc, COMPLETE);
	DEBUG "Checkin %s\n", ms_name);
	dmCheckIn (mod_key, COMPLETE);
    }

    DEBUG "Ending '%s'\n", ms_name);
    return;
skip:
    if (pass == 2 && verbose) {
	PE "%s: %d: *** Skipping cell: %s\n", argv0, rec_nr, ms_name);
    }
    search_id (ENDSTR);
    NEXT_RECORD;
}

/*
** Read a boundary-record and place a box or poly
** in the ICD database, if there are no errors.
** A syntax check is also done.
*/
read_boundary ()
{
    register int i;
    int rv = 1;

    check_rec_id (2, LAYER);
    read_rec_contents ();
    lay_code = int_val[0];

    check_rec_id (0, DATATYPE);
    NEXT_RECORD;

    check_rec_id (0, XY);
    nr_coor = (rec_len - 4) / LONG;
    read_rec_contents ();

    if (lay_code < 0 || lay_code > MAX_GDS_LAYNR || masknr[lay_code] == -1) {
	sprintf (nbuf, "%d", lay_code);
	pr_exit (W, 28, nbuf);
	goto skip;
    }
    if (nr_coor < 8) {
	pr_exit (W, 7, 0);
	goto skip;
    }
    if (nr_coor & 1) {
	pr_exit (W, 13, 0);
	goto skip;
    }
    if (int_val[0] != int_val[nr_coor - 2]
	    || int_val[1] != int_val[nr_coor - 1]) {
	/* last point not equal to first point */
	pr_exit (W, 4, 0);
	goto skip;
    }

    lay_code = masknr[lay_code];
    i = 3;
    if (nr_coor == 10) { /* check for orthogonal box */
	if (int_val[0] == int_val[2]) i = 5;
	while (i < nr_coor) {
	    if (int_val[i - 2] != int_val[i]) break;
	    if (i & 1) ++i;
	    else i += 3;
	}
    }
    if (i >= nr_coor) { /* orth. */
	i = 0;
	if (int_val[i] == int_val[i+2]) i = 2;
	rv = box (int_val[i], int_val[i+2], int_val[i+3], int_val[i+5]);
    }
    else
	rv = poly ();
skip:
    if (rv) pr_exit (W+R, 32, "boundary");
}

/*
** Read path element.
*/
read_path ()
{
    int rv = 1;
    check_rec_id (2, LAYER);
    read_rec_contents ();
    lay_code = int_val[0];

    check_rec_id (0, DATATYPE);
    NEXT_RECORD;

    pathtype = width = 0;
    read_rec_header ();
    if (rec_id == PATHTYPE) {
	read_rec_contents ();
	pathtype = int_val[0];
	read_rec_header ();
    }
    if (rec_id == WIDTH) {
	read_rec_contents ();
	width = int_val[0];
	read_rec_header ();
    }
    if (rec_id != XY) {
	NEXT_RECORD;
	check_rec_id (1, XY);
    }

    read_rec_contents ();
    nr_coor = (rec_len - 4) / LONG;

    if (lay_code < 0 || lay_code > MAX_GDS_LAYNR || masknr[lay_code] == -1) {
	sprintf (nbuf, "%d", lay_code);
	pr_exit (W, 28, nbuf);
	goto skip;
    }
    if (nr_coor < 4) {
	pr_exit (W, 7, 0);
	goto skip;
    }
    if (nr_coor & 1) {
	pr_exit (W, 13, 0);
	goto skip;
    }
    lay_code = masknr[lay_code];

    if (width <= 0) {
	pr_exit (W, 5, "width");
	goto skip;
    }

    rv = ese_swire ();
    if (rv == 0) return; /* successful processing of 45 degree path */

    if (pathtype != 0 && pathtype != 2) {
	sprintf (buf1, "%d", pathtype);
	pr_exit (W, 34, buf1);
	pathtype = 0;
    }
    rv = path ();
skip:
    if (rv) pr_exit (W+R, 32, "path");
}

/*
** Read an aref/sref-record.
*/
read_ref (id)
int id;
{
    IMPCELL    *ic;
    DM_PROJECT *proj;
    DM_CELL    *mc_key;
    DM_STREAM  *fp_mc_info;
    long    b_xl, b_xr, b_yb, b_yt, tmp, tx, ty;
    register long   a, b, c, d;

    /**** pass TWO ****/
    check_rec_id (2, SNAME);
    read_rec_contents ();
    strcpy (mc_name, gds_name);

    read_strans ();

    a = b = c = d = 0;
    switch ((long) angle) {	/* rotate (degrees) */
	case   0: a =  magnify; d =  magnify; break;
	case  90: b = -magnify; c =  magnify; break;
	case 180: a = -magnify; d = -magnify; break;
	case 270: b =  magnify; c = -magnify; break;
	default:
	    pr_exit (A+R, 5, "angle");
	    goto skip;
    }
    mir &= 0X8000;
    if (mir) {		/* mirror */
	b = -b;
	d = -d;
    }

    if (id == AREF) {
	Expect (COLROW);
	if (rec_id != COLROW) {
	    pr_exit (A+R, 5, "syntax");
	    goto skip;
	}
	read_rec_contents ();
	if (!b) { /* 0 or 180 degree */
	    nx = int_val[0];
	    ny = int_val[1];
	}
	else { /* 90 or 270 degree */
	    ny = int_val[0];
	    nx = int_val[1];
	}
	if (nx < 1 || ny < 1) {
	    pr_exit (W, 21, "COLROW value < 1");
	    goto skip;
	}
	read_rec_header ();
    }

    Expect (XY);
    if (rec_id != XY) {
	pr_exit (A+R, 5, "syntax");
	goto skip;
    }
    nr_coor = (rec_len - 4) / LONG;
    read_rec_contents ();

    dx = dy = 0;
    if (id == AREF) {
	if (nr_coor != 6) {
	    pr_exit (W, 25, "!= 6");
	    goto skip;
	}
	if (nx > 1) { /* x-copies */
	    if (!b) { /* 0 or 180 degree */
		dx = (int_val[2] - int_val[0]) / nx;
	    }
	    else { /* 90 or 270 degree */
		dx = (int_val[4] - int_val[0]) / nx;
	    }
	    if (dx < 0) dx = -dx;
	    if (!dx) pr_exit (W+R, 21, "AREF dx = 0");
	}
	if (ny > 1) { /* y-copies */
	    if (!b) { /* 0 or 180 degree */
		dy = (int_val[5] - int_val[1]) / ny;
	    }
	    else { /* 90 or 270 degree */
		dy = (int_val[3] - int_val[1]) / ny;
	    }
	    if (dy < 0) dy = -dy;
	    if (!dy) pr_exit (W+R, 21, "AREF dy = 0");
	}
	--nx;
	--ny;
    }
    else { /* sref */
	if (nr_coor != 2) {
	    pr_exit (W, 25, "!= 2");
	    goto skip;
	}
	nx = ny = 0;
    }

    /*
    ** Process aref and sref.
    */
    DEBUG "Instantiating '%s'\n", mc_name);

    if (!CheckCell (mc_name)) {
	pr_exit (W, 9, mc_name); /* not found */
	goto skip;
    }
    if (tree_ptr -> status == DEF_ERR) {
	pr_exit (W, 33, mc_name); /* cell def. error */
	goto skip;
    }

    ic = tree_ptr -> impcell;

    if (tree_ptr -> bbox) {
	b_xl = tree_ptr -> bbox -> xl;
	b_xr = tree_ptr -> bbox -> xr;
	b_yb = tree_ptr -> bbox -> yb;
	b_yt = tree_ptr -> bbox -> yt;
	DEBUG "%s %s: bbox present %d %d %d %d\n",
	    ms_name, mc_name, b_xl, b_xr, b_yb, b_yt);
    }
    else {
	if (ic) {
	    proj = dmOpenProject (ic -> dmpath, PROJ_READ);
	    mc_key = dmCheckOut (proj, ic -> cellname,
			ACTUAL, DONTCARE, LAYOUT, READONLY);
	}
	else {
	    mc_key = dmCheckOut (dmproject, mc_name,
			ACTUAL, DONTCARE, LAYOUT, READONLY);
	}
	if (!mc_key) {
	    pr_exit (A, 16, mc_name);
	    goto skip;
	}

	fp_mc_info = dmOpenStream (mc_key, "info", "r");
	if (dmGetDesignData (fp_mc_info, GEO_INFO) <= 0) {
	    pr_exit (A, 16, mc_name);
	    goto skip;
	}
	dmCloseStream (fp_mc_info, COMPLETE);
	dmCheckIn (mc_key, COMPLETE);

	ALLOC (tree_ptr -> bbox, mod_bbox);
	tree_ptr -> bbox -> xl = b_xl = ginfo.bxl;
	tree_ptr -> bbox -> xr = b_xr = ginfo.bxr;
	tree_ptr -> bbox -> yb = b_yb = ginfo.byb;
	tree_ptr -> bbox -> yt = b_yt = ginfo.byt;
	DEBUG "%s %s: bbox using checkout %d %d %d %d\n",
	    ms_name, mc_name, b_xl, b_xr, b_yb, b_yt);
    }

    gmc.imported = (ic) ? IMPORTED : LOCAL;
    strcpy (instance, ".");


    tmp = b_xl;
    b_xl = a * tmp + b * b_yb;
    b_yb = c * tmp + d * b_yb;
    tmp = b_xr;
    b_xr = a * tmp + b * b_yt;
    b_yt = c * tmp + d * b_yt;
    if (b_xl > b_xr) { tmp = b_xl; b_xl = b_xr; b_xr = tmp; } /* swap */
    if (b_yb > b_yt) { tmp = b_yb; b_yb = b_yt; b_yt = tmp; } /* swap */

    switch ((long) angle) {	/* rotate (degrees) */
	case   0:
	    if (mir) int_val[1] -= ny * dy;
	    break;
	case  90:
	    if (!mir) int_val[0] -= nx * dx;
	    break;
	case 180:
	    int_val[0] -= nx * dx;
	    if (!mir) int_val[1] -= ny * dy;
	    break;
	case 270:
	    if (mir) int_val[0] -= nx * dx;
	    int_val[1] -= ny * dy;
	    break;
    }

    tx = round_int (int_val[0], "tx");
    ty = round_int (int_val[1], "ty");
    dx = round_int (dx, "dx");
    dy = round_int (dy, "dy");

    b_xl += tx;
    b_xr += tx + nx * dx;
    b_yb += ty;
    b_yt += ty + ny * dy;

	    check_for_prop ();

    strcpy (gmc.cell_name, mc_name);
    strcpy (gmc.inst_name, instance);
    gmc.mtx[0] = a;
    gmc.mtx[1] = b;
    gmc.mtx[2] = tx;
    gmc.mtx[3] = c;
    gmc.mtx[4] = d;
    gmc.mtx[5] = ty;
    gmc.bxl = b_xl;
    gmc.bxr = b_xr;
    gmc.byb = b_yb;
    gmc.byt = b_yt;
    gmc.dx = dx;
    gmc.nx = nx;
    gmc.dy = dy;
    gmc.ny = ny;
    DEBUG "%s %s: calculated bbox %d %d %d %d\n\n",
		ms_name, mc_name, b_xl, b_xr, b_yb, b_yt);
    dmPutDesignData (fp_mc, GEO_MC);

    /* updating cellcall bounding box */
    if (ini_mcbbox) {
	mcbb_xl = b_xl;
	mcbb_xr = b_xr;
	mcbb_yb = b_yb;
	mcbb_yt = b_yt;
	ini_mcbbox = 0;
    }
    else {
	if (mcbb_xl > b_xl) mcbb_xl = b_xl;
	if (mcbb_xr < b_xr) mcbb_xr = b_xr;
	if (mcbb_yb > b_yb) mcbb_yb = b_yb;
	if (mcbb_yt < b_yt) mcbb_yt = b_yt;
    }
    return;
skip:
    pr_exit (W+R, 32, recordName (id));
}

/*
** Read strans and set angle, scaling and mirroring.
*/
read_strans ()
{
    mir = 0;
    angle = 0;
    magnify = 1;
    read_rec_header ();
    if (rec_id == STRANS) {
	read_rec_contents ();
	mir = int_val[0];
	if (mir & 0X2) pr_exit (W+R, 6, "angle");
	if (mir & 0X4) pr_exit (W+R, 6, "magnification");
	read_rec_header ();
	if (rec_id == MAG) {
	    read_rec_contents ();
	    magnify = (int) (double_val[0] + 0.5);
	    if ((double) magnify != double_val[0]) {
		PE "%s: %d: warning: rounding magnification from %.3f to %d\n",
		    argv0, rec_nr, double_val[0], magnify);
	    }
	    if (magnify < 1) {
		PE "%s: %d: warning: illegal magnification factor %d (set to 1)\n",
		    argv0, rec_nr, magnify);
		magnify = 1;
	    }
	    read_rec_header ();
	}
	if (rec_id == ANGLE) {
	    read_rec_contents ();
	    angle = (double) ((long) (double_val[0] + 0.5));
	    read_rec_header ();
	}
    }
}

/*
** Search for record-type id.
*/
search_id (id)
int     id;
{
    for (;;) {
	read_rec_header ();
	if (rec_id == id) return;
	if (rec_id == ENDLIB || rec_id == -1) break;
	NEXT_RECORD;
    }
    pr_exit (I, 47, recordName (id)); /* cannot find */
    pr_exit (A+R, 5, "syntax");
}

/*
** Read two- or four-byte integers (length SHORT or LONG).
*/
read_int (length)
int length;
{
    register int nr, i, value;

    nr = (rec_len - 4) / length;
    if (nr > NOINTS) {
	pr_exit (A, 29, 0);
	return;
    }

    for (i = 0; i < nr; ++i) {
	value = mygetc ();
	value = (value << 8) | mygetc ();
	if (length == LONG) {
	    value = (value << 8) | mygetc ();
	    value = (value << 8) | mygetc ();
	}
	int_val[i] = value;
    }
}

/*
** Read eight-byte real numbers.
*/
read_double ()
{
    double  ln, ldexp ();
    register int i, nr, e_h;
    unsigned int m_l, m_h;

    nr = (rec_len - 4) / 8;
    if (nr > MAX_DOUBLE) {
	pr_exit (A, 21, "too many values");
	return;
    }

    for (i = 0; i < nr; ++i) {
	e_h = mygetc ();
	m_h = mygetc ();
	m_h = (m_h << 8) | mygetc ();
	m_h = (m_h << 8) | mygetc ();
	m_l = mygetc ();
	m_l = (m_l << 8) | mygetc ();
	m_l = (m_l << 8) | mygetc ();
	m_l = (m_l << 8) | mygetc ();

	if (m_h) {
	    ln = m_l;
	    ln = ldexp (ln + 4294967296.0 * m_h, -56);
	    ln = ldexp (ln, 4 * ((e_h & 0X7F) - 64));
	    if (e_h & 0X80) ln = -ln;
	}
	else ln = 0;
	double_val[i] = ln;
    }
}

/*
** Read ASCII string.
*/
read_string ()
{
    register int nr, c, i, j = 0;

    if ((nr = rec_len - 4) > MAX_STRLEN) {
	pr_exit (A, 21, "too long string");
	goto skip;
    }

    for (i = 0; i < nr; ++i) {
	if ((c = mygetc ()) == '/') j = 0;
	else {
	    if (c == '$') c = '_';
	    gds_name[j++] = c;
	}
    }
    if (j && gds_name[j-1] == '\0') --j;

    if (j > DM_MAXNAME) {
	pr_exit (W, 8, 0);
	j = DM_MAXNAME;
    }
    else if (j == 0) {
	pr_exit (A, 21, "no string found");
    }
skip:
    gds_name[j] = '\0';
}

/*
** Read the header (length and id) of a GDS record.
*/
read_rec_header ()
{
    Index = 0;
    rec_len = mygetc () << 8;
    rec_len |= mygetc ();
    if (rec_len == 0) rec_len += 2;

    if (rec_len == 2) rec_id = -1;
    else {
	rec_id = mygetc () << 8;
	rec_id |= mygetc ();
    }

    if (!N_VOL && rec_id == TAPENUM) {
	next_volume ();
	read_rec_header ();
    }
}

/*
** Read contents of a GDS record.
*/
read_rec_contents ()
{
    switch (rec_id & 0XFF) {
	case 0XFF:
	case 0:
	case 4:
	    break;
	case 1:
	case 2:
	    read_int (SHORT);
	    break;
	case 3:
	    read_int (LONG);
	    break;
	case 5:
	    read_double ();
	    break;
	case 6:
	    read_string ();
	    break;
    }
    NEXT_RECORD;
}

/*
** Check record id.
*/
check_rec_id (nr, id)
int     nr, id;
{
    for (;;) {
	read_rec_header ();
	if (rec_id == id) return;
	if (--nr < 0 || rec_id == ENDLIB || rec_id == -1) break;
	NEXT_RECORD;
    }
    Expect (id);
    pr_exit (A+R, 5, "syntax");
}

/*
** Process the path type.
*/
path ()
{
    int xr, xl, yb, yt;
    int dir, prev_dir;
    register int i, delta_x, delta_y;

    for (i = 2; i < nr_coor; i += 2) {
	if (int_val[i]   != int_val[i-2]
	 && int_val[i+1] != int_val[i-1]) return (swire ());
    }

    if (width & 1) pr_exit (W, 21, "odd width");
    width /= 2;
    prev_dir = -1;
    for (i = 2; i < nr_coor;) {
	if (!(delta_x = int_val[i] - int_val[i-2])) {
	    if (!(delta_y = int_val[i+1] - int_val[i-1])) {
		pr_exit (W, 25, "dx=0,dy=0");
		return (1);
	    }
	    dir = 1; /* y-direction */
	}
	else {
	    dir = 0; /* x-direction */
	}
	if (dir == prev_dir) {
	    pr_exit (W, 25, "dir=prev_dir");
	    return (1);
	}
	prev_dir = dir;

	if (!dir) {		/* x-direction */
	    if (delta_x > 0) {
		xl = int_val[i] - delta_x - width;
		xr = int_val[i] + width;
	    }
	    else {
		xl = int_val[i] - width;
		xr = int_val[i] - delta_x + width;
	    }
	    yb = int_val[i+1] - width;
	    yt = int_val[i+1] + width;
	}
	else {			/* y-direction */
	    xl = int_val[i] - width;
	    xr = int_val[i] + width;
	    if (delta_y > 0) {
		yb = int_val[i+1] - delta_y - width;
		yt = int_val[i+1] + width;
	    }
	    else {
		yb = int_val[i+1] - width;
		yt = int_val[i+1] - delta_y + width;
	    }
	}
	i += 2;
	if (pathtype == 0) {
	    if (i == 4) { /* first box */
		if (!dir) xl += width;
		else      yb += width;
	    }
	    else if (i == nr_coor) { /* last box */
		if (!dir) xr -= width;
		else      yt -= width;
	    }
	}
	if (box (xl, xr, yb, yt)) return (1);
    }
    return (0); /* ok */
}

long
roundh (d)
double d;
{
    long i;
    i = (d < 0) ? (d - 0.0005) : (d + 0.9995);
    return (i);
}

long
roundl (d)
double d;
{
    long i;
    i = (d < 0) ? (d - 0.9995) : (d + 0.0005);
    return (i);
}

int
round_int (v, s)
int v;
char *s;
{
    tmp_d = resolution * v;
    v = (v < 0) ? (tmp_d - 0.5) : (tmp_d + 0.5);
    if (tmp_d - v > 0.01 || tmp_d - v < -0.01) {
	PE "%s: %d: warning: rounding %s from %.3f to %d\n",
	    argv0, rec_nr, s, tmp_d, v);
    }
    return (v);
}

box (xl, xr, yb, yt)
int  xl, xr, yb, yt;
{
    xl = round_int (xl, "xl");
    xr = round_int (xr, "xr");
    yb = round_int (yb, "yb");
    yt = round_int (yt, "yt");

    if (xl == xr || yb == yt) {
	pr_exit (W, 25, "x=x,y=y"); /* illegal box co-ordinates */
	return (1);
    }

    if (xl > xr) { tmp_i = xl; xl = xr; xr = tmp_i; } /* swap */
    if (yb > yt) { tmp_i = yb; yb = yt; yt = tmp_i; } /* swap */

    /* updating cell bounding box */
    if (ini_bbbox) {
	bbnd_xl = xl;
	bbnd_xr = xr;
	bbnd_yb = yb;
	bbnd_yt = yt;
	ini_bbbox = 0;
    }
    else {
	if (xl < bbnd_xl) bbnd_xl = xl;
	if (xr > bbnd_xr) bbnd_xr = xr;
	if (yb < bbnd_yb) bbnd_yb = yb;
	if (yt > bbnd_yt) bbnd_yt = yt;
    }

    gbox.layer_no = lay_code;
    gbox.bxl = gbox.xl = xl;
    gbox.bxr = gbox.xr = xr;
    gbox.byb = gbox.yb = yb;
    gbox.byt = gbox.yt = yt;
    dmPutDesignData (fp_box, GEO_BOX);
    return (0); /* ok */
}

/*
** Check the poly co-ordinates, calculate the bounding-box
** and place the data in the ICD database.
*/
poly ()
{
    long    b_xl, b_xr, b_yb, b_yt;
    register int    i;
    int     ind_4;
    int     x0, y0, x1, y1, flag = mode45;
#ifdef CHECK_INTERSECT
    int     j;
    double  xa, ya, xb, yb, xp, yp, xq, yq;
    double  DXab, DYab, DXpq, DYpq;
    double  numnab, numnpq, numxs, numys, denomxs, denomys;
#endif				/* CHECK_INTERSECT */
/*
** DY contains the difference between the y-values.
** DX between the x-values.  numn contains the value of
** the numerator of n; denomn contains the value of the
** denominator of n (see the main expression of a line:
** y = mx + n).  numxs till denomys contain the values of
** the numerator and the denominator of the co-ordinates of
** the intersection.  result denotes whether a certain point
** is an element of a linepart (1) or not (0).
*/
    ind_4 = nr_coor - 4;

 /* check on co-ordinates */

    for (i = 0; i <= ind_4; i += 2)
	if (int_val[i] == int_val[i + 2] && int_val[i + 1] == int_val[i + 3]) {
	    pr_exit (W, 12, 0);
	    return (1);
	}

#ifdef CHECK_INTERSECT
 /* check on intersections */

    for (i = 0; i < ind_4; i += 2) {
	xa = int_val[i];
	ya = int_val[i + 1];
	xb = int_val[i + 2];
	yb = int_val[i + 3];

	for (j = i + 2; j <= ind_4; j += 2) {
	    xp = int_val[j];
	    yp = int_val[j + 1];
	    xq = int_val[j + 2];
	    yq = int_val[j + 3];

	    if ((yb - ya) * (xq - xp) == (yq - yp) * (xb - xa)) {
	    /* the same direction coefficients */
		if (j == i + 2 || j == i + ind_4) {
		/* two adjacent lineparts:illegal direction change */
		    pr_exit (W, 11, 0);
		    return (1);
		}
	    /* real parallel lineparts or lineparts with the same carrier
	    */
		if ((xb * ya - xa * yb) * (xq - xp) ==
			(xq * yp - xp * yq) * (xb - xa)) {
		/* the lineparts have the same carrier; check whether the
		   lineparts overlap or not */
		/* test==1 if the lineparts (partly overlap */

		/* point p on linepart ab ? */
		    if (test (xa, ya, xb, yb, xp, 1.0, yp, 1.0) == 1) {
			pr_exit (W, 14, 0);
			return (1);
		    }
		/* point q on linepart ab ? */
		    if (test (xa, ya, xb, yb, xq, 1.0, yq, 1.0) == 1) {
			pr_exit (W, 14, 0);
			return (1);
		    }
		/* point a on linepart pq ? */
		    if (test (xp, yp, xq, yq, xa, 1.0, ya, 1.0) == 1) {
			pr_exit (W, 14, 0);
			return (1);
		    }
		/* point b on linepart pq ? */
		    if (test (xp, yp, xq, yq, xb, 1.0, yb, 1.0) == 1) {
			pr_exit (W, 14, 0);
			return (1);
		    }
		}
	    }
	    else {		/* not the same directioncoefficients; the
				   LINES intersect, the lineparts maybe */

		if (j != i + 2 && j != i + ind_4) {
		/* No adjacent lineparts; check on intersecting or
		   touching */
		    DYab = yb - ya;
		    DXab = xb - xa;
		    numnab = xb * ya - xa * yb;
		    DYpq = yq - yp;
		    DXpq = xq - xp;
		    numnpq = xq * yp - xp * yq;

		/* determining the intersection of the LINES */
		/* first the x co-ordinate */
		    numxs = numnpq * DXab - numnab * DXpq;
		    denomxs = DYab * DXpq - DYpq * DXab;

		/* the y co-ordinate */
		    numys = numnpq * DYab - numnab * DYpq;
		    denomys = denomxs;

		/* check whether the intersection is an element of both
		   lineparts or not */
		    if (test (xa, ya, xb, yb, numxs, denomxs, numys, denomys) == 1) {
		    /* the intersection is an element of the linepart ab
		    */
			if (test (xp, yp, xq, yq, numxs, denomxs, numys, denomys) == 1) {
			/* the intersection is an element of both
			   lineparts */
			    pr_exit (W, 10, 0);
			    return (1);
			}
		    }
		}
	    }
	}
    }
#endif				/* CHECK_INTERSECT */

    /* calculating the bounding box */
    x0 = b_xr = b_xl = int_val[0];
    y0 = b_yt = b_yb = int_val[1];

    for (i = 2; i < nr_coor;) {
	x1 = int_val[i++];
	y1 = int_val[i++];
	if (x1 < b_xl) b_xl = x1;
	else if (x1 > b_xr) b_xr = x1;
	if (y1 < b_yb) b_yb = y1;
	else if (y1 > b_yt) b_yt = y1;
	if (flag) {
	    if ((x0 -= x1) < 0) x0 = -x0;
	    if ((y0 -= y1) < 0) y0 = -y0;
	    if (x0 && y0 && x0 != y0) {
		flag = 0;
		pr_exit (W+R, 35, 0); /* non-45 degree */
	    }
	    x0 = x1;
	    y0 = y1;
	}
    }

    b_xl = gnor_ini.bxl = roundl (resolution * b_xl);
    b_xr = gnor_ini.bxr = roundh (resolution * b_xr);
    b_yb = gnor_ini.byb = roundl (resolution * b_yb);
    b_yt = gnor_ini.byt = roundh (resolution * b_yt);

    /* updating cell bounding box */
    if (ini_bbbox) {
	bbnd_xl = b_xl;
	bbnd_xr = b_xr;
	bbnd_yb = b_yb;
	bbnd_yt = b_yt;
	ini_bbbox = 0;
    }
    else {
	if (b_xl < bbnd_xl) bbnd_xl = b_xl;
	if (b_xr > bbnd_xr) bbnd_xr = b_xr;
	if (b_yb < bbnd_yb) bbnd_yb = b_yb;
	if (b_yt > bbnd_yt) bbnd_yt = b_yt;
    }

    nr_coor -= 2;  /* first point == last point */
    gnor_ini.layer_no = lay_code;
    gnor_ini.elmt = POLY_NOR;
    gnor_ini.no_xy = nr_coor / 2;
    dmPutDesignData (fp_nor, GEO_NOR_INI);

    for (i = 0; i < nr_coor;) {
	gnor_xy.x = resolution * int_val[i++];
	gnor_xy.y = resolution * int_val[i++];
	dmPutDesignData (fp_nor, GEO_NOR_XY);
    }
    return (0); /* ok */
}

swire ()
{
    double rad45, atan (), cos (), sin ();
    long   b_xl, b_xr, b_yb, b_yt;
    double bxl, bxr, byb, byt;
    double d_x, d_y, a1, a2;
    double dx1, dy1, dx2, dy2;
    double c1xr, c1yr, a1xy, b1xy, half_width;
    double c2xl, c2yl, c2xr, c2yr, a2xy, b2xy;
    double aspx, aspy, bspx, bspy;
    register int i;
    int x, y, flag = mode45;

#ifdef ESE
    pr_exit (A, -1,
      "\n\tCannot handle PATH statement containing non-orthogonal elements.");
#endif /* ESE */

    if (pathtype == 2) {
	pr_exit (W, 21, "pathtype 2 not supported at this moment");
	pr_exit (W, 21, "using pathtype 0 for swire");
    }

    half_width = (double) width / 2;
    rad45 = atan (1.0);
    c1xr = int_val[0];
    c1yr = int_val[1];

    /* calculating the bounding box */
    for (i = 2;;) {
	dx2 = x = int_val[i]   - int_val[i-2];
	dy2 = y = int_val[i+1] - int_val[i-1];
	if (x == 0) {
	    if (y == 0) {
		pr_exit (W, 24, 0); /* incr. values are 0 */
		return (1);
	    }
	    if (y > 0) a2 = 2 * rad45;
	    else a2 = 6 * rad45;
	}
	else {
	    a2 = atan (dy2 / dx2);
	    if (x > 0) {
		if (y < 0) { a2 += 8 * rad45; y = -y; }
	    }
	    else { /* x < 0 */
		a2 += 4 * rad45;
		if (y > 0) y = -y;
	    }
	    if (flag && y && x != y) {
		flag = 0;
		pr_exit (W+R, 35, 0); /* non-45 degree */
	    }
	}
	d_x = half_width * sin (a2);
	d_y = half_width * cos (a2);
	c2xl = c1xr;
	c2yl = c1yr;
	c2xr = c2xl + dx2;
	c2yr = c2yl + dy2;
	a2xy = (c2xr - d_x) * (c2yl + d_y) - (c2xl - d_x) * (c2yr + d_y);
	b2xy = (c2xr + d_x) * (c2yl - d_y) - (c2xl + d_x) * (c2yr - d_y);

	if (i == 2) { /* begin of swire */
	    if (dx2 > 0) {
		bxl = (d_x > 0) ? (c2xl - d_x) : (c2xl + d_x);
		bxr = (d_x > 0) ? (c2xr + d_x) : (c2xr - d_x);
	    }
	    else {
		bxl = (d_x > 0) ? (c2xr - d_x) : (c2xr + d_x);
		bxr = (d_x > 0) ? (c2xl + d_x) : (c2xl - d_x);
	    }
	    if (dy2 > 0) {
		byb = (d_y > 0) ? (c2yl - d_y) : (c2yl + d_y);
		byt = (d_y > 0) ? (c2yr + d_y) : (c2yr - d_y);
	    }
	    else {
		byb = (d_y > 0) ? (c2yr - d_y) : (c2yr + d_y);
		byt = (d_y > 0) ? (c2yl + d_y) : (c2yl - d_y);
	    }
	}
	else {
	    tmp_d = (a1 > a2) ? (a1 - a2) : (a2 - a1);
	    if (tmp_d > (2 * rad45) && tmp_d < (6 * rad45)) {
		pr_exit (W, 15, 0); /* too big direction change */
		return (1);
	    }
	    tmp_d = dx2 * dy1 - dx1 * dy2;
	    if (tmp_d == 0) {
		pr_exit (W, 25, "dir=prev_dir");
		return (1);
	    }
	    else {
		aspx = (dx1 * a2xy - dx2 * a1xy) / tmp_d;
		aspy = (dy1 * a2xy - dy2 * a1xy) / tmp_d;
		bspx = (dx1 * b2xy - dx2 * b1xy) / tmp_d;
		bspy = (dy1 * b2xy - dy2 * b1xy) / tmp_d;
		if (aspx > bspx) {
		    if (bspx < bxl) bxl = bspx;
		    if (aspx > bxr) bxr = aspx;
		}
		else {
		    if (aspx < bxl) bxl = aspx;
		    if (bspx > bxr) bxr = bspx;
		}
		if (aspy > bspy) {
		    if (bspy < byb) byb = bspy;
		    if (aspy > byt) byt = aspy;
		}
		else {
		    if (aspy < byb) byb = aspy;
		    if (bspy > byt) byt = bspy;
		}
	    }
	}
	if ((i += 2) >= nr_coor) break;
	a1 = a2;
	dx1 = dx2;
	dy1 = dy2;
	a1xy = a2xy;
	b1xy = b2xy;
	c1xr = c2xr;
	c1yr = c2yr;
    }

    if (dx2 > 0) {
	tmp_d = (d_x > 0) ? (c2xr + d_x) : (c2xr - d_x);
	if (tmp_d > bxr) bxr = tmp_d;
    }
    else {
	tmp_d = (d_x > 0) ? (c2xr - d_x) : (c2xr + d_x);
	if (tmp_d < bxl) bxl = tmp_d;
    }
    if (dy2 > 0) {
	tmp_d = (d_y > 0) ? (c2yr + d_y) : (c2yr - d_y);
	if (tmp_d > byt) byt = tmp_d;
    }
    else {
	tmp_d = (d_y > 0) ? (c2yr - d_y) : (c2yr + d_y);
	if (tmp_d < byb) byb = tmp_d;
    }

    b_xl = gnor_ini.bxl = roundl (resolution * bxl);
    b_xr = gnor_ini.bxr = roundh (resolution * bxr);
    b_yb = gnor_ini.byb = roundl (resolution * byb);
    b_yt = gnor_ini.byt = roundh (resolution * byt);

    /* updating cell bounding box */
    if (ini_bbbox) {
	bbnd_xl = b_xl;
	bbnd_xr = b_xr;
	bbnd_yb = b_yb;
	bbnd_yt = b_yt;
	ini_bbbox = 0;
    }
    else {
	if (b_xl < bbnd_xl) bbnd_xl = b_xl;
	if (b_xr > bbnd_xr) bbnd_xr = b_xr;
	if (b_yb < bbnd_yb) bbnd_yb = b_yb;
	if (b_yt > bbnd_yt) bbnd_yt = b_yt;
    }

    gnor_ini.layer_no = lay_code;
    gnor_ini.elmt = WIRE_NOR;
    gnor_ini.no_xy = nr_coor / 2 + 1;
    dmPutDesignData (fp_nor, GEO_NOR_INI);

    gnor_xy.x = resolution * width;
    gnor_xy.y = 0;
    dmPutDesignData (fp_nor, GEO_NOR_XY);
    gnor_xy.x = resolution * int_val[0];
    gnor_xy.y = resolution * int_val[1];
    dmPutDesignData (fp_nor, GEO_NOR_XY);
    for (i = 2; i < nr_coor; i += 2) {
	x = int_val[i]   - int_val[i-2];
	y = int_val[i+1] - int_val[i-1];
	gnor_xy.x = resolution * x;
	gnor_xy.y = resolution * y;
	dmPutDesignData (fp_nor, GEO_NOR_XY);
    }
    return (0); /* ok */
}

#ifdef CHECK_INTERSECT
/*
** The following function tests whether a point (denoted by
** numx till denomy) is an element of a linepart (denoted by
** x0 till y1) or not.  If it is an element of the linepart,
** then the return code is 1, otherwise 0.
*/
test (x0, y0, x1, y1, numx, denomx, numy, denomy)
double  x0, y0, x1, y1, numx, denomx, numy, denomy;
{
    double  xmin, xmax, ymin, ymax;

    xmin = Min (x0, x1);
    xmax = Max (x0, x1);
    ymin = Min (y0, y1);
    ymax = Max (y0, y1);

 /* check whether the point is an element of the linepart ** between the
    points (x0,y0) and (x1,y1) */
    if (denomx >= 0) {
	if (numx < xmin * denomx || numx > xmax * denomx)
	    return (0);
	if (numy < ymin * denomy || numy > ymax * denomy)
	    return (0);
    }
    else {			/* negative denominators, the signs have
				   to be switched */
	if (numx > xmin * denomx || numx < xmax * denomx)
	    return (0);
	if (numy > ymin * denomy || numy < ymax * denomy)
	    return (0);
    }
    return (1);
}
#endif				/* CHECK_INTERSECT */

/*
** Routines, which handle the internal tree structures.
*/
append_tree (name, head)
char   *name;
struct name_tree  **head;
{
    register int    cmp;

    if ((tree_ptr = *head)) {
	cmp = strcmp (name, tree_ptr -> name);
	if (cmp > 0)
	    return (append_tree (name, &(tree_ptr -> rchild)));
	if (cmp < 0)
	    return (append_tree (name, &(tree_ptr -> lchild)));
	return (1);		/* found */
    }

    ALLOC (tree_ptr, name_tree);
    strcpy (tree_ptr -> name, name);
    tree_ptr -> impcell = NULL;
    tree_ptr -> bbox    = NULL;
    tree_ptr -> status  = TRUE;
    tree_ptr -> rchild  = NULL;
    tree_ptr -> lchild  = NULL;
    *head = tree_ptr;
    return (0);			/* not found */
}

check_tree (name, ptr)
char   *name;
struct name_tree   *ptr;
{
    register int    cmp;

    if (ptr) {
	cmp = strcmp (name, ptr -> name);
	if (cmp > 0)
	    return (check_tree (name, ptr -> rchild));
	if (cmp < 0)
	    return (check_tree (name, ptr -> lchild));
	tree_ptr = ptr;
	return (1);		/* found */
    }
    return (0);			/* not found */
}

ini_modtree ()
{
    register char **ml;
    register IMPCELL **iml;
#if NCF_RELEASE >= 400
    register IMPCELL **tmp_iml;
#endif

    mod_tree = NULL;
    /*
     * For Release 4 the MODULELIST request retrieves
     * ALL module names.
     * Names of imported cells do not have to be retrieved
     * separately.  We only have to check whether a cell
     * is imported or not.
     */
#if NCF_RELEASE < 400
    if ((ml = (char **) dmGetMetaDesignData (CELLLIST, dmproject, LAYOUT))
#else
    if ((ml = (char **) dmGetMetaDesignData (MODULELIST, dmproject, LAYOUT))
#endif
	&& (iml = (IMPCELL **) dmGetMetaDesignData (IMPORTEDCELLLIST,
					dmproject, LAYOUT))) {
	for (; *ml; ++ml) {
	    if (AppendCell (*ml)) pr_exit (W, 23, *ml);
#if NCF_RELEASE >= 400
	    /*
	     * Rel3: celllist contains local cells only.
	     * Rel4: check whether actual version is imported.
	     */
	    for (tmp_iml = iml; *tmp_iml; ++tmp_iml) {
		if (strcmp (*ml, (*tmp_iml) -> alias) == 0) {
		    /* Cellname is name of imported cell. */
		    break;
		}
	    }
	    tree_ptr -> impcell = *tmp_iml;
#endif
	}

#if NCF_RELEASE < 400
	for (; *iml; ++iml) {
	    if (AppendCell ((*iml) -> alias)) {
		pr_exit (W, 23, (*iml) -> alias);
	    }
	    tree_ptr -> impcell = *iml;
	}
#endif
    }
}

/*
** Write the 3 bounding-boxes to the info-file.
*/
write_info ()
{
    /**** pass TWO ****/
    if (!tree_ptr -> bbox) { /* no element bbox */
	bbnd_xl = bbnd_xr = bbnd_yb = bbnd_yt = 0;
	if (ini_mcbbox) { /* no cellcalls */
	    mcbb_xl = mcbb_xr = mcbb_yb = mcbb_yt = 0;
	    pr_exit (W+R, 26, ms_name); /* empty */
	}
	ALLOC (tree_ptr -> bbox, mod_bbox);
	tree_ptr -> bbox -> xl = ginfo.bxl = mcbb_xl;
	tree_ptr -> bbox -> xr = ginfo.bxr = mcbb_xr;
	tree_ptr -> bbox -> yb = ginfo.byb = mcbb_yb;
	tree_ptr -> bbox -> yt = ginfo.byt = mcbb_yt;
    }
    else {
	bbnd_xl = tree_ptr -> bbox -> xl;
	bbnd_xr = tree_ptr -> bbox -> xr;
	bbnd_yb = tree_ptr -> bbox -> yb;
	bbnd_yt = tree_ptr -> bbox -> yt;
	if (ini_mcbbox) { /* no cellcalls */
	    mcbb_xl = mcbb_xr = mcbb_yb = mcbb_yt = 0;
	    ginfo.bxl = bbnd_xl;
	    ginfo.bxr = bbnd_xr;
	    ginfo.byb = bbnd_yb;
	    ginfo.byt = bbnd_yt;
	}
	else {
	    ginfo.bxl = Min (bbnd_xl, mcbb_xl);
	    ginfo.bxr = Max (bbnd_xr, mcbb_xr);
	    ginfo.byb = Min (bbnd_yb, mcbb_yb);
	    ginfo.byt = Max (bbnd_yt, mcbb_yt);
	    tree_ptr -> bbox -> xl = ginfo.bxl;
	    tree_ptr -> bbox -> xr = ginfo.bxr;
	    tree_ptr -> bbox -> yb = ginfo.byb;
	    tree_ptr -> bbox -> yt = ginfo.byt;
	}
    }

    DEBUG "%s: encapsul bbox %d %d %d %d\n",
	ms_name, ginfo.bxl, ginfo.bxr, ginfo.byb, ginfo.byt);
    dmPutDesignData (fp_info, GEO_INFO);

    ginfo.bxl = mcbb_xl;
    ginfo.bxr = mcbb_xr;
    ginfo.byb = mcbb_yb;
    ginfo.byt = mcbb_yt;
    DEBUG "%s: mc bbox %d %d %d %d\n",
	ms_name, ginfo.bxl, ginfo.bxr, ginfo.byb, ginfo.byt);
    dmPutDesignData (fp_info, GEO_INFO);

    ginfo.bxl = bbnd_xl;
    ginfo.bxr = bbnd_xr;
    ginfo.byb = bbnd_yb;
    ginfo.byt = bbnd_yt;
    DEBUG "%s: box  bbox %d %d %d %d\n",
	ms_name, ginfo.bxl, ginfo.bxr, ginfo.byb, ginfo.byt);
    dmPutDesignData (fp_info, GEO_INFO);
}

/*
** Open GDS file.
*/
open_gds ()
{
    if (vol_nr > nr_files) pr_exit (A, 1, 0);
    strcpy (file_name, vector[vol_nr]);
    if (stat (file_name, &stat_buf) == -1) pr_exit (A, 37, file_name);
    if ((stat_buf.st_mode & S_IFREG) != S_IFREG) pr_exit (A, 38, file_name);
    if (!(fp_gds = fopen (file_name, "r"))) pr_exit (A, 2, file_name);
}

/*
** Check for end of volume and open next volume.
*/
next_volume ()
{
    N_VOL = TRUE;
    fclose (fp_gds);
    if (pass == 1) {
	if (vol_nr > 1) {
	    read_rec_contents ();
	    if (vol_nr != int_val[0])
		pr_exit (A, 36, file_name);
	}
	else
	    read_tape_id ();
    }
    vol_nr++;
    open_gds ();
    check_rec_id (0, TAPENUM);
    read_tape_id ();
    N_VOL = FALSE;
}

/*
** Read tape id.
*/
read_tape_id ()
{
    register int i;

    read_rec_contents ();
    if (vol_nr != int_val[0]) pr_exit (A, 36, file_name);
    check_rec_id (0, TAPECODE);
    read_rec_contents ();
    if (vol_nr == 1) {
	t_code[0] = int_val[0];
	t_code[1] = int_val[1];
	t_code[2] = int_val[2];
    }
    else
	for (i = 0; i < 3; ++i)
	    if (t_code[i] != int_val[i])
		pr_exit (A, 36, file_name);
    check_rec_id (0, LIBNAME);
    read_rec_contents ();
    if (vol_nr > 1 && strcmp (libname, gds_name))
	pr_exit (A, 36, file_name);
}

/*
** DB-error routines.
*/
dmError (s)
char   *s;
{
    dmPerror (s);
    pr_exit (A, 22, 0);
}

/*
** Process interrupts.
*/
sig_handler (sig)		/* signal handler */
int     sig;
{
    char    rbuf[6];
    signal (SIGINT, SIG_IGN);	/* ignore intr signal */
    signal (SIGQUIT, SIG_IGN);	/* ignore quit signal */
    sprintf (rbuf, "%d", sig);
    pr_exit (A, 20, rbuf);
}

/*
** Give an 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 & R) { PE "%d: ", rec_nr); mode -= R; }
    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 (debug) return;
    if (mode == A) {
	dmQuit ();
	PE "%s: -- program aborted --\n", argv0);
	exit (1);
    }
    if (mode == E) exit (0);
}

static char *recordNames[] =
{
    "header", "bgnlib", "libname", "units", "endlib",
    "bgnstr", "strname", "endstr", "boundary", "path",
    "sref", "aref", "text", "layer", "datatype",
    "width", "xy", "endel", "sname", "colrow",
    "textnode", "node", "texttype", "presentation", "spacing",
    "string", "strans", "mag", "angle", "uinteger",
    "ustring", "reflibs", "fonts", "pathtype", "generations",
    "attrtable", "styptable", "strtype", "elflags", "elkey",
    "linktype", "linkkeys", "nodetype", "propattr", "propvalue",
    "box", "boxtype", "plex", "bgnextn", "endextn",
    "tapenum", "tapecode", "strclass", "reserved", "format",
    "mask", "endmasks"
};

char *
recordName (id)
int id;
{
    static char rbuf[16];

    id = (id & 0XFF00) >> 8;
    if (id >= sizeof (recordNames) / sizeof (char *)) {
	sprintf (rbuf, "#%d", id);
	return (rbuf);
    }
    return (recordNames[id]);
}

#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

/*
** Check and execute for properties.
*/
check_for_prop ()
{
    static   int termside;
    register int i, foundprop = 0;

loop1:
    read_rec_header ();
    switch (rec_id) {
	case PROPATTR:
	    DEBUG "check_for_prop %x propattr\n", rec_id);
	    read_rec_contents ();
	    read_rec_header ();
	    switch (rec_id) {
		case PROPVALUE:
		    DEBUG "check_for_prop %x propval\n", rec_id);
		    read_rec_contents ();
		    break;
		default:
		    pr_exit (I, 21, "no property value found");
	    }
	    property[foundprop].attr = int_val[0];
	    strcpy (property[foundprop].value, gds_name);
	    ++foundprop;
	    goto loop1;
	case ENDEL:
	    break;
	default:
	    /* expected property attribute not found */
	    pr_exit (I, 48, recordName (rec_id));
	    pr_exit (A+R, 5, "syntax");
	    return;
    }

#ifdef PROP_DEBUG
    PE "found %d properties\n", foundprop);
#endif PROP_DEBUG

    /* Execute properties */
    for (i = 0; i < foundprop; ++i) {
#ifdef PROP_DEBUG
	PE "prop: %d %s\n", property[i].attr, property[i].value);
#endif PROP_DEBUG
	switch (property[i].attr) {
	    case PROP_TERMSIDE:
		termside = atoi (property[i].value);
		DEBUG "PROP_TERMSIDE %d\n", termside);
		break;
	    case PROP_TERMLAY:
		lay_code = atoi (property[i].value);
		DEBUG "PROP_TERMLAY  %d\n", lay_code);
		break;
	    case PROP_INSTANCE:
		DEBUG "PROP_INSTANCE %s\n", property[i].value);
		strncpy (instance, property[i].value, DM_MAXNAME);
		break;
	    case PROP_TERMINAL:
		DEBUG "PROP_TERMINAL %s\n", property[i].value);
		term (property[i].value, termside);
		break;
	    default:
		;
	}
    }
}

term (termname, termside)
char *termname;
int   termside;
{
    DEBUG "%s %d %d\n", termname, lay_code, termside);
    if (lay_code < 0 || lay_code > MAX_GDS_LAYNR || masknr[lay_code] == -1) {
	sprintf (nbuf, "%d", lay_code);
	pr_exit (W, 28, nbuf);
	pr_exit (W, 32, "propattr(terminal)");
	return;
    }

    gterm.layer_no = masknr[lay_code];
    strcpy (gterm.term_name, termname);
    gterm.bxl = gterm.xl = gbox.xl;
    gterm.bxr = gterm.xr = gbox.xr;
    gterm.byb = gterm.yb = gbox.yb;
    gterm.byt = gterm.yt = gbox.yt;
    dmPutDesignData (fp_term, GEO_TERM);
}

_quadragon (x1, y1, x2, y2, x3, y3, x4, y4)
double x1, y1, x2, y2, x3, y3, x4, y4;
{
    long    b_xl, b_xr, b_yb, b_yt;

    /* calculating the bounding box */

    b_xl = gnor_ini.bxl = roundl (1.0 * x1);
    b_xr = gnor_ini.bxr = roundh (1.0 * x3);
    b_yb = gnor_ini.byb = roundl (1.0 * y1);
    b_yt = gnor_ini.byt = roundh (1.0 * y3);

    /* updating cell bounding box */
    if (ini_bbbox) {
	bbnd_xl = b_xl;
	bbnd_xr = b_xr;
	bbnd_yb = b_yb;
	bbnd_yt = b_yt;
	ini_bbbox = 0;
    }
    else {
	if (b_xl < bbnd_xl) bbnd_xl = b_xl;
	if (b_xr > bbnd_xr) bbnd_xr = b_xr;
	if (b_yb < bbnd_yb) bbnd_yb = b_yb;
	if (b_yt > bbnd_yt) bbnd_yt = b_yt;
    }

    gnor_ini.layer_no = lay_code;
    gnor_ini.elmt = POLY_NOR;
    gnor_ini.no_xy = 4;
    dmPutDesignData (fp_nor, GEO_NOR_INI);

	gnor_xy.x = 1.0 * x1;
	gnor_xy.y = 1.0 * y1;
	dmPutDesignData (fp_nor, GEO_NOR_XY);
	gnor_xy.x = 1.0 * x2;
	gnor_xy.y = 1.0 * y2;
	dmPutDesignData (fp_nor, GEO_NOR_XY);
	gnor_xy.x = 1.0 * x3;
	gnor_xy.y = 1.0 * y3;
	dmPutDesignData (fp_nor, GEO_NOR_XY);
	gnor_xy.x = 1.0 * x4;
	gnor_xy.y = 1.0 * y4;
	dmPutDesignData (fp_nor, GEO_NOR_XY);

    return (0); /* ok */
}
