Logo Search packages:      
Sourcecode: magic version File versions  Download package

CmdCD.c

/*
 * CmdCD.c --
 *
 * Commands with names beginning with the letters C through D.
 *
 *     ********************************************************************* 
 *     * Copyright (C) 1985, 1990 Regents of the University of California. * 
 *     * Permission to use, copy, modify, and distribute this              * 
 *     * software and its documentation for any purpose and without        * 
 *     * fee is hereby granted, provided that the above copyright          * 
 *     * notice appear in all copies.  The University of California        * 
 *     * makes no representations about the suitability of this            * 
 *     * software for any purpose.  It is provided "as is" without         * 
 *     * express or implied warranty.  Export of this software outside     * 
 *     * of the United States of America may require an export license.    * 
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-7.5/commands/CmdCD.c,v 1.19 2009/09/11 16:15:05 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "utils/main.h"
#include "commands/commands.h"
#include "utils/utils.h"
#include "textio/textio.h"
#include "drc/drc.h"
#include "graphics/graphics.h"
#include "textio/txcommands.h"
#include "textio/textio.h"
#include "cif/cif.h"
#include "calma/calma.h"
#include "utils/styles.h"
#include "router/rtrDcmpose.h"
#include "select/select.h"
#include "utils/signals.h"
#include "utils/malloc.h"
#include "cif/CIFint.h"

#ifdef NONMANHATTAN
#include "cif/CIFread.h"
#endif

/* The following structure is used by CmdCorner to keep track of
 * areas to be filled.
 */

00060 struct cmdCornerArea
{
    Rect cca_area;                  /* Area to paint. */
    TileType cca_type;              /* Type of material. */
    struct cmdCornerArea *cca_next; /* Next in list of areas to paint. */
};

/* Forward declarations */
int cmdDumpFunc();
bool cmdDumpParseArgs();
#ifdef CALMA_MODULE

/*
 * ----------------------------------------------------------------------------
 *
 * CmdCalma --
 *
 * Implement the "gds" or "calma" command.
 *
 * Usage:
 *    gds option args
 *    calma option args
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    There are no side effects on the circuit.  Currently, there
 *    is only a single option, "write", to write a CALMA stream
 *    file.
 *
 * ----------------------------------------------------------------------------
 */

#define CALMA_HELP      0
#define CALMA_ARRAYS    1
#define CALMA_CONTACTS  2
#define     CALMA_FLATTEN     3
#define CALMA_ORDERING  4
#define     CALMA_LABELS      5
#define     CALMA_LOWER 6
#define CALMA_MERGE     7
#define CALMA_READ      8
#define CALMA_READONLY  9
#define CALMA_RESCALE   10
#define CALMA_WARNING   11
#define CALMA_WRITE     12
#ifdef NONMANHATTAN
#define CALMA_POLYS     13
#endif

#define CALMA_WARN_HELP CIF_WARN_END      /* undefined by CIF module */

void
CmdCalma(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    int option, ext;
    char **msg, *namep, *dotptr;
    CellDef *rootDef;
    FILE *f;

    static char *gdsExts[] = {".gds", ".gds2", ".strm", "", NULL};
    static char *cmdCalmaYesNo[] = { "no", "false", "off", "yes", "true", "on", 0 };
    static char *cmdCalmaWarnOptions[] = { "default", "none", "align",
            "limit", "redirect", "help", 0 };
    static char *cmdCalmaOption[] =
    { 
      "help       print this help information",
      "arrays [yes|no]  output arrays as individual subuses (like in CIF)",
      "contacts [yes|no]      optimize output by arraying contacts as subcells",
      "flatten [yes|no] flatten simple cells (e.g., contacts) on input",
      "ordering [on|off]      cause cells to be read in post-order",
      "labels [yes|no]  cause labels to be output when writing GDS-II",
      "lower [yes|no]         allow both upper and lower case in labels",
      "merge [yes|no]         merge tiles into polygons in the output",
      "read file        read Calma GDS-II format from \"file\"\n"
      "           into edit cell",
      "readonly [yes|no]      set cell as read-only and generate output from GDS file",
      "rescale [yes|no] allow or disallow internal grid subdivision",
      "warning [option] set warning information level",
      "write file       output Calma GDS-II format to \"file\"\n"
      "           for the window's root cell",
#ifdef NONMANHATTAN
      "polygon subcells [yes|no]\n"
      "           put non-Manhattan polygons into subcells",
#endif
      NULL
    };

    if (cmd->tx_argc == 1)
      option = CALMA_WRITE;
    else
    {
      option = Lookup(cmd->tx_argv[1], cmdCalmaOption);
      if (option < 0)
      {
          TxError("\"%s\" isn't a valid gds option.\n", cmd->tx_argv[1]);
          option = CALMA_HELP;
          cmd->tx_argc = 2;
      }
    }

    /* Only check for a window on options requiring one */

    switch (option)
    {
      case CALMA_READ: case CALMA_WRITE:
          windCheckOnlyWindow(&w, DBWclientID);
          if (w == (MagWindow *) NULL)
          {
            TxError("Point to a window first\n");
            return;
          }
          rootDef = ((CellUse *) w->w_surfaceID)->cu_def;

          if (cmd->tx_argc == 1)
          {
            namep = rindex(rootDef->cd_name, '/');
            if (namep == (char *) NULL)
                namep = rootDef->cd_name;
            goto outputCalma;
          }
          break;
    }

    switch (option)
    {
      case CALMA_HELP:
          TxPrintf("GDS commands have the form \":gds option\",");
          TxPrintf(" where option is one of:\n");
          for (msg = &(cmdCalmaOption[0]); *msg != NULL; msg++)
          {
            if (**msg == '*') continue;
            TxPrintf("    %s\n", *msg);
          }
          TxPrintf("If no option is given, a CALMA GDS-II stream file is\n");
          TxPrintf("    produced for the root cell.\n");
          TxPrintf("The current CIF output style (\"cif ostyle\") is used\n");
          TxPrintf("    to select the mask layers output by :gds write.\n");
          TxPrintf("The current CIF input style (\"cif istyle\") is used\n");
          TxPrintf("    to select the mask layers read by :gds read.\n");
          return;

      case CALMA_LABELS:
          if (cmd->tx_argc == 2)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaDoLabels));
#else
            TxPrintf("Labels will %sbe output to the GDS file.\n",
                  (CalmaDoLabels) ?  "" : "not ");
#endif
            return;
          }
          else if (cmd->tx_argc != 3)
          {
            wrongNumArgs:
            TxError("Wrong number of arguments in \"gds\" command.");
            TxError("  Try \":gds help\" for help.\n");
            return;
          }

          option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
          if (option < 0)
            goto wrongNumArgs;
          CalmaDoLabels = (option < 3) ? FALSE : TRUE;
          return;

      case CALMA_CONTACTS:
          if (cmd->tx_argc == 2)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaContactArrays));
#else
            if (CalmaContactArrays)
                TxPrintf("Contact areas are arrayed as subcells.\n");
            else
                TxPrintf("Contact areas are output as individual cuts.\n");
#endif
            return;
          }
          else if (cmd->tx_argc != 3)
            goto wrongNumArgs;

          option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
          if (option < 0)
            goto wrongNumArgs;
          CalmaContactArrays = (option < 3) ? FALSE : TRUE;
          return;

      case CALMA_FLATTEN:
          if (cmd->tx_argc == 2)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaFlattenUses));
#else
            if (CalmaFlattenUses)
                TxPrintf("Small cells in input are flattened.\n");
            else
                TxPrintf("Cells are never flattened on input.\n");
#endif
            return;
          }
          else if (cmd->tx_argc != 3)
            goto wrongNumArgs;

          option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
          if (option < 0)
            goto wrongNumArgs;
          CalmaFlattenUses = (option < 3) ? FALSE : TRUE;
          return;

      case CALMA_ORDERING:
          if (cmd->tx_argc == 2)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaPostOrder));
#else
            if (CalmaPostOrder)
                TxPrintf("GDS parser reads cells in post-order.\n");
            else
                TxPrintf("GDS parser reads cells in the order "
                        "encountered in the stream file.\n");
            return;
#endif
          }
          else if (cmd->tx_argc != 3)
            goto wrongNumArgs;

          option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
          if (option < 0)
            goto wrongNumArgs;
          CalmaPostOrder = (option < 3) ? FALSE : TRUE;
          return;

      case CALMA_ARRAYS:
          if (cmd->tx_argc == 2)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaFlattenArrays));
#else
            TxPrintf("GDS are %s.\n",
                  (CalmaFlattenArrays) ?  "flattened" : "hierarchical");
#endif
            return;
          }
          else if (cmd->tx_argc != 3)
            goto wrongNumArgs;

          option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
          if (option < 0)
            goto wrongNumArgs;
          CalmaFlattenArrays = (option < 3) ? FALSE : TRUE;
          return;

      case CALMA_LOWER:
          if (cmd->tx_argc == 2)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaDoLower));
#else
            TxPrintf("GDS labels are %s.\n",
                  (CalmaDoLower) ?  "lowercase only" : "mixed-case");
#endif
            return;
          }
          else if (cmd->tx_argc != 3)
            goto wrongNumArgs;

          option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
          if (option < 0)
            goto wrongNumArgs;
          CalmaDoLower = (option < 3) ? FALSE : TRUE;
          return;

      case CALMA_MERGE:
          if (cmd->tx_argc == 2)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaMergeTiles));
#else
            if (CalmaMergeTiles)
                TxPrintf("Merge connected tiles into polygons on output\n");
            else
                TxPrintf("Output individual tiles\n");
#endif
            return;
          }
          else if (cmd->tx_argc != 3)
            goto wrongNumArgs;

          option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
          if (option < 0)
            goto wrongNumArgs;
          CalmaMergeTiles = (option < 3) ? FALSE : TRUE;
          return;

#ifdef NONMANHATTAN
      case CALMA_POLYS:
          if (cmd->tx_argc == 3)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaSubcellPolygons));
#else
            if (CalmaSubcellPolygons)
                TxPrintf("Non-manhattan polygons placed in subcells.\n");
            else
                TxPrintf("Non-manhattan polygons read as-is.\n");
#endif
            return;
          }
          else if (cmd->tx_argc != 4)
            goto wrongNumArgs;

          option = Lookup(cmd->tx_argv[3], cmdCalmaYesNo);
          if (option < 0)
            goto wrongNumArgs;
          CalmaSubcellPolygons = (option < 3) ? FALSE : TRUE;
          return;
#endif  /* NONMANHATTAN */

      case CALMA_READONLY:
          if (cmd->tx_argc == 2)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaReadOnly));
#else
            TxPrintf("GDS cells read from input file are set read-%s.\n",
                  (CalmaReadOnly) ?  "only" : "write");
#endif
            return;
          }
          else if (cmd->tx_argc != 3)
            goto wrongNumArgs;

          option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
          if (option < 0)
            goto wrongNumArgs;
          CalmaReadOnly = (option < 3) ? FALSE : TRUE;
          return;

      case CALMA_RESCALE:
          if (cmd->tx_argc == 2)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CIFRescaleAllow));
#else
            TxPrintf("Internal grid rescaling %sallowed\n",
                  (CIFRescaleAllow) ?  "" : "dis");
#endif
            return;
          }
          else if (cmd->tx_argc != 3)
            goto wrongNumArgs;

          option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
          if (option < 0)
            goto wrongNumArgs;
          CIFRescaleAllow = (option < 3) ? FALSE : TRUE;
          if (!CIFRescaleAllow)
            CIFWarningLevel = CIF_WARN_LIMIT;
          return;

      case CALMA_WARNING:
          if (cmd->tx_argc == 2)
          {
            TxPrintf("Warning display options: %s\n",
                  cmdCalmaWarnOptions[CIFWarningLevel]);
          }
          else
          {
            int suboption = Lookup(cmd->tx_argv[2], cmdCalmaWarnOptions);
            if (suboption < 0)
            {
                TxError("\"%s\" isn't a valid gds warning option.\n",
                        cmd->tx_argv[2]);
                suboption = CALMA_WARN_HELP;
            }
            if (suboption == CALMA_WARN_HELP)
                TxError("Valid options are: default, align, limit, "
                  "redirect, and none.\n");
            else
                CIFWarningLevel = suboption;

            if (suboption == CIF_WARN_REDIRECT)
            {
                if (cmd->tx_argc == 4)
                  StrDup(&CIFErrorFilename, cmd->tx_argv[3]);
                else
                  StrDup(&CIFErrorFilename, NULL);
            }
          }
          return;
      case CALMA_WRITE:
          if (cmd->tx_argc != 3) goto wrongNumArgs;
          namep = cmd->tx_argv[2];
          goto outputCalma;

      case CALMA_READ:
          if (cmd->tx_argc != 3) goto wrongNumArgs;

          /* Check for various common file extensions, including  */
          /* no extension (as-is), ".gds", ".gds2", and ".strm".  */

          for (ext = 0; gdsExts[ext] != NULL; ext++)
            if ((f = PaOpen(cmd->tx_argv[2], "r", gdsExts[ext], Path,
                  (char *) NULL, &namep)) != (FILE *)NULL)
                break;

          if (f == (FILE *) NULL)
          {
              TxError("Cannot open %s.gds, %s.strm or %s to read "
                  "GDS-II stream input.\n",
                  cmd->tx_argv[2], cmd->tx_argv[2], cmd->tx_argv[2]);
              return;
          }
          CalmaReadFile(f, namep);
          (void) fclose(f);
          return;
    }

    /*
     * If control gets here, we're going to output GDS-II (stream)
     * into the file given by namep.
     */

outputCalma:
    dotptr = strrchr(namep, '.');

    f = PaOpen(namep, "w", (dotptr == NULL) ? ".gds" : "", ".",
            (char *) NULL, (char **) NULL);

    if (f == (FILE *) NULL)
    {
      TxError("Cannot open %s%s to write GDS-II stream output\n", namep,
            (dotptr == NULL) ? ".gds" : "");
      return;
    }

    if (!CalmaWrite(rootDef, f))
    {
      TxError("I/O error in writing file %s.\n", namep);
      TxError("File may be incompletely written.\n");
    }
    (void) fclose(f);
}
#endif

/*
 * ----------------------------------------------------------------------------
 *
 * CmdCellname --
 *
 * Implement the "cellname" and "instance" commands. List the child(ren),
 * parent(s), or name of the selected or indicated cell or instance.
 *
 * Usage:
 *    cellname [list] children|parents|exists|instances|
 *                celldef|self|childinst [name]
 * or
 *    cellname [list] topcells|allcells|window
 * or
 *    cellname delete [name]
 * or
 *    cellname writeable [name] [true|false]
 * or
 *    cellname rename [name] [newname]
 * or
 *    cellname property [name] [property_key [property_value]]
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Print out the names of all the cell currently loaded.
 *    Or (optionally) only the cell given.
 *
 * ----------------------------------------------------------------------------
 */

      /* ARGSUSED */

void
CmdCellname(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    bool dolist = FALSE;
    int option;
    int locargc = cmd->tx_argc;
    char *cellname = NULL;
    void (*func)();
    CellDef *newDef, *cellDef;

    static char *cmdCellOption[] =
    { 
      "children   list children of selected or named cell",
      "parents    list parents of selected or named cell",
      "exists     true if loaded, false if not, or name of selected",
      "self       true if loaded, false if not, or name of selected",
      "instances  list instances of a cell definition",
      "childinst  list children instances of a cell definition",
      "celldef    name the cell definition of an instance",
      "allcells   list all cells",
      "topcells   list top-level cells",
      "window           list top-level cell of a layout window",
      "create           create a new cell definition",
      "delete           delete the named cell definition",
      "flags            list option flags of the indicated cell definition",
      "lock       lock the named cell (prevent changes to cell use)",
      "unlock           unlock the named cell (allow changes to cell use)",
      "property   list or set cell definition properties",
      "rename           rename the indicated cell",
      "writeable  make the cell definition read-only or read-write",
      NULL
    };
    typedef enum { IDX_CHILDREN, IDX_PARENTS, IDX_EXISTS, IDX_SELF,
               IDX_INSTANCE, IDX_CHILDINST, IDX_CELLDEF, IDX_ALLCELLS,
               IDX_TOPCELLS, IDX_IN_WINDOW, IDX_CREATE,
               IDX_DELETE, IDX_FLAGS, IDX_LOCK, IDX_UNLOCK,
               IDX_PROPERTY, IDX_RENAME, IDX_READWRITE } optionType;

    if (strstr(cmd->tx_argv[0], "in"))
      func = DBUsePrint;
    else
      func = DBCellPrint;

    if (locargc > 1)
    {
      if (!strcmp(cmd->tx_argv[1], "list")) {
          dolist = TRUE;
          locargc--;
      }
    }
    if (locargc > 5 || locargc < 2) goto badusage;

    option = Lookup(cmd->tx_argv[1 + ((dolist) ? 1 : 0)], cmdCellOption);
    if (option < 0) goto badusage;

    if ((locargc > 3) && (option != IDX_RENAME) && (option != IDX_DELETE) &&
            (option != IDX_READWRITE) && (option != IDX_PROPERTY))
      goto badusage;

    if ((locargc > 4) && (option != IDX_PROPERTY))
      goto badusage;

    if (locargc >= 3) {
      switch (option) {
          case IDX_ALLCELLS:
          case IDX_TOPCELLS:
          case IDX_IN_WINDOW:
            goto badusage;
            break;
          default:
            cellname = cmd->tx_argv[2 + ((dolist) ? 1 : 0)];
      }
    }

    if (func != DBUsePrint) 
    {
      /* These functions only work with cell uses (instances) */
      switch (option) {
          case IDX_LOCK:
          case IDX_UNLOCK:
            TxError("Cell definitions cannot be locked.  Use \"instance\"?\n");
            TxError("  or do you mean \"cellname writeable\"?\n");
            return;
      }
    }
    else 
    {
      /* These functions only work with cell definitions */
      switch (option) {
          case IDX_TOPCELLS:
            TxError("Instances do not have a top level.  Use \"cellname\"?\n");
            return;
          case IDX_IN_WINDOW: case IDX_READWRITE: case IDX_FLAGS: case IDX_PROPERTY:
            TxError("Function unimplemented for instances.\n");
            return;
          case IDX_DELETE:
            TxError("Function unimplemented for instances.  Use \"delete\"\n");
            return;
          case IDX_CREATE:
            TxError("Function unimplemented for instances.  Use \"getcell\"\n");
            return;
          case IDX_RENAME:
            TxError("Function unimplemented for instances.  Use \"identify\"\n");
            return;
      }
    }

    switch (option) {
      case IDX_ALLCELLS:
          (*func)(NULL, ALLCELLS, dolist);
          break;
      case IDX_TOPCELLS:
          (*func)(NULL, TOPCELLS, dolist);
          break;
      case IDX_IN_WINDOW:
          DBTopPrint(w, dolist);
          break;
      case IDX_SELF:
      case IDX_EXISTS:
          (*func)(cellname, SELF, dolist);
          break;
      case IDX_CELLDEF:
          (*func)(cellname, ((func == DBUsePrint) ? OTHER : SELF), dolist);
          break;
      case IDX_INSTANCE:
          (*func)(cellname, ((func == DBUsePrint) ? SELF : OTHER), dolist);
          break;
      case IDX_CHILDREN:
          (*func)(cellname, CHILDREN, dolist);
          break;
      case IDX_CHILDINST:
          (*func)(cellname, ((func == DBUsePrint) ? CHILDREN : CHILDINST), dolist);
          break;
      case IDX_PARENTS:
          (*func)(cellname, PARENTS, dolist);
          break;
      case IDX_PROPERTY:
          if (cellname == NULL)
            cellDef = EditRootDef;
          else
            cellDef = DBCellLookDef(cellname);

          if (cellDef == (CellDef *) NULL)
            TxError("Unknown cell %s\n", cellname);
          else
            CmdDoProperty(cellDef, cmd, 3 + ((dolist) ? 1 : 0));
          break;

      case IDX_DELETE:
          /* Unload the cell definition and free memory */
          if ((locargc == 4) && !strcmp(cmd->tx_argv[3 + ((dolist) ? 1 : 0)],
                  "-noprompt"))
              DBCellDelete(cellname, TRUE);
          else
              DBCellDelete(cellname, FALSE);
          break;

      case IDX_READWRITE:
          if (cellname == NULL)
            cellDef = EditRootDef;
          else
            cellDef = DBCellLookDef(cellname);
          if (cellDef == (CellDef *) NULL)
          {
            TxError("Unknown cell %s\n", cellname);
            break;
          }

          if (locargc == 3)
          {
            if (cellDef->cd_flags & CDNOEDIT)
#ifdef MAGIC_WRAPPER
                if (dolist)
                  Tcl_SetResult(magicinterp, "read-only", 0);
                else
#endif
                TxPrintf("read-only\n");
            else
#ifdef MAGIC_WRAPPER
                if (dolist)
                  Tcl_SetResult(magicinterp, "writeable", 0);
                else
#endif
                TxPrintf("writeable\n");
          }
          else if (locargc == 4)
          {
            if (tolower(*cmd->tx_argv[3 + ((dolist) ? 1 : 0)]) == 't')
            {
                /* Check if file is already read-write */
                if (!(cellDef->cd_flags & CDNOEDIT))
                  break;

                /* Make file read-write */

#ifdef FILE_LOCKS
                if (cellDef->cd_fd == -1)
                  dbReadOpen(cellDef, NULL, TRUE);

                if (cellDef->cd_fd != -1)
                  cellDef->cd_flags &= ~CDNOEDIT;
                else
                  TxError("Advisory lock held on cell %s\n", cellDef->cd_name);
#else
                cellDef->cd_flags &= ~CDNOEDIT;
#endif
                WindAreaChanged(w, &w->w_screenArea);
                CmdSetWindCaption(EditCellUse, EditRootDef);
            }
            else  /* "cellname writeable false" */
            {
                /* Check if file is already read-only */
                if (cellDef->cd_flags & CDNOEDIT)
                  break;

                /* Make file read-only */

                cellDef->cd_flags |= CDNOEDIT;

#ifdef FILE_LOCKS
                /* Release any advisory lock held on this file */

                if (cellDef->cd_fd != -1)
                {
                  close(cellDef->cd_fd);
                  cellDef->cd_fd = -1;
                }
#endif

                if (EditCellUse && (EditCellUse->cu_def == cellDef))
                  EditCellUse = (CellUse *)NULL;

                if (EditRootDef == cellDef)
                  EditRootDef = (CellDef *)NULL;

                WindAreaChanged(w, &w->w_screenArea);
                CmdSetWindCaption(EditCellUse, (CellDef *)NULL);
            }
          }
          break;

      case IDX_FLAGS:
          if (cellname == NULL)
            cellDef = EditRootDef;
          else
            cellDef = DBCellLookDef(cellname);
          if (cellDef == (CellDef *) NULL)
          {
            TxError("Unknown cell %s\n", cellname);
            break;
          }
#ifdef MAGIC_WRAPPER
          if (cellDef->cd_flags & CDAVAILABLE)
            Tcl_AppendElement(magicinterp, "available");
          if (cellDef->cd_flags & CDMODIFIED)
            Tcl_AppendElement(magicinterp, "modified");
          if (cellDef->cd_flags & CDNOEDIT)
            Tcl_AppendElement(magicinterp, "readonly");
#else
          TxPrintf("Flag settings for cell %s:\n", cellname);
          TxPrintf("in database: %s\n", (cellDef->cd_flags & CDAVAILABLE) ?
                  "true" : "false");
          TxPrintf("modified: %s\n", (cellDef->cd_flags & CDMODIFIED) ?
                  "true" : "false");
          TxPrintf("readonly: %s\n", (cellDef->cd_flags & CDNOEDIT) ?
                  "true" : "false");
#endif
          break;
      case IDX_RENAME:
          /* Rename the cell and mark as modified.  Do not write to disk. */
          if (locargc != 4) goto badusage;
          DBCellRename(cellname, cmd->tx_argv[3 + ((dolist) ? 1 : 0)]);
          break;
      case IDX_CREATE:
          newDef = DBCellLookDef(cellname);
          if (newDef == (CellDef *) NULL)
          {
            newDef = DBCellNewDef(cellname, (char *) NULL);
            DBCellSetAvail(newDef);
          }
          break;
      case IDX_LOCK:
          DBLockUse(cellname, TRUE);
          break;
      case IDX_UNLOCK:
          DBLockUse(cellname, FALSE);
          break;
    }
    return;

badusage:
    TxError("Usage: %s [list] children|parents|self|exists|"
            "instances|celldef|delete [name]\n", cmd->tx_argv[0]);
    TxError("or:    %s [list] allcells|topcells|window\n", cmd->tx_argv[0]);
    TxError("or:    %s create name\n", cmd->tx_argv[0]);
    TxError("or:    %s rename name newname\n", cmd->tx_argv[0]);
    TxError("or:    %s [un]lock [name]\n", cmd->tx_argv[0]);
    TxError("or:    %s writeable [name] [true|false]\n", cmd->tx_argv[0]);
    return;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdCif --
 *
 * Implement the "cif" command.
 *
 * Usage:
 *    cif option args
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    There are no side effects on the circuit.  Various options
 *    may produce cif files, read cif, or display cif information
 *    on the screen.
 *
 * ----------------------------------------------------------------------------
 */

#ifdef CIF_MODULE

#define ARRAY            0
#define HIER             1
#define     AREALABELS   2
#define COVERAGE   3
#define HELP             4
#define IDCELL           5
#define ISTYLE           6
#define CIF_LAMBDA       7
#define CIF_LIMIT  8
#define OSTYLE           9
#define CIF_PAINT 10
#define PREFIX          11
#define READ            12
#define RESCALE         13
#define SEE       14
#define STATS           15
#define WARNING         16
#define CIF_WRITE 17
#define CIF_WRITE_FLAT  18
#ifdef NONMANHATTAN
#define POLYGONS  19
#endif

#define CIF_WARN_HELP  CIF_WARN_END /* undefined by CIF module */

void
CmdCif(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    int option, yesno;
    char **msg, *namep;
    CellDef *rootDef, *paintDef;
    Rect box;
    TileType layer;
    TileTypeBitMask mask;
    FILE *f;
    bool wizardHelp;
    bool flatCif = FALSE;
    bool dolist = FALSE;
    bool doforall = FALSE;
    float curscale;
    int argc = cmd->tx_argc;
    char **argv = cmd->tx_argv;

    static char *cmdCifWarnOptions[] = { "default", "none", "align",
            "limit", "redirect", "help", 0 };
    static char *cmdCifYesNo[] = { "no", "yes", 0 };
    static char *cmdCifInOut[] = { "input", "output", 0 };
    static char *cmdCifOption[] =
    { 
      "*array layer           display CIF layer under box (array only)",
      "*hier layer            display CIF layer under box (hier only)",
      "arealabels yes|no      enable/disable us of area label extension",
      "coverage layer         print area coverage of indicated layer",
      "help       print this help information",
      "idcell yes|no    enable/disable use of cell ID extension",
      "istyle [style]   change style for reading CIF to style",
      "lambda in|out    show microns per lambda for the current style",
      "limit [value]    limit internal grid subdivision by scalefactor value",
      "ostyle [style]   change style for writing CIF to style",
      "paint ciflayer magiclayer [cellname]\n"
      "                 generate CIF and paint into cell",
      "prefix [path]    prepend path to cell names in CIF output",
      "read file        read CIF from \"file\" into edit cell",
      "rescale [yes|no] allow/disallow rescaling of internal grid",
      "see layer        display CIF layer under box",
      "statistics       print out statistics for CIF generator",
      "warning [option] set warning display options",
      "write file       output CIF for the window's root cell to \"file\"",
      "flat file        output flattened CIF for "
                        "the window's root cell to \"file\"",
#ifdef NONMANHATTAN
      "polygon subcells [yes|no]\n"
      "                 put non-Manhattan polygons in subcells",
#endif
      NULL
    };

    if (argc == 1)
      option = CIF_WRITE;
    else
    {
      if (!strncmp(argv[1], "list", 4))   
      {
          dolist = TRUE;
          if (!strncmp(argv[1], "listall", 7))
          {
            doforall = TRUE;
          }
          argv++;
          argc--;
      }
      option = Lookup(argv[1], cmdCifOption);
      if (option < 0)
      {
          TxError("\"%s\" isn't a valid cif option.\n", argv[1]);
          option = HELP;
          argc = 2;
      }
    }

    /* Only check for the presence of a window on options requiring one */

    switch (option)
    {
      case HELP: case ISTYLE: case OSTYLE: case PREFIX:
      case AREALABELS: case WARNING: case CIF_LIMIT:
#ifdef NONMANHATTAN
      case POLYGONS:
#endif
         break;
      default:
          windCheckOnlyWindow(&w, DBWclientID);
          if (w == (MagWindow *) NULL)
          {
            TxError("Point to a window first\n");
            return;
          }
          rootDef = ((CellUse *) w->w_surfaceID)->cu_def;

          if (argc == 1)
          {
            namep = rindex(rootDef->cd_name, '/');
            if (namep == (char *) NULL)
                namep = rootDef->cd_name;
            goto outputCIF;
          }
    }

    switch (option)
    {
      case ARRAY:
          if (argc == 4) {
              if (!strncmp(argv[2], "write", 5))
            {
                if (!strncmp(argv[3], "dis", 3))
                  CIFArrayWriteDisable = TRUE;
                else if (!strncmp(argv[3], "en", 2))
                  CIFArrayWriteDisable = FALSE;
                else
                  TxPrintf("Subcell interaction output %s\n",
                        (CIFArrayWriteDisable) ? "disabled" : "enabled");
                return;
            }
            else goto wrongNumArgs;
          }
          else if (argc != 3)
          {
            wrongNumArgs:
            TxError("Wrong arguments in \"cif %s\" command:\n",
                argv[1]);
            TxError("    :cif %s\n", cmdCifOption[option]);
            TxError("Try \":cif help\" for more help.\n");
            return;
          }
          if (!ToolGetBox(&rootDef, &box))
          {
            TxError("Use the box to select the area in");
            TxError(" which you want to see CIF.\n");
            return;
          }
          CIFSeeHierLayer(rootDef, &box, argv[2], TRUE, FALSE);
          return;
      
      case HIER:
          if (argc == 4) {
              if (!strncmp(argv[2], "write", 5))
            {
                if (!strncmp(argv[3], "dis", 3))
                  CIFHierWriteDisable = TRUE;
                else if (!strncmp(argv[3], "en", 2))
                  CIFHierWriteDisable = FALSE;
                else
                  TxPrintf("Subcell interaction output %s\n",
                        (CIFHierWriteDisable) ? "disabled" : "enabled");
                return;
            }
            else goto wrongNumArgs;
          }
          else if (argc != 3) goto wrongNumArgs;
          if (!ToolGetBox(&rootDef, &box))
          {
            TxError("Use the box to select the area in");
            TxError(" which you want to see CIF.\n");
            return;
          }
          CIFSeeHierLayer(rootDef, &box, argv[2], FALSE, TRUE);
          return;

      case COVERAGE:
          if ((argc == 4) && !strcmp(argv[3], "box"))
          {
            if (!ToolGetBox(&rootDef, &box))
            {
                TxError("Box requested but no cursor box exists\n");
                return;
            }
            CIFCoverageLayer(rootDef, &box, argv[2]);
          }
          else if (argc == 3)
            CIFCoverageLayer(rootDef, &rootDef->cd_bbox, argv[2]);
          else
            goto wrongNumArgs;

          return;

      case CIF_LAMBDA:
          if (argc != 3) goto wrongNumArgs;
          yesno = Lookup(argv[2], cmdCifInOut); /* "input" or "output" */
          if (yesno < 0)
            goto wrongNumArgs;
          if (yesno == 0)           /* option "input" */
            curscale = CIFGetInputScale(1000);
          else
            curscale = CIFGetOutputScale(1000);
#ifdef MAGIC_WRAPPER
          Tcl_SetObjResult(magicinterp, Tcl_NewDoubleObj((double)curscale));
#else
          TxPrintf("%s lambda is %2.1f microns.\n", (yesno) ?
                  "Output" : "Input", curscale);
#endif
          return;

      case CIF_LIMIT:
          if (argc == 2)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(CIFRescaleLimit));
#else
            TxPrintf("Rescale limit is %d\n", CIFRescaleLimit);
#endif
            return;
          }
          else if (argc != 3)
            goto wrongNumArgs;
          else if (!StrIsInt(argv[2]))
            goto wrongNumArgs;

          CIFRescaleLimit = atoi(argv[2]);
          if (CIFRescaleLimit > CIFMAXRESCALE)
          {
            CIFRescaleLimit = CIFMAXRESCALE;
            TxError("Warning:  rescale limit set at maximum value of %d\n",
                  CIFMAXRESCALE);
          }
          else if (CIFRescaleLimit < 1)
          {
            CIFRescaleLimit = 1;
            TxError("Warning:  rescale limit set at minimum value of 1\n");
          }
          return;

      case AREALABELS:
          if (argc > 3) goto wrongNumArgs;
          if (argc == 3)
          {
            yesno = Lookup(argv[2], cmdCifYesNo);
            if (yesno < 0)
                goto wrongNumArgs;
            CIFDoAreaLabels = yesno;
          }
          TxPrintf("Output of CIF area labels is now %s\n",
            CIFDoAreaLabels ? "enabled" : "disabled");
          return;

      case HELP:
          if ((argc == 3)
                && (strcmp(argv[2], "wizard") == 0))
            wizardHelp = TRUE;
          else wizardHelp = FALSE;
          TxPrintf("CIF commands have the form \":cif option\",");
          TxPrintf(" where option is one of:\n");
          for (msg = &(cmdCifOption[0]); *msg != NULL; msg++)
          {
            if ((**msg == '*') && !wizardHelp) continue;
            TxPrintf("    %s\n", *msg);
          }
          TxPrintf("If no option is given, CIF is output for the");
          TxPrintf(" root cell.\n");
          return;

      case IDCELL:
          if (argc > 3) goto wrongNumArgs;
          if (argc == 3)
          {
            yesno = Lookup(argv[2], cmdCifYesNo);
            if (yesno < 0)
                goto wrongNumArgs;
            CIFDoCellIdLabels = yesno;
          }
          TxPrintf ("Output of CIF cell ID labels is now %s\n",
            CIFDoCellIdLabels ? "enabled" : "disabled");
          return;
      
      case ISTYLE:
          if (argc == 3)
            CIFSetReadStyle(argv[2]);
          else if (argc == 2)
            CIFPrintReadStyle(dolist, doforall, !doforall);
          else goto wrongNumArgs;
          return;

#ifdef NONMANHATTAN
      case POLYGONS:
          if (argc == 3)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CIFSubcellPolygons));
#else
            if (CIFSubcellPolygons)
                TxPrintf("Non-manhattan polygons placed in subcells.\n");
            else
                TxPrintf("Non-manhattan polygons read as-is.\n");
#endif
            return;
          }
          else if (argc != 4)
            goto wrongNumArgs;

          yesno = Lookup(argv[3], cmdCifYesNo);
          if (yesno < 0)
            goto wrongNumArgs;
          CIFSubcellPolygons = yesno;
          return;
#endif  /* NONMANHATTAN */

      case PREFIX:
          if (argc == 2)
            StrDup (&CIFPathPrefix, NULL);
          else if (argc == 3)
            StrDup (&CIFPathPrefix, argv[2]);
          else goto wrongNumArgs;
          return;
      
      case OSTYLE:
          if (argc == 3)
            CIFSetStyle(argv[2]);
          else if (argc == 2)
            CIFPrintStyle(dolist, doforall, !doforall);
          else goto wrongNumArgs;
          return;

      case READ:
          if (argc != 3) goto wrongNumArgs;
          f = PaOpen(argv[2], "r", ".cif", Path,
                (char *) NULL, (char **) NULL);
          if (f == (FILE *) NULL)
          {
            TxError("Cannot open %s.cif to read CIF.\n", argv[2]);
            return;
          }
          CIFReadFile(f);
          (void) fclose(f);
          return;
      
      case RESCALE:
          if (argc == 2)
          {
#ifdef MAGIC_WRAPPER
            Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CIFRescaleAllow));
#else
            TxPrintf("Internal grid rescaling %sallowed\n",
                  (CIFRescaleAllow) ?  "" : "dis");
#endif
            return;
          }
          else if (argc != 3)
            goto wrongNumArgs;

          yesno = Lookup(argv[2], cmdCifYesNo);
          if (yesno < 0)
            goto wrongNumArgs;
          CIFRescaleAllow = yesno;
          if (!CIFRescaleAllow)
            CIFWarningLevel = CIF_WARN_LIMIT;
          return;
      
      case SEE:
          if (argc != 3) goto wrongNumArgs;
          if (!ToolGetBox(&rootDef, &box))
          {
            TxError("Use the box to select the area in");
            TxError(" which you want to see CIF.\n");
            return;
          }
          CIFSeeLayer(rootDef, &box, argv[2]);
          return;

      case CIF_PAINT:
          if ((argc != 4) && (argc != 5)) goto wrongNumArgs;
          if (!ToolGetBox(&rootDef, &box))
          {
            TxError("Use the box to select the area in");
            TxError(" which you want to see CIF.\n");
            return;
          }
          if (argc == 5)
          {
            paintDef = DBCellLookDef(argv[4]);
            if (paintDef == (CellDef *)NULL)
            {
                paintDef = DBCellNewDef(argv[4], (char *)NULL);
                DBCellSetAvail(paintDef);
            }
          }
          else
            paintDef = rootDef;

          layer = DBTechNoisyNameType(argv[3]);
          if (layer >= TT_TECHDEPBASE)
          {
            CIFPaintLayer(rootDef, &box, argv[2], layer, paintDef);

            /* Refresh the layout drawing */
            TTMaskSetOnlyType(&mask, layer);
            DBWAreaChanged(paintDef, &box, DBW_ALLWINDOWS, &mask);
            DRCCheckThis(paintDef, TT_CHECKPAINT, &box);
          }
          return;
      
      case STATS:
          CIFPrintStats();
          return;

      case WARNING:
          if (argc == 2)
          {
            TxPrintf("Warning display options: %s\n",
                  cmdCifWarnOptions[CIFWarningLevel]);
          }
          else
          {
            int suboption = Lookup(argv[2], cmdCifWarnOptions);
            if (suboption < 0)
            {
                TxError("\"%s\" isn't a valid cif warning option.\n", argv[2]);
                suboption = CIF_WARN_HELP;
            }
            if (suboption == CIF_WARN_HELP)
                TxError("Valid options are: default, align, limit, "
                  "redirect, and none\n");
            else
                CIFWarningLevel = suboption;
            if (suboption == CIF_WARN_REDIRECT)
            {
                if (argc == 4)
                  StrDup(&CIFErrorFilename, argv[3]);
                else
                  StrDup(&CIFErrorFilename, NULL);
            }
          }
          return;

      case CIF_WRITE:
          if (argc != 3) goto wrongNumArgs;
          namep = argv[2];
          goto outputCIF;

      case CIF_WRITE_FLAT:
          if (argc != 3) goto wrongNumArgs;
          namep = argv[2];
          flatCif = TRUE;
          goto outputCIF;
    }

    /* If control gets here, we're going to output CIF into the
     * file given by namep.
     */

    outputCIF:
    f = PaOpen(namep, "w", ".cif", ".", (char *) NULL, (char **) NULL);
    if (f == (FILE *) NULL)
    {
      TxError("Cannot open %s.cif to write CIF\n", namep);
      return;
    }
    if (flatCif == TRUE)
    {
      if (!CIFWriteFlat(rootDef, f))
      {
          TxError("I/O error in writing file %s.\n", namep);
          TxError("File may be incompletely written.\n");
    
      }
    }
    else
    {
      if (!CIFWrite(rootDef, f))
      {
          TxError("I/O error in writing file %s.\n", namep);
          TxError("File may be incompletely written.\n");
    
      }
    }
    (void) fclose(f);
}
#endif

/*
 * ----------------------------------------------------------------------------
 *
 * CmdClockwise --
 *
 * Implement the "clockwise" command.  Rotate the selection and the
 * box clockwise around the point.
 *
 * Usage:
 *    clockwise [degrees] [-origin] or
 *    rotate [degrees] [-origin]
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Modifies the edit cell.
 *
 * Notes:
 *    "rotate" has been added as an alias for "clockwise" because the
 *    usual shorthand "clock" conflicts with the built-in Tcl command
 *    in the Tcl version.  "clockwise" is still kept for backward
 *    compatibility but the command must contain at least "clockw" for
 *    the interpreter to uniquely identify it.
 *
 *    The "-origin" option has been added to allow rotating stuff
 *    relative to the origin, insteadd of the lower-left corner.
 *
 * ----------------------------------------------------------------------------
 */

void
CmdClockwise(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    Transform trans, t2;
    int degrees, locargc;
    Rect rootBox,  bbox;
    CellDef *rootDef;
    bool noAdjust = FALSE;

    locargc = cmd->tx_argc;
    if (!strncmp(cmd->tx_argv[locargc - 1], "-orig", 5))
    {
      noAdjust = TRUE;
      locargc--;
    }

    if (locargc == 1)
      degrees = 90;
    else if (locargc == 2)
    {
      if (!StrIsInt(cmd->tx_argv[1])) goto badusage;
      degrees = atoi(cmd->tx_argv[1]);
    }
    else goto badusage;

    if (!ToolGetEditBox((Rect *)NULL)) return;
    if (degrees < 0) degrees += 360;

    switch (degrees)
    {
      case 90:
          t2 = Geo90Transform;
          break;
      case 180:
          t2 = Geo180Transform;
          break;
      case 270:
          t2 = Geo270Transform;
          break;
      default:
          TxError("Rotation angle must be 90, 180, or 270 degrees\n");
          return;
    }


    /* To rotate the selection, first rotate it around the origin
     * then move it so its lower-left corner is at the same place
     * that it used to be.
     */
    
    if (noAdjust)
    {
      GeoTransRect(&t2, &SelectDef->cd_bbox, &bbox);
      trans = t2;
    }
    else
    {
      GeoTransRect(&t2, &SelectDef->cd_bbox, &bbox);
      GeoTranslateTrans(&t2, SelectDef->cd_bbox.r_xbot - bbox.r_xbot,
            SelectDef->cd_bbox.r_ybot - bbox.r_ybot, &trans);
    }

    SelectTransform(&trans);

    /* Rotate the box, if it exists and is in the same window as the
     * selection.
     */
    
    if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef))
    {
      Rect newBox;

      GeoTransRect(&trans, &rootBox, &newBox);
      DBWSetBox(rootDef, &newBox);
    }

    return;

    badusage:
    TxError("Usage: %s [degrees]\n", cmd->tx_argv[0]);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdContact --
 *
 * Implement the "contact" command.
 *
 * Usage:
 *    contact <type>
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The area in the box is searched for the intersection of all residues
 *    of the contact type <type>.  The intersecting areas are replaced with
 *    the contact type.
 *
 * ----------------------------------------------------------------------------
 */

01505 typedef struct CCS
{
    CellDef *rootDef;
    TileTypeBitMask *rmask;
    TileType tstart;
    Rect area;
    Rect clip;
    LinkedRect *lhead;
} CCStruct;

void
CmdContact(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    TileType type, rtype;
    TileTypeBitMask *rmask, smask;
    CCStruct ccs;
    Rect area;
    int cmdContactFunc();     /* Forward declaration */

    windCheckOnlyWindow(&w, DBWclientID);
    if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID))
    {
      TxError("Put the cursor in a layout window\n");
      return;
    }

    if (cmd->tx_argc != 2)
    {
      TxError("Usage: %s <contact_type>\n", cmd->tx_argv[0]);
      return;
    }
    if (!ToolGetEditBox(&area)) return;

    type = DBTechNoisyNameType(cmd->tx_argv[1]);
    if (type >= 0)
    {
      if (!DBIsContact(type))
      {
          TxError("Error:  tile type \"%s\" is not a contact.\n",
                  cmd->tx_argv[1]);
          return;
      }

      /* Find the first residue type */
      rmask = DBResidueMask(type);
      for (rtype = 0; rtype < DBNumUserLayers; rtype++)
          if (TTMaskHasType(rmask, rtype))
            break;

      /* Find all tiles of one residue type */
      ccs.lhead = (LinkedRect *)NULL;
      ccs.rmask = rmask;
      ccs.tstart = rtype;
      ccs.rootDef = EditCellUse->cu_def;
      ccs.clip = area;
      TTMaskSetOnlyType(&smask, rtype);
      DBSrPaintArea((Tile *) NULL, EditCellUse->cu_def->cd_planes[DBPlane(rtype)],
            &area, &smask, cmdContactFunc, (ClientData) &ccs);

      while (ccs.lhead != NULL)
      {
          TTMaskSetOnlyType(&smask, type);
          TTMaskAndMask(&smask, &DBActiveLayerBits);
          DBPaintMask(EditCellUse->cu_def, &ccs.lhead->r_r, &smask);
          freeMagic(ccs.lhead);
          ccs.lhead = ccs.lhead->r_next;
      }

      /* Refresh the layout drawing */
      DBWAreaChanged(EditCellUse->cu_def, &area, DBW_ALLWINDOWS, &smask);
      DRCCheckThis (EditCellUse->cu_def, TT_CHECKPAINT, &area);
    }
}

/* For each tile in the first residue type, search for tiles of the
 * next residue type, and add the intersection of areas to the linked
 * rectangle list.
 */

int
cmdContactFunc(tile, ccs)
    Tile *tile;
    CCStruct *ccs;
{
    TileType stype;
    TileTypeBitMask smask;
    int cmdContactFunc2();    /* Forward declaration */

    TiToRect(tile, &ccs->area);
    GeoClip(&ccs->area, &ccs->clip);

    for (stype = ccs->tstart + 1; stype < DBNumUserLayers; stype++)
      if (TTMaskHasType(ccs->rmask, stype))
          break;

    TTMaskSetOnlyType(&smask, stype);
    DBSrPaintArea((Tile *) NULL, ccs->rootDef->cd_planes[DBPlane(stype)],
            &ccs->area, &smask, cmdContactFunc2, (ClientData)ccs);
    return 0;
}

int
cmdContactFunc2(tile, ccs)
    Tile *tile;
    CCStruct *ccs;
{
    LinkedRect *newlr;
    Rect area;
    TiToRect(tile, &area);

    GeoClip(&area, &ccs->area);

    newlr = (LinkedRect *) mallocMagic(sizeof(LinkedRect));
    newlr->r_r = area;
    newlr->r_next = ccs->lhead;
    ccs->lhead = newlr;

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdCopy --
 *
 * Implement the "copy" command.
 *
 * Usage:
 *    copy [direction [amount]]
 *    copy to x y
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The selection is copied.
 *
 * ----------------------------------------------------------------------------
 */

void
CmdCopy(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    Transform t;
    Rect rootBox, newBox;
    Point rootPoint, editPoint;
    CellDef *rootDef;
    int argpos;

    if (cmd->tx_argc > 4)
    {
      badUsage:
      TxError("Usage: %s [direction [amount]]\n", cmd->tx_argv[0]);
      TxError("   or: %s to x y\n", cmd->tx_argv[0]);
      return;
    }

    if (cmd->tx_argc > 1)
    {
      int indx, amountx, amounty;
      int xdelta, ydelta;

      if (!ToolGetEditBox((Rect *)NULL)) return;

      if (strcmp(cmd->tx_argv[1], "to") == 0)
      {
          if (cmd->tx_argc != 4)
            goto badUsage;
          editPoint.p_x = cmdParseCoord(w, cmd->tx_argv[2], FALSE, TRUE);
          editPoint.p_y = cmdParseCoord(w, cmd->tx_argv[3], FALSE, FALSE);
          GeoTransPoint(&EditToRootTransform, &editPoint, &rootPoint);
          goto copyToPoint;
      }

      indx = GeoNameToPos(cmd->tx_argv[1], FALSE, FALSE);
      argpos = (indx < 0) ? 1 : 2;

      if (cmd->tx_argc >= 3)
      {
          switch (indx)
          {
            case GEO_EAST: case GEO_WEST:
                amountx = cmdParseCoord(w, cmd->tx_argv[argpos], TRUE, TRUE);
                amounty = 0;
                break;
            case GEO_NORTH: case GEO_SOUTH:
                amountx = 0;
                amounty = cmdParseCoord(w, cmd->tx_argv[argpos], TRUE, FALSE);
                break;
            default:
                amountx = cmdParseCoord(w, cmd->tx_argv[argpos], TRUE, TRUE);
                amounty = cmdParseCoord(w, cmd->tx_argv[cmd->tx_argc - 1],
                        TRUE, FALSE);
                break;
          }
      }
      else
      {
          if (indx < 0) {
            TxError("Improperly defined copy. . . direction needed.\n");
            return;
          }
          amountx = cmdParseCoord(w, "1l", TRUE, TRUE);
          amounty = cmdParseCoord(w, "1l", TRUE, FALSE);
      }

      switch (indx)
      {
          case GEO_NORTH:
            xdelta = 0;
            ydelta = amounty;
            break;
          case GEO_SOUTH:
            xdelta = 0;
            ydelta = -amounty;
            break;
          case GEO_EAST:
            xdelta = amountx;
            ydelta = 0;
            break;
          case GEO_WEST:
            xdelta = -amountx;
            ydelta = 0;
            break;
          case GEO_NORTHWEST:
            xdelta = -amountx;
            ydelta = amounty;
            break;
          case GEO_NORTHEAST:
          case -2:
            xdelta = amountx;
            ydelta = amounty;
            break;
          case GEO_SOUTHWEST:
            xdelta = -amountx;
            ydelta = -amounty;
            break;
          case GEO_SOUTHEAST:
            xdelta = amountx;
            ydelta = -amounty;
            break;
          case GEO_CENTER:
            xdelta = 0;
            ydelta = 0;
            break;
          default:
            ASSERT(FALSE, "Bad direction in CmdCopy");
            return;
      }
      GeoTransTranslate(xdelta, ydelta, &GeoIdentityTransform, &t);

      /* Move the box by the same amount as the selection, if the
       * box exists.
       */

      if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef))
      {
          GeoTransRect(&t, &rootBox, &newBox);
          DBWSetBox(rootDef, &newBox);
      }
    }
    else
    {
      /* Use the displacement between the box lower-left corner and
       * the point as the transform.
       */
      
      MagWindow *window;

      window = ToolGetPoint(&rootPoint, (Rect *) NULL);
      if ((window == NULL) ||
          (EditRootDef != ((CellUse *) window->w_surfaceID)->cu_def))
      {
          TxError("\"Copy\" uses the point as the place to put down a\n");
          TxError("    copy of the selection, but the point doesn't\n");
          TxError("    point to the edit cell.\n");
          return;
      }

copyToPoint:
      if (!ToolGetBox(&rootDef, &rootBox) || (rootDef != SelectRootDef))
      {
          TxError("\"Copy\" uses the box lower-left corner as a place\n");
          TxError("    to pick up the selection for copying, but the box\n");
          TxError("    isn't in a window containing the selection.\n");
          return;
      }
      GeoTransTranslate(rootPoint.p_x - rootBox.r_xbot,
          rootPoint.p_y - rootBox.r_ybot, &GeoIdentityTransform, &t);
      GeoTransRect(&t, &rootBox, &newBox);
      DBWSetBox(rootDef, &newBox);
    }
    SelectCopy(&t);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdCorner --
 *
 * Implement the "corner" command.  Find all paint touching one side
 * of the box, and paint it around two edges of the box in an "L"
 * shape.
 *
 * Usage:
 *    corner firstDirection secondDirection [layers]
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The edit cell is modified.
 *
 * ----------------------------------------------------------------------------
 */

/* Data passed between CmdCorner and cmdCornerFunc: */

int cmdCornerDir1;                  /* First direction each wire must
                               * be extended.
                               */
int cmdCornerDir2;                  /* Second direction each wire must
                               * be extended.
                               */
Rect cmdCornerRootBox;              /* Root coords of box. */
struct cmdCornerArea *cmdCornerList;      /* List of areas to fill. */

#ifdef NONMANHATTAN
typedef struct cifpathlist
{
    TileType pathtype;
    CIFPath *pathhead;
    struct cifpathlist *cpl_next;
} CIFPathList;

typedef struct nmcornerpath
{
    bool hasErr;
    CIFPathList *pathlist;
} NMCornerPath;

#endif

void
CmdCorner(w, cmd)
    MagWindow *w; /* Window in which command was invoked. */
    TxCommand *cmd;     /* Describes the command that was invoked. */
{
    TileTypeBitMask maskBits;
    Rect editBox;
    SearchContext scx;
    extern int cmdCornerFunc();
    bool hasErr = FALSE;
    int locargc = cmd->tx_argc;

#ifdef NONMANHATTAN
    extern int cmdBevelFunc();
    bool dobevel = FALSE;
    NMCornerPath cmdPathList;

    if (cmd->tx_argc < 3 || cmd->tx_argc > 5)
    {
      TxError("Usage: %s direction1 direction2 [layers]\n",
                  cmd->tx_argv[0]);
#else
    if (cmd->tx_argc < 3 || cmd->tx_argc > 4)
    {
      TxError("Usage: %s direction1 direction2 [layers]\n", cmd->tx_argv[0]);
#endif
      return;
    }

    windCheckOnlyWindow(&w, DBWclientID);
    if ( w == (MagWindow *) NULL )
    {
      TxError("Point to a window\n");
      return;
    }

    /* Find and check validity of directions. */

    cmdCornerDir1 = GeoNameToPos(cmd->tx_argv[1], TRUE, TRUE);
    if (cmdCornerDir1 < 0)
      return;
    cmdCornerDir2 = GeoNameToPos(cmd->tx_argv[2], TRUE, TRUE);
    if (cmdCornerDir2 < 0)
      return;
    if ((cmdCornerDir1 == GEO_NORTH) || (cmdCornerDir1 == GEO_SOUTH))
    {
      if ((cmdCornerDir2 == GEO_NORTH) || (cmdCornerDir2 == GEO_SOUTH))
      {
          TxPrintf("Can't corner-fill %s and then %s.\n",
                cmd->tx_argv[1], cmd->tx_argv[2]);
          return;
      }
    }
    else
    {
      if ((cmdCornerDir2 == GEO_EAST) || (cmdCornerDir2 == GEO_WEST))
      {
          TxPrintf("Can't corner-fill %s and then %s.\n",
                cmd->tx_argv[1], cmd->tx_argv[2]);
          return;
      }
    }

#ifdef NONMANHATTAN
    /* Check for "bevel" keyword */

    if (locargc > 3)
    {
      if (!strncmp(cmd->tx_argv[locargc - 1], "bevel", 5))
      {
          locargc--;
          dobevel = TRUE;
      }
    }
#endif  /* NONMANHATTAN */

    /* Figure out which layers to fill. */

    if (locargc < 4)
      maskBits = DBAllButSpaceAndDRCBits;
    else
    {
      if (!CmdParseLayers(cmd->tx_argv[3], &maskBits))
          return;
    }

    /* Figure out which material to search for and invoke a search
     * procedure to find it.
     */

    if (!ToolGetEditBox(&editBox)) return;
    GeoTransRect(&EditToRootTransform, &editBox, &cmdCornerRootBox);
    scx.scx_area = cmdCornerRootBox;
    switch (cmdCornerDir1)
    {
      case GEO_NORTH:
          scx.scx_area.r_ytop = scx.scx_area.r_ybot + 1;
          scx.scx_area.r_ybot -= 1;
          break;
      case GEO_SOUTH:
          scx.scx_area.r_ybot = scx.scx_area.r_ytop - 1;
          scx.scx_area.r_ytop += 1;
          break;
      case GEO_EAST:
          scx.scx_area.r_xtop = scx.scx_area.r_xbot + 1;
          scx.scx_area.r_xbot -= 1;
          break;
      case GEO_WEST:
          scx.scx_area.r_xbot = scx.scx_area.r_xtop - 1;
          scx.scx_area.r_xtop += 1;
          break;
    }
    scx.scx_use = (CellUse *) w->w_surfaceID;
    scx.scx_trans = GeoIdentityTransform;

#ifdef NONMANHATTAN
    if (dobevel)
    {
      cmdPathList.hasErr = FALSE;
      cmdPathList.pathlist = NULL;

      (void) DBTreeSrTiles(&scx, &maskBits,
            ((DBWclientRec *) w->w_clientData)->dbw_bitmask,
            cmdBevelFunc, (ClientData) &cmdPathList);

      if (cmdPathList.hasErr)
          TxError("There's not enough room in the box for all the wires.\n");

      while (cmdPathList.pathlist != NULL)
      {
          /* It is a bit of a hack to use CIFPolyToRects here, but it   */
          /* beats rewriting the entire path parsing routine.           */

          LinkedRect *rectp;
          PaintUndoInfo ui;
          int pNum =  DBPlane(cmdPathList.pathlist->pathtype);
          PaintResultType  *resultTbl =
                  DBStdPaintTbl(cmdPathList.pathlist->pathtype, pNum);
          Plane *plane = EditRootDef->cd_planes[pNum];
      
          ui.pu_def = EditRootDef;
          ui.pu_pNum = pNum;

          rectp = CIFPolyToRects(cmdPathList.pathlist->pathhead, plane,
                  resultTbl, &ui);
          for (; rectp != NULL; rectp = rectp->r_next)
          {
            DBPaintPlane(plane, &rectp->r_r, resultTbl, &ui);
            freeMagic((char *)rectp);
          }
          CIFFreePath(cmdPathList.pathlist->pathhead);
          freeMagic((char *)cmdPathList.pathlist);
          cmdPathList.pathlist = cmdPathList.pathlist->cpl_next;
      }
    }
    else
    {
#endif

    cmdCornerList = (struct cmdCornerArea *) NULL;

    (void) DBTreeSrTiles(&scx, &maskBits,
          ((DBWclientRec *) w->w_clientData)->dbw_bitmask,
          cmdCornerFunc, (ClientData) &hasErr);

    if (hasErr)
      TxError("There's not enough room in the box for all the wires.\n");

    /* Now that we've got all the material, scan over the list
     * painting the material and freeing up the entries on the list.
     */
    while (cmdCornerList != NULL)
    {
      DBPaint(EditCellUse->cu_def, &cmdCornerList->cca_area,
            cmdCornerList->cca_type);
      freeMagic((char *) cmdCornerList);
      cmdCornerList = cmdCornerList->cca_next;
    }

#ifdef NONMANHATTAN
    }
#endif

    SelectClear();
    DBAdjustLabels(EditCellUse->cu_def, &editBox);
    DRCCheckThis(EditCellUse->cu_def, TT_CHECKPAINT, &editBox);
    DBWAreaChanged(EditCellUse->cu_def, &editBox, DBW_ALLWINDOWS, &maskBits);
    DBReComputeBbox(EditCellUse->cu_def);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cmdCornerFunc --
 *
 *    Search procedure called by DBTreeSrTiles from CmdCorner.  Called once
 *    for each tile that crosses the appropriate boundary of the box.
 *    Makes an L-shaped 90 degree turn to extend a wire out of an
 *    adjacent side.
 *
 * Results:
 *    Returns 0 to keep the search alive.
 *
 * Side effects:
 *    Adds paint tiles to the display list.  If there are tiles found
 *    that can't be cornered correctly, the clientData value is set
 *    to TRUE.
 *
 * ----------------------------------------------------------------------------
 */
int
cmdCornerFunc(tile, cxp)
    Tile *tile;               /* Tile to fill with. */
    TreeContext *cxp;         /* Describes state of search. */
{
    Rect r1, r2, r3;
    struct cmdCornerArea *cca;
    bool *errPtr = (bool *) cxp->tc_filter->tf_arg;

    /* Get the tile dimensions in root coordinates.  Clip to the box.
     */
    TiToRect(tile, &r1);
    GeoTransRect(&cxp->tc_scx->scx_trans, &r1, &r2);
    GeoClip(&r2, &cmdCornerRootBox);

    /* Generate r2 and r3, the first and second legs of the L-shaped
     * geometry to be painted for this tile.
     */

    r3 = r2;
    switch (cmdCornerDir1)
    {
      case GEO_NORTH:
          if (cmdCornerDir2 == GEO_EAST)
          {
            r2.r_ytop = r3.r_ytop = cmdCornerRootBox.r_ytop
                  - (r2.r_xbot - cmdCornerRootBox.r_xbot);
            r3.r_xtop = cmdCornerRootBox.r_xtop;
          }
          else
          {
            r2.r_ytop = r3.r_ytop = cmdCornerRootBox.r_ytop
                  - (cmdCornerRootBox.r_xtop - r2.r_xtop);
            r3.r_xbot = cmdCornerRootBox.r_xbot;
          }
          r3.r_ybot = r3.r_ytop - (r2.r_xtop - r2.r_xbot);
          if (r3.r_ybot < cmdCornerRootBox.r_ybot)
            *errPtr = TRUE;
          break;

      case GEO_SOUTH:
          if (cmdCornerDir2 == GEO_EAST)
          {
            r2.r_ybot = r3.r_ybot = cmdCornerRootBox.r_ybot
                  + (r2.r_xbot - cmdCornerRootBox.r_xbot);
            r3.r_xtop = cmdCornerRootBox.r_xtop;
          }
          else
          {
            r2.r_ybot = r3.r_ybot = cmdCornerRootBox.r_ybot
                  + (cmdCornerRootBox.r_xtop - r2.r_xtop);
            r3.r_xbot = cmdCornerRootBox.r_xbot;
          }
          r3.r_ytop = r3.r_ybot + (r2.r_xtop - r2.r_xbot);
          if (r3.r_ytop > cmdCornerRootBox.r_ytop)
            *errPtr = TRUE;
          break;

      case GEO_EAST:
          if (cmdCornerDir2 == GEO_NORTH)
          {
            r2.r_xtop = r3.r_xtop = cmdCornerRootBox.r_xtop
                  - (r2.r_ybot - cmdCornerRootBox.r_ybot);
            r3.r_ytop = cmdCornerRootBox.r_ytop;
          }
          else
          {
            r2.r_xtop = r3.r_xtop = cmdCornerRootBox.r_xtop
                  - (cmdCornerRootBox.r_ytop - r2.r_ytop);
            r3.r_ybot = cmdCornerRootBox.r_ybot;
          }
          r3.r_xbot = r3.r_xtop - (r2.r_ytop - r2.r_ybot);
          if (r3.r_xbot < cmdCornerRootBox.r_xbot)
            *errPtr = TRUE;
          break;

      case GEO_WEST:
          if (cmdCornerDir2 == GEO_NORTH)
          {
            r2.r_xbot = r3.r_xbot = cmdCornerRootBox.r_xbot
                  + (r2.r_ybot - cmdCornerRootBox.r_ybot);
            r3.r_ytop = cmdCornerRootBox.r_ytop;
          }
          else
          {
            r2.r_xbot = r3.r_xbot = cmdCornerRootBox.r_xbot
                  + (cmdCornerRootBox.r_ytop - r2.r_ytop);
            r3.r_ybot = cmdCornerRootBox.r_ybot;
          }
          r3.r_xtop = r2.r_xbot + (r2.r_ytop - r2.r_ybot);
          if (r3.r_xtop > cmdCornerRootBox.r_xtop)
            *errPtr = TRUE;
          break;
    }

    /* Clip the resulting geometry to the box, translate to edit cell
     * coords, and add to the paint list if non-NULL.
     */

    GeoClip(&r2, &cmdCornerRootBox);
    GeoTransRect(&RootToEditTransform, &r2, &r1);
    if (!GEO_RECTNULL(&r1))
    {
      /* Add this rectangle to the list. */

      cca = (struct cmdCornerArea *)
            mallocMagic(sizeof(struct cmdCornerArea));
      cca->cca_area = r1;
      cca->cca_type = TiGetType(tile);
      cca->cca_next = cmdCornerList;
      cmdCornerList = cca;
    }

    GeoClip(&r3, &cmdCornerRootBox);
    GeoTransRect(&RootToEditTransform, &r3, &r1);
    if (!GEO_RECTNULL(&r1))
    {
      cca = (struct cmdCornerArea *)
            mallocMagic(sizeof(struct cmdCornerArea));
      cca->cca_area = r1;
      cca->cca_type = TiGetType(tile);
      cca->cca_next = cmdCornerList;
      cmdCornerList = cca;
    }
    return 0;
}

#ifdef NONMANHATTAN

/*
 * ----------------------------------------------------------------------------
 *
 * Convenience function for cmdBevelFunc.  Allocates a CIFPath record.
 *
 * ----------------------------------------------------------------------------
 */

void
AddNewPoint(CIFPath **pathhead)
{
    CIFPath *newpoint;

    newpoint = (CIFPath *)mallocMagic(sizeof(CIFPath));
    newpoint->cifp_next = *pathhead;
    *pathhead = newpoint;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cmdBevelFunc --
 *
 *    Search procedure called by DBTreeSrTiles from CmdCorner.  Called once
 *    for each tile that crosses the appropriate boundary of the box.
 *    Makes a beveled 90 degree turn to extend a wire out of an
 *    adjacent side.
 *
 * Results:
 *    Returns 0 to keep the search alive.
 *
 * Side effects:
 *    Adds paint tiles to the display list.  If there are tiles found
 *    that can't be cornered correctly, the clientData value is set
 *    to TRUE.
 *
 * ----------------------------------------------------------------------------
 */
int
cmdBevelFunc(tile, cxp)
    Tile *tile;               /* Tile to fill with. */
    TreeContext *cxp;         /* Describes state of search. */
{
    Rect r1, r2, r3;
    CIFPathList *cpl;
    CIFPath *pptr, *pathhead = NULL;
    NMCornerPath *pathrecord = (NMCornerPath *) cxp->tc_filter->tf_arg;
    int wirewidth, boxwidth, boxheight;
    TileType pathtype;

    /* Get the tile dimensions in root coordinates.  Clip to the box.
     */
    TiToRect(tile, &r1);
    GeoTransRect(&cxp->tc_scx->scx_trans, &r1, &r2);
    GeoClip(&r2, &cmdCornerRootBox);

    /* Find the Manhattan rectangles like cmdCornerFunc.  Shrink
     * these to a minimum square or rectangle.  Finally, pick the
     * path points off of the two boxes to generate the nonManhattan
     * path.
     */

    r3 = r2;
    switch (cmdCornerDir1)
    {
      case GEO_NORTH:

          pathtype = TiGetTopType(tile);
          if (pathtype == TT_SPACE) return 0;
          wirewidth = r2.r_xtop - r2.r_xbot;
          if (cmdCornerDir2 == GEO_EAST)
          {
            r3.r_ytop = cmdCornerRootBox.r_ytop
                  - (r2.r_xbot - cmdCornerRootBox.r_xbot);
            r3.r_xtop = r3.r_xbot = cmdCornerRootBox.r_xtop;
            boxwidth = r3.r_xtop - r2.r_xbot;
            boxheight = r3.r_ytop - r3.r_ybot;

            if (boxwidth > boxheight)
                r3.r_xbot = r2.r_xbot + boxheight;
            r3.r_xbot -= wirewidth;
          }
          else
          {
            r3.r_ytop = cmdCornerRootBox.r_ytop
                  - (cmdCornerRootBox.r_xtop - r2.r_xtop);
            r3.r_xbot = r3.r_xtop = cmdCornerRootBox.r_xbot;
            boxwidth = r2.r_xtop - r3.r_xbot;
            boxheight = r3.r_ytop - r3.r_ybot;

            if (boxwidth > boxheight)
                r3.r_xtop = r2.r_xtop - boxheight;
            r3.r_xtop += wirewidth;
          }
          r3.r_ybot = r3.r_ytop - wirewidth;
          if (r3.r_ybot < cmdCornerRootBox.r_ybot)
             pathrecord->hasErr = TRUE;

          if (boxheight > boxwidth)
            r2.r_ytop = r3.r_ytop - boxwidth;
          else
            r2.r_ytop = r2.r_ybot;
          r2.r_ytop += wirewidth;

          /* Create nonManhattan path */
          if (cmdCornerDir2 == GEO_EAST)
          {
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xbot;
            pathhead->cifp_y = r2.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xbot;
            pathhead->cifp_y = r2.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xtop;
            pathhead->cifp_y = r2.r_ybot;
            if (boxheight > boxwidth)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r2.r_xtop;
                pathhead->cifp_y = r2.r_ytop - wirewidth;
            }
            else if (boxwidth > boxheight)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r3.r_xbot + wirewidth;
                pathhead->cifp_y = r3.r_ybot;
            }
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xtop;
            pathhead->cifp_y = r3.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xtop;
            pathhead->cifp_y = r3.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xbot;
            pathhead->cifp_y = r3.r_ytop;
          }
          else
          {
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xtop;
            pathhead->cifp_y = r2.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xtop;
            pathhead->cifp_y = r2.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xbot;
            pathhead->cifp_y = r2.r_ybot;
            if (boxheight > boxwidth)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r2.r_xbot;
                pathhead->cifp_y = r2.r_ytop - wirewidth;
            }
            else if (boxwidth > boxheight)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r3.r_xtop - wirewidth;
                pathhead->cifp_y = r3.r_ybot;
            }
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xbot;
            pathhead->cifp_y = r3.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xbot;
            pathhead->cifp_y = r3.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xtop;
            pathhead->cifp_y = r3.r_ytop;
          }
          break;

      case GEO_SOUTH:
          pathtype = TiGetBottomType(tile);
          if (pathtype == TT_SPACE) return 0;
          wirewidth = r2.r_xtop - r2.r_xbot;
          if (cmdCornerDir2 == GEO_EAST)
          {
            r3.r_ybot = cmdCornerRootBox.r_ybot
                  + (r2.r_xbot - cmdCornerRootBox.r_xbot);
            r3.r_xtop = r3.r_xbot = cmdCornerRootBox.r_xtop;
            boxwidth = r3.r_xtop - r2.r_xbot;
            boxheight = r3.r_ytop - r3.r_ybot;

            if (boxwidth > boxheight)
                r3.r_xbot = r2.r_xbot + boxheight;
            r3.r_xbot -= wirewidth;
          }
          else
          {
            r3.r_ybot = cmdCornerRootBox.r_ybot
                  + (cmdCornerRootBox.r_xtop - r2.r_xtop);
            r3.r_xbot = r3.r_xtop = cmdCornerRootBox.r_xbot;
            boxwidth = r2.r_xtop - r3.r_xbot;
            boxheight = r3.r_ytop - r3.r_ybot;

            if (boxwidth > boxheight)
                r3.r_xtop = r2.r_xtop - boxheight;
            r3.r_xtop += wirewidth;
          }
          r3.r_ytop = r3.r_ybot + wirewidth;
          if (r3.r_ytop > cmdCornerRootBox.r_ytop)
            pathrecord->hasErr = TRUE;

          if (boxheight > boxwidth)
            r2.r_ybot = r3.r_ybot + boxwidth;
          else
            r2.r_ybot = r2.r_ytop;
          r2.r_ybot -= wirewidth;

          /* Create nonManhattan path */
          if (cmdCornerDir2 == GEO_EAST)
          {
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xbot;
            pathhead->cifp_y = r2.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xbot;
            pathhead->cifp_y = r2.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xtop;
            pathhead->cifp_y = r2.r_ytop;
            if (boxheight > boxwidth)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r2.r_xtop;
                pathhead->cifp_y = r2.r_ybot + wirewidth;
            }
            else if (boxwidth > boxheight)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r3.r_xbot + wirewidth;
                pathhead->cifp_y = r3.r_ytop;
            }
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xtop;
            pathhead->cifp_y = r3.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xtop;
            pathhead->cifp_y = r3.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xbot;
            pathhead->cifp_y = r3.r_ybot;
          }
          else
          {
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xtop;
            pathhead->cifp_y = r2.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xtop;
            pathhead->cifp_y = r2.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xbot;
            pathhead->cifp_y = r2.r_ytop;
            if (boxheight > boxwidth)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r2.r_xbot;
                pathhead->cifp_y = r2.r_ybot + wirewidth;
            }
            else if (boxwidth > boxheight)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r3.r_xtop - wirewidth;
                pathhead->cifp_y = r3.r_ytop;
            }
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xbot;
            pathhead->cifp_y = r3.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xbot;
            pathhead->cifp_y = r3.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xtop;
            pathhead->cifp_y = r3.r_ybot;
          }
          break;

      case GEO_EAST:
          pathtype = TiGetRightType(tile);
          if (pathtype == TT_SPACE) return 0;
          wirewidth = r2.r_ytop - r2.r_ybot;
          if (cmdCornerDir2 == GEO_NORTH)
          {
            r3.r_xtop = cmdCornerRootBox.r_xtop
                  - (r2.r_ybot - cmdCornerRootBox.r_ybot);
            r3.r_ytop = r3.r_ybot = cmdCornerRootBox.r_ytop;
            boxwidth = r3.r_xtop = r3.r_xbot;
            boxheight = r3.r_xtop = r2.r_ybot;

            if (boxheight > boxwidth)
                r3.r_ybot = r2.r_ybot + boxwidth;
            r3.r_ybot -= wirewidth;
          }
          else
          {
            r3.r_xtop = cmdCornerRootBox.r_xtop
                  - (cmdCornerRootBox.r_ytop - r2.r_ytop);
            r3.r_ybot = r3.r_ytop = cmdCornerRootBox.r_ybot;
            boxwidth = r3.r_xtop = r3.r_xbot;
            boxheight = r2.r_xtop = r3.r_ybot;

            if (boxheight > boxwidth)
                r3.r_ytop = r2.r_ytop - boxwidth;
            r3.r_ytop += wirewidth;
          }
          r3.r_xbot = r3.r_xtop - wirewidth;
          if (r3.r_xbot < cmdCornerRootBox.r_xbot)
            pathrecord->hasErr = TRUE;

          if (boxwidth > boxheight)
            r2.r_xtop = r3.r_xtop - boxheight;
          else
            r2.r_xtop = r2.r_xbot;
          r2.r_xtop += wirewidth;

          /* Create nonManhattan path */
          if (cmdCornerDir2 == GEO_NORTH)
          {
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xtop;
            pathhead->cifp_y = r2.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xbot;
            pathhead->cifp_y = r2.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xbot;
            pathhead->cifp_y = r2.r_ytop;
            if (boxheight > boxwidth)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r3.r_xbot;
                pathhead->cifp_y = r3.r_ybot + wirewidth;
            }
            else if (boxwidth > boxheight)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r2.r_xtop - wirewidth;
                pathhead->cifp_y = r2.r_ytop;
            }
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xbot;
            pathhead->cifp_y = r3.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xtop;
            pathhead->cifp_y = r3.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xtop;
            pathhead->cifp_y = r3.r_ybot;
          }
          else
          {
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xtop;
            pathhead->cifp_y = r2.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xbot;
            pathhead->cifp_y = r2.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xbot;
            pathhead->cifp_y = r2.r_ybot;
            if (boxheight > boxwidth)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r3.r_xbot;
                pathhead->cifp_y = r3.r_ytop - wirewidth;
            }
            else if (boxwidth > boxheight)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r2.r_xtop - wirewidth;
                pathhead->cifp_y = r2.r_ybot;
            }
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xbot;
            pathhead->cifp_y = r3.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xtop;
            pathhead->cifp_y = r3.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xtop;
            pathhead->cifp_y = r3.r_ytop;
          }
          break;

      case GEO_WEST:
          pathtype = TiGetLeftType(tile);
          if (pathtype == TT_SPACE) return 0;
          wirewidth = r2.r_ytop - r2.r_ybot;
          if (cmdCornerDir2 == GEO_NORTH)
          {
            r3.r_xbot = cmdCornerRootBox.r_xbot
                  + (r2.r_ybot - cmdCornerRootBox.r_ybot);
            r3.r_ytop = r3.r_ybot = cmdCornerRootBox.r_ytop;
            boxwidth = r3.r_xtop = r3.r_xbot;
            boxheight = r3.r_xtop = r2.r_ybot;

            if (boxheight > boxwidth)
                r3.r_ybot = r2.r_ybot + boxwidth;
            r3.r_ybot -= wirewidth;
          }
          else
          {
            r3.r_xbot = cmdCornerRootBox.r_xbot
                  + (cmdCornerRootBox.r_ytop - r2.r_ytop);
            r3.r_ybot = r3.r_ytop = cmdCornerRootBox.r_ybot;
            boxwidth = r3.r_xtop = r3.r_xbot;
            boxheight = r2.r_xtop = r3.r_ybot;

            if (boxheight > boxwidth)
                r3.r_ytop = r2.r_ytop - boxwidth;
            r3.r_ytop += wirewidth;
          }
          r3.r_xtop = r2.r_xbot + wirewidth;
          if (r3.r_xtop > cmdCornerRootBox.r_xtop)
            pathrecord->hasErr = TRUE;

          if (boxwidth > boxheight)
            r2.r_xbot = r3.r_xbot + boxheight;
          else
            r2.r_xbot = r2.r_xtop;
          r2.r_xbot -= wirewidth;

          /* Create nonManhattan path */
          if (cmdCornerDir2 == GEO_NORTH)
          {
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xbot;
            pathhead->cifp_y = r2.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xtop;
            pathhead->cifp_y = r2.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xtop;
            pathhead->cifp_y = r2.r_ytop;
            if (boxheight > boxwidth)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r3.r_xtop;
                pathhead->cifp_y = r3.r_ybot + wirewidth;
            }
            else if (boxwidth > boxheight)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r2.r_xbot + wirewidth;
                pathhead->cifp_y = r2.r_ytop;
            }
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xtop;
            pathhead->cifp_y = r3.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xbot;
            pathhead->cifp_y = r3.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xbot;
            pathhead->cifp_y = r3.r_ybot;
          }
          else
          {
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xbot;
            pathhead->cifp_y = r2.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xtop;
            pathhead->cifp_y = r2.r_ytop;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r2.r_xtop;
            pathhead->cifp_y = r2.r_ybot;
            if (boxheight > boxwidth)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r3.r_xtop;
                pathhead->cifp_y = r3.r_ytop - wirewidth;
            }
            else if (boxwidth > boxheight)
            {
                AddNewPoint(&pathhead);
                pathhead->cifp_x = r2.r_xbot + wirewidth;
                pathhead->cifp_y = r2.r_ybot;
            }
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xtop;
            pathhead->cifp_y = r3.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xbot;
            pathhead->cifp_y = r3.r_ybot;
            AddNewPoint(&pathhead);
            pathhead->cifp_x = r3.r_xbot;
            pathhead->cifp_y = r3.r_ytop;
          }
          break;
    }

    /* If either rectangle is NULL after clipping, free the point */
    /* memory and return without adding the path.             */

    GeoClip(&r2, &cmdCornerRootBox);
    GeoClip(&r3, &cmdCornerRootBox);
    if (GEO_RECTNULL(&r2) || GEO_RECTNULL(&r3))
    {
      for (pptr = pathhead; pptr != NULL; pptr = pptr->cifp_next)
          freeMagic((char *)pptr);
      return 0;
    }

    /* Clip the point geometry to the box, and translate to edit cell
     * coords.
     */

    for (pptr = pathhead; pptr != NULL; pptr = pptr->cifp_next)
    {
      Point p = pptr->cifp_point;
      GeoClipPoint(&p, &cmdCornerRootBox);
      GeoTransPoint(&RootToEditTransform, &p, &pptr->cifp_point);
    }

    /* Create the path and add it to the list. */

    cpl = (CIFPathList *) mallocMagic(sizeof(CIFPathList));
    cpl->pathtype = pathtype;
    cpl->cpl_next = pathrecord->pathlist;
    cpl->pathhead = pathhead;
    pathrecord->pathlist = cpl;

    return 0;     /* Keep the search going */
}
#endif  /* NONMANHATTAN */

/*
 * ----------------------------------------------------------------------------
 *
 * CmdCrash --
 *
 * Save cells to or recover cells from a crash backup file
 *
 * Usage:
 *    crash save|recover [file]
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Loads the database with multiple cells from the crash recovery
 *    file, and removes the crash recovery file.
 *
 * ----------------------------------------------------------------------------
 */
void
CmdCrash(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    int option;
    char *filename = NULL;
    static char *cmdCrashOpt[] = {"save", "recover", 0};

    if (cmd->tx_argc > 3)
      TxError("Usage: %s save|recover [filename]\n", cmd->tx_argv[0]);
    else if (cmd->tx_argc >= 2)
    {
      option = Lookup(cmd->tx_argv[1], cmdCrashOpt);
      if (option < 0)
      {
          TxError("Usage: %s save|recover [filename]\n", cmd->tx_argv[0]);
          return;
      }
    }
    if (cmd->tx_argc == 3) filename = cmd->tx_argv[2];

    switch(option) {
      case 0:                 /* save */
          DBWriteBackup(filename);
          break;
      case 1:                 /* recover */
          DBFileRecovery(filename);
          break;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdCrosshair --
 *
 * Manipulate the crosshair highlight
 *
 * Usage:
 *    crosshair <x> <y>
 *    crosshair off
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The crosshair is enabled, moved, or disabled.
 * ----------------------------------------------------------------------------
 */
void
CmdCrosshair(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    Point pos;

    if (cmd->tx_argc == 2)
    {
      if (!strcmp(cmd->tx_argv[1], "off"))
      {
          /* Erase and turn off */
          pos.p_x = pos.p_y = MINFINITY;
          DBWSetCrosshair(w, &pos);
      }
    }
    else if (cmd->tx_argc == 3)
    {
      pos.p_x = cmdParseCoord(w, cmd->tx_argv[1], FALSE, TRUE);
      pos.p_y = cmdParseCoord(w, cmd->tx_argv[2], FALSE, FALSE);
      DBWSetCrosshair(w, &pos);
    }
    else
      TxError("Usage: %s off|x y \n", cmd->tx_argv[0]);
}


/*
 * ----------------------------------------------------------------------------
 *
 * CmdDelete --
 *
 * Implement the "delete" command.
 *
 * Usage:
 *    delete
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The selection is deleted.
 *
 * ----------------------------------------------------------------------------
 */

void
CmdDelete(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    if (cmd->tx_argc != 1) goto badusage;
    if (!ToolGetEditBox((Rect *)NULL)) return;

    SelectDelete("deleted", TRUE);
    return;

    badusage:
    TxError("Usage: %s\n", cmd->tx_argv[0]);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdDown --
 *
 * Implement the "down" command.
 * Use the cell that is currently selected as the edit cell.  If more than
 * one cell is selected, use the point to choose between them. Load the
 * new edit cell into the window containing the point tool.
 *
 * Usage:
 *    down
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Sets EditCellUse.
 *
 * ----------------------------------------------------------------------------
 */

/* The following variable is set by cmdDownEnumFunc to signal that there
 * was at least one cell in the selection.
 */

static bool cmdFoundNewDown;

void
CmdDown(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    Rect area, pointArea;
    int cmdEditRedisplayFunc();           /* External declaration. */
    int cmdDownEnumFunc();          /* Forward declaration. */

    if (cmd->tx_argc > 1)
    {
      TxError("Usage: edit\nMaybe you want the \"load\" command\n");
      return;
    }

    /* Record the current edit cell's area for redisplay (now that it's
     * not the edit cell, it will be displayed differently).  Do this
     * only in windows where the edit cell is displayed differently from
     * other cells.
     */
    
    GeoTransRect(&EditToRootTransform, &(EditCellUse->cu_def->cd_bbox), &area);
    (void) WindSearch(DBWclientID, (ClientData) NULL,
          (Rect *) NULL, cmdEditRedisplayFunc, (ClientData) &area);
      
    /* Use the position of the point to select one of the currently-selected
     * cells (if there are more than one).  If worst comes to worst, just
     * select any selected cell.
     */
    
    (void) ToolGetPoint((Point *) NULL, &pointArea);
    cmdFoundNewDown = FALSE;
    (void) SelEnumCells(FALSE, (bool *) NULL, (SearchContext *) NULL,
          cmdDownEnumFunc, (ClientData) &pointArea);
    if (!cmdFoundNewDown)
      TxError("You haven't selected a new cell to edit.\n");

    /* Now record the new edit cell's area for redisplay. */

    GeoTransRect(&EditToRootTransform, &(EditCellUse->cu_def->cd_bbox), &area);
    (void) WindSearch(DBWclientID, (ClientData) NULL,
          (Rect *) NULL, cmdEditRedisplayFunc, (ClientData) &area);
    DBWloadWindow(w, EditCellUse->cu_def->cd_name, TRUE, FALSE);
}

/* Search function to find the new edit cell:  look for a cell use
 * that contains the rectangle passed as argument.  If we find such
 * a use, return 1 to abort the search.  Otherwise, save information
 * about this use anyway:  it'll become the edit cell if nothing
 * better is found.
 */

    /* ARGSUSED */
int
cmdDownEnumFunc(selUse, use, transform, area)
    CellUse *selUse;          /* Use from selection (not used). */
    CellUse *use;       /* Use from layout that corresponds to
                         * selUse (could be an array!).
                         */
    Transform *transform;     /* Transform from use->cu_def to root coords. */
    Rect *area;               /* We're looking for a use containing this
                         * area, in root coords.
                         */
{
    Rect defArea, useArea;

    /* Save this use as the default next edit cell, regardless of whether
     * or not it overlaps the area we're interested in.
     */

    EditToRootTransform = *transform;
    GeoInvertTrans(transform, &RootToEditTransform);
    EditCellUse = use;
    EditRootDef = SelectRootDef;
    cmdFoundNewDown = TRUE;

    /* See if the bounding box of this use overlaps the area we're
     * interested in.
     */

    GeoTransRect(&RootToEditTransform, area, &defArea);
    GeoTransRect(&use->cu_transform, &defArea, &useArea);
    if (!GEO_OVERLAP(&useArea, &use->cu_bbox)) return 0;
    return 1;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdDrc --
 *
 * Implement the "drc" command.
 *
 * Usage:
 *    drc option
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Most options have no side effects.  The only major side
 *    effects are to turn continuous DRC on or off, or recheck an
 *    area of a cell.
 *
 * ----------------------------------------------------------------------------
 */

#define FLATCHECK 0
#define DRC_HALO  1
#define SHOWINT         2
#define DRC_STEPSIZE    3
#define CATCHUP         4
#define CHECK           5
#define COUNT           6
#define EUCLIDEAN 7
#define FIND            8
#define DRC_HELP  9
#define DRC_OFF         10
#define DRC_ON          11
#define DRC_STATUS      12
#define DRC_STYLE 13
#define PRINTRULES      14
#define RULESTATS 15
#define STATISTICS      16
#define WHY       17

void
CmdDrc(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    FILE        * fp;
    static int    drc_nth = 1;
    int             option, result, radius;
    Rect      rootArea, area;
    CellUse * rootUse, *use;
    CellDef * rootDef;
    Transform       trans;
    MagWindow     * window;
    char    **msg;
    bool    wizardHelp;
    bool    incremental;
    bool    doforall = FALSE;
    bool    dolist = FALSE;
    int           count_total;
    DRCCountList *dcl, *dclsrch;
    int argc = cmd->tx_argc;
    char **argv = cmd->tx_argv;

    static char *cmdDrcOption[] =
    { 
      "*flatcheck             check box area by flattening",
      "*halo [d]        limit error checking to areas of d lambda",
      "*showint radius        show interaction area under box",
      "*stepsize [d]          change DRC step size to d lambda",
      "catchup                run checker and wait for it to complete",
      "check                  recheck area under box in all cells",
      "count                  count error tiles in each cell under box",
      "euclidean on|off enable/disable Euclidean geometry checking",
      "find [nth]       locate next (or nth) error in the layout",
      "help                   print this help information",
      "off                    turn off background checker",
      "on                     reenable background checker",
      "status                 report if the drc checker is on or off",
      "style                  set the DRC style",
      "printrules [file]      print out design rules in file or on tty",
      "rulestats              print out stats about design rule database",
      "statistics             print out statistics gathered by checker",
      "why                    print out reasons for errors under box",
      NULL
    };

    if (argc < 2)
    {
      TxError("No option given in \":drc\" command.\n");
      option = DRC_HELP;
    }
    else
    {
      if (!strncmp(argv[1], "list", 4))
      {
          dolist = TRUE;
          if (!strncmp(argv[1], "listall", 7))
          {
            doforall = TRUE;
          }
          argv++;
          argc--;
      }
      option = Lookup(argv[1], cmdDrcOption);
      if (option < 0)
      {
          TxError("%s isn't a valid drc option.\n", argv[1]);
          option = DRC_HELP;
          argc = 2;
      }
      if ((argc > 2) && (option != PRINTRULES) && (option != FIND)
          && (option != SHOWINT) && (option != DRC_HELP) && (option != EUCLIDEAN)
          && (option != DRC_STEPSIZE) && (option != DRC_HALO) && (option != COUNT)
          && (option != DRC_STYLE))
      {
          badusage:
          TxError("Wrong arguments in \"drc %s\" command:\n", argv[1]);
          TxError("    :drc %s\n", cmdDrcOption[option]);
          TxError("Try \":drc help\" for more help.\n");
          return;
      }
    }
    switch (option)
    {
      case FLATCHECK:
          window = ToolGetBoxWindow(&rootArea, (int *) NULL);
          if (window == NULL) return;
          rootUse = (CellUse *) window->w_surfaceID;
          DRCFlatCheck(rootUse, &rootArea);
          break;
      
      case SHOWINT:
          if (argc != 3) goto badusage;
          radius = cmdParseCoord(w, argv[2], TRUE, FALSE);
          if (radius < 0)
          {
            TxPrintf("Radius must not be negative\n");
            return;
          }
          window = ToolGetBoxWindow(&rootArea, (int *) NULL);
          if (window == NULL) return;
          rootUse = (CellUse *) window->w_surfaceID;
          if (!DRCFindInteractions(rootUse->cu_def, &rootArea,
            radius, &area))
          {
            TxPrintf("No interactions in this area for that radius.\n");
            return;
          }
          ToolMoveBox(TOOL_BL, &area.r_ll, FALSE, rootUse->cu_def);
          ToolMoveCorner(TOOL_TR, &area.r_ur, FALSE, rootUse->cu_def);
          break;

      case DRC_STYLE:
          if (argc == 2)
            DRCPrintStyle(dolist, doforall, !doforall);
          else
            DRCSetStyle(argv[2]);
          break;
      
      case CATCHUP:
          DRCCatchUp();
          break;

      case CHECK:
          window = ToolGetBoxWindow(&rootArea, (int *) NULL);
          if (window == NULL) return;
          rootUse = (CellUse *) window->w_surfaceID;
          DRCCheck(rootUse, &rootArea);
          break;
      
      case COUNT:
          count_total = -1;
          if (argc == 3)
            if (!strncmp(argv[2], "total", 5))
                count_total = 0;

          if ((window = w) == NULL)
          {
            window = ToolGetBoxWindow(&rootArea, (int *) NULL);
            if (window == NULL) return;
          }
          else
            rootArea = w->w_surfaceArea;

          rootUse = (CellUse *) window->w_surfaceID;
          dcl = DRCCount(rootUse, &rootArea);
          while (dcl != NULL)
          {
            if (count_total >= 0)
                count_total += dcl->dcl_count;
            else
            {
               
                if (dcl->dcl_count > 1)
                  TxPrintf("Cell %s has %d error tiles.\n",
                        dcl->dcl_def->cd_name, dcl->dcl_count);
                else if (dcl->dcl_count == 1)
                  TxPrintf("Cell %s has just one error tile.\n",
                        dcl->dcl_def->cd_name);
            }
            freeMagic((char *)dcl);
            dcl = dcl->dcl_next;
          }
          if ((DRCBackGround != DRC_SET_OFF) && (count_total == -1))
            count_total = 0;
          if (count_total >= 0)
#ifdef MAGIC_WRAPPER
              Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(count_total));
#else
            TxPrintf("Total DRC errors found: %d\n", count_total);
#endif
          break;

      case EUCLIDEAN:
          if (argc == 2)
          {
            TxPrintf("DRC measurements are %s.\n", DRCEuclidean ?
                  "Euclidean" : "Manhattan");
          }
          else
          {
            if (!strcmp(argv[2], "on"))
                DRCEuclidean = TRUE;
            else
                DRCEuclidean = FALSE;
          }
          break;
      
      case FIND:
          if ((window = w) == NULL)
          {
            window = ToolGetBoxWindow(&rootArea, (int *) NULL);
            if (window == NULL) return;
          }
          else
            rootArea = w->w_surfaceArea;
          if (argc > 3) goto badusage;

          rootUse = (CellUse *) window->w_surfaceID;
          rootDef = rootUse->cu_def;

          incremental = FALSE;
          if (argc == 3 && StrIsInt(argv[2]))
          {
            drc_nth = atoi(argv[2]);
            if (drc_nth <= 0) drc_nth = 1;
          }
          else
          {
              incremental = TRUE;
            drc_nth++;
          }

          result = DRCFind(rootUse, &rootArea, &area, drc_nth);
          if (incremental && (result < 0))
          {
            /* 2nd pass, if we exceeded the total number of errors */
            drc_nth = 1;
            result = DRCFind(rootUse, &rootArea, &area, drc_nth);
          }

          if (result > 0)
          {
            ToolMoveBox(TOOL_BL, &area.r_ll, FALSE, rootDef);
            ToolMoveCorner(TOOL_TR, &area.r_ur, FALSE, rootDef);
            TxPrintf("Error area #%d:\n", result);
            DRCWhy(rootUse, &area);
          }
          else
          {
            if (result < 0) TxPrintf("There aren't that many errors");
            else TxPrintf("There are no errors");
            TxPrintf(" in %s.\n", rootDef->cd_name);
          }
          break;
      
      case DRC_HALO:
          if (argc == 3)
          {
            DRCTechHalo = cmdScaleCoord(w, argv[2], FALSE, TRUE);
            DRCTechHalo *= DRCCurStyle->DRCScaleFactorN;
            DRCTechHalo /= DRCCurStyle->DRCScaleFactorD;
            if (DRCTechHalo > DRCCurStyle->DRCTechHalo)
                DRCTechHalo = DRCCurStyle->DRCTechHalo;

            if (DRCTechHalo < DRCCurStyle->DRCTechHalo)
                TxPrintf("Warning: rulechecking limited to halo of %d lambda.\n",
                        DRCTechHalo * DRCCurStyle->DRCScaleFactorD
                        / DRCCurStyle->DRCScaleFactorN);
            else
                TxPrintf("DRC checks all rules (halo of %d lambda)\n",
                        DRCTechHalo * DRCCurStyle->DRCScaleFactorD
                        / DRCCurStyle->DRCScaleFactorN);
          }
          else
          {
            TxPrintf("DRC halo is %d lambda\n",
                        DRCTechHalo * DRCCurStyle->DRCScaleFactorD
                        / DRCCurStyle->DRCScaleFactorN);
            if (DRCTechHalo != DRCCurStyle->DRCTechHalo)
                TxPrintf("Maximum rule distance is %d lambda\n",
                        DRCCurStyle->DRCTechHalo * DRCCurStyle->DRCScaleFactorD
                        / DRCCurStyle->DRCScaleFactorN);
          }
          break;

      case DRC_HELP:
          if ((argc == 3)
                && (strcmp(argv[2], "wizard") == 0))
            wizardHelp = TRUE;
          else wizardHelp = FALSE;
          TxPrintf("DRC commands have the form \":drc option\",");
          TxPrintf(" where option is one of:\n");
          for (msg = &(cmdDrcOption[0]); *msg != NULL; msg++)
          {
            if ((**msg == '*') && !wizardHelp) continue;
            TxPrintf("    %s\n", *msg);
          }
          break;
      
      case DRC_OFF:
          DRCBackGround = DRC_SET_OFF;
#ifdef MAGIC_WRAPPER
          if (TxInputRedirect != TX_INPUT_REDIRECTED)
#endif
            TxSetPrompt('%');       /* Return prompt to "normal" */
          break;
      
      case DRC_ON:
          /* Don't allow override of DRC_NOT_SET */
          if (DRCBackGround == DRC_SET_OFF) DRCBackGround = DRC_SET_ON;
          break;

      case DRC_STATUS:
#ifdef MAGIC_WRAPPER
          Tcl_SetObjResult(magicinterp,
                  Tcl_NewBooleanObj((DRCBackGround == DRC_SET_OFF) ?
                  FALSE : TRUE));
#else
          TxPrintf("DRC checker is %s.\n", (DRCBackGround == DRC_SET_OFF) ?
                  "off" : "on");
#endif
          break;
      
      case PRINTRULES:
          if (argc > 3) goto badusage;
          if (argc < 3)
            fp = stdout;
          else if ((fp = fopen (argv[2],"w")) == (FILE *) NULL)
          {
            TxError("Cannot write file %s\n", argv[2]);
            return;
          }
          DRCPrintRulesTable (fp);
          if (fp != stdout)
            (void) fclose(fp);
          break;
      
      case RULESTATS:
          DRCTechRuleStats();
          break;
      
      case STATISTICS:
          DRCPrintStats();
          break;

      case DRC_STEPSIZE:
          if (argc == 3)
          {
            DRCStepSize = cmdScaleCoord(w, argv[2], FALSE, TRUE);
            DRCStepSize *= DRCCurStyle->DRCScaleFactorN;
            DRCStepSize /= DRCCurStyle->DRCScaleFactorD;
            TxPrintf("DRC step size is now %d units)\n",
                        DRCStepSize * DRCCurStyle->DRCScaleFactorD
                        / DRCCurStyle->DRCScaleFactorN);
          }
          else
          {
            TxPrintf("DRC step size is %d units\n",
                  DRCStepSize * DRCCurStyle->DRCScaleFactorD
                  / DRCCurStyle->DRCScaleFactorN);
            if (DRCStepSize != (16 * DRCCurStyle->DRCTechHalo))
                TxPrintf("Recommended step size is %d units\n",
                        (16 * DRCCurStyle->DRCTechHalo) *
                        DRCCurStyle->DRCScaleFactorD /
                        DRCCurStyle->DRCScaleFactorN);
          }
          break;

      case WHY:
          window = ToolGetBoxWindow(&rootArea, (int *) NULL);
          if (window == NULL) return;
          rootUse = (CellUse *) window->w_surfaceID;
          DRCWhy(rootUse, &rootArea);
          break;
    }
    return;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CmdDump --
 *
 *    Implement the ":dump" command.
 *
 * Usage:
 *    dump cellName [child refPointChild] [parent refPointParent]
 *
 * where the refPoints are either a label name, e.g., SOCKET_A, or an x-y
 * pair of integers, e.g., 100 200.  The words "child" and "parent" are
 * keywords, and may be abbreviated.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Copies the contents of a given cell into the edit cell,
 *    so that refPointChild in the child cell (or the lower-left
 *    corner of its bounding box) ends up at location refPointParent
 *    in the edit cell (or the location of the box tool's lower-left).
 *
 * ----------------------------------------------------------------------------
 */

void
CmdDump(w, cmd)
    MagWindow *w;       /* Window in which command was invoked. */
    TxCommand *cmd;           /* Describes command arguments. */
{
    SearchContext scx;
    CellUse dummy;

    if (cmdDumpParseArgs("dump", w, cmd, &dummy, &scx))
      SelectDump(&scx);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cmdDumpParseArgs --
 *
 * Do the real work of the "dump" and "getcell" commands, by reading
 * in the child cell to be used and figuring out the transform implied
 * by the reference point arguments to the command.
 *
 * Results:
 *      TRUE on success; FALSE if arguments were missing or
 *    incorrect, or if the cell couldn't be found.
 *
 * Side effects:
 *    Fills in *dummy so that dummy->cu_def points to the cell
 *    specified by name in cmd->tx_argv[] (see CmdDump() for a
 *    description of the syntax of these args).  Also fills in
 *    *scx so scx_use is dummy, scx_trans is the desired transform
 *    from dummy->cu_def back to root coordinates, and scx_area
 *    is the bounding box of dummy->cu_def.  (Scx is set up
 *    directly for a call to SelectDump()).
 *
 * ----------------------------------------------------------------------------
 */

    /*ARGSUSED*/
bool
cmdDumpParseArgs(cmdName, w, cmd, dummy, scx)
    char *cmdName;      /* Either "dump" or "getcell" */
    MagWindow *w; /* Window in which command was invoked (UNUSED) */
    TxCommand *cmd;     /* Arguments to command */
    CellUse *dummy;     /* Filled in to point to cell mentioned in command */
    SearchContext *scx; /* Filled in with the transform from the child cell's
                   * def to ROOT coordinates, the bounding box of the
                   * child cell in child cell coordinates, and with
                   * scx_use = dummy, where dummy->cu_def is the child
                   * cell itself.
                   */
{
    Point childPoint, editPoint, rootPoint;
    CellDef *def, *rootDef, *editDef;
    bool hasChild, hasRoot, hasTrans;
    Rect rootBox;
    Transform *tx_cell, trans_cell;
    char **av;
    int ac, clen;

    if (cmd->tx_argc < 2)
    {
      TxError("Missing cell name in \"%s\" command.\n", cmdName);
      goto usage;
    }

    if (EditCellUse == NULL)
    {
      TxError("No cell def being edited; cannot place cell use!\n");
      return FALSE;
    }

    /* Locate the cell specified by the command */
    if (CmdIllegalChars(cmd->tx_argv[1], "", "Cell name"))
      return (FALSE);

    /* If the name still has ".mag" attached, then strip it. */
    clen = strlen(cmd->tx_argv[1]);
    if ((clen > 4) && !strcmp(cmd->tx_argv[1] + clen - 4, ".mag"))
      *(cmd->tx_argv[1] + clen - 4) = '\0';

    def = DBCellLookDef(cmd->tx_argv[1]);
    if (def == (CellDef *) NULL)
      def = DBCellNewDef(cmd->tx_argv[1], (char *) NULL);
    editDef = EditCellUse->cu_def;

    /*
     * The following line of code is a bit of a hack.  It's needed to
     * force DBCellRead to print an error message if it can't find the
     * cell.  Otherwise, if the cell wasn't found the last time it was
     * looked for then no new error message will be printed.
     */
    def->cd_flags &= ~CDNOTFOUND;
    if (!DBCellRead(def, (char *) NULL, TRUE))
      return (FALSE);
    DBReComputeBbox(def);
    dummy->cu_def = def;
    dummy->cu_transform = GeoIdentityTransform;
    dummy->cu_expandMask = CU_DESCEND_SPECIAL;
    if (DBIsAncestor(def, EditCellUse->cu_def))
    {
      TxError("The edit cell is already a desecendant of \"%s\",\n",
          cmd->tx_argv[1]);
      TxError("    which means that you're trying to create a circular\n");
      TxError("    structure.  This isn't legal.\n");
      return FALSE;
    }

    /*
     * Parse the remainder of the arguments to find out the reference
     * points in the child cell and the edit cell.  Use the defaults
     * of the lower-left corner of the child cell's bounding box, and
     * the lower-left corner of the box tool, if the respective reference
     * points weren't provided.  (Lower-left of the box tool is interpreted
     * in root coordinates).
     */
    av = &cmd->tx_argv[2];
    ac = cmd->tx_argc - 2;
    hasChild = hasRoot = hasTrans = FALSE;
    while (ac > 0)
    {
      static char *kwdNames[] = { "child", "parent", "90", "180", "270",
                                  "v", "90v", "180v", "270v",
                                  "h", "90h", "180h", "270h", 0 };
      static char *refPointNames[] = { "ll", "lr", "ul", "ur", 0 };
      Label *lab;
      int n,p;

      n = Lookup(av[0], kwdNames);
      if (n < 0)
      {
          TxError("Unrecognized parent/child keyword: \"%s\"\n", av[0]);
          goto usage;
      }
      switch (n)
      {
          case  0:      /* Child */
            if (ac < 2)
            {
                TxError("Keyword must be followed by a reference point\n");
                goto usage;
              }
            if (StrIsInt(av[1]))
            {
                childPoint.p_x = atoi(av[1]);
                if (ac < 3 || !StrIsInt(av[2]))
                {
                  TxError("Must provide two coordinates\n");
                  goto usage;
                }
                childPoint.p_y = atoi(av[2]);
                av += 3;
                ac -= 3;
            }
            else
            {
                p = Lookup(av[1], refPointNames);
                if (p == 0) /* lower left */
                {
                  childPoint.p_x = def->cd_bbox.r_ll.p_x;
                  childPoint.p_y = def->cd_bbox.r_ll.p_y;
                }
                else if (p == 1) /* lower right */
                {
                  childPoint.p_x = def->cd_bbox.r_ur.p_x;
                  childPoint.p_y = def->cd_bbox.r_ll.p_y;
                }
                else if (p == 2) /* upper left */
                {
                  childPoint.p_x = def->cd_bbox.r_ll.p_x;
                  childPoint.p_y = def->cd_bbox.r_ur.p_y;
                }
                else if (p == 3) /* upper right */
                {
                  childPoint.p_x = def->cd_bbox.r_ur.p_x;
                  childPoint.p_y = def->cd_bbox.r_ur.p_y;
                }
                else
                {
                  childPoint = TiPlaneRect.r_ur;
                  (void) DBSrLabelLoc(dummy, av[1], cmdDumpFunc,
                                  &childPoint);
                  if (childPoint.p_x == TiPlaneRect.r_xtop &&
                      childPoint.p_y == TiPlaneRect.r_ytop)
                  {
                      TxError("Couldn't find label \"%s\" in cell \"%s\".\n",
                            av[1], cmd->tx_argv[1]);
                      return FALSE;
                  }
                }
                av += 2;
                ac -= 2;
            }
            hasChild = TRUE;
            break;
          case  1:      /* Parent */
            if (ac < 2)
            {
                TxError("Keyword must be followed by a reference point\n");
                goto usage;
              }
            if (StrIsInt(av[1]))
            {
                editPoint.p_x = atoi(av[1]);
                if (ac < 3 || !StrIsInt(av[2]))
                {
                  TxError("Must provide two coordinates\n");
                  goto usage;
                }
                editPoint.p_y = atoi(av[2]);
                av += 3;
                ac -= 3;
                GeoTransPoint(&EditToRootTransform, &editPoint,
                          &rootPoint);
            }
            else
            {
                p = Lookup(av[1], refPointNames);
                if (p == 0) /* lower left */
                {
                  if (!ToolGetBox(&rootDef, &rootBox) ||
                      (rootDef != EditRootDef)) goto box_error;
                  rootPoint.p_x = rootBox.r_ll.p_x;
                  rootPoint.p_y = rootBox.r_ll.p_y;
                }
                else if (p == 1) /* lower right */
                {
                  if (!ToolGetBox(&rootDef, &rootBox) ||
                      (rootDef != EditRootDef)) goto box_error;
                  rootPoint.p_x = rootBox.r_ur.p_x;
                  rootPoint.p_y = rootBox.r_ll.p_y;
                }
                else if (p == 2) /* upper left */
                {
                  if (!ToolGetBox(&rootDef, &rootBox) ||
                      (rootDef != EditRootDef)) goto box_error;
                  rootPoint.p_x = rootBox.r_ll.p_x;
                  rootPoint.p_y = rootBox.r_ur.p_y;
                }
                else if (p == 3) /* upper right */
                {
                  if (!ToolGetBox(&rootDef, &rootBox) ||
                      (rootDef != EditRootDef)) goto box_error;
                  rootPoint.p_x = rootBox.r_ur.p_x;
                  rootPoint.p_y = rootBox.r_ur.p_y;
                }
                else
                {
                  for (lab = editDef->cd_labels; lab; lab = lab->lab_next)
                      if (strcmp(lab->lab_text, av[1]) == 0)
                        break;

                  if (lab == NULL)
                  {
                      TxError("Couldn't find label \"%s\" in edit cell.\n",
                            av[1]);
                      return FALSE;
                  }
                  editPoint = lab->lab_rect.r_ll;
                  GeoTransPoint(&EditToRootTransform, &editPoint,
                              &rootPoint);
                }
                av += 2;
                ac -= 2;
            }
            hasRoot = TRUE;
            break;
          case  2:      /* 90 */
            tx_cell = &Geo90Transform;
transform_cell:
            if (ac < 2 )
            {
default_action:
                {
                  Rect r;

                  GeoTransRect(tx_cell, &def->cd_bbox, &r);
                  GeoTranslateTrans(tx_cell, def->cd_bbox.r_xbot - r.r_xbot,
                                def->cd_bbox.r_ybot - r.r_ybot,
                                &trans_cell);
                }
                av += 1;
                ac -= 1;
              }
            else if (Lookup(av[1], kwdNames)>=0)
            {
                goto default_action;
            }
            else
            {
                if (StrIsInt(av[1]))
                {
                  editPoint.p_x = atoi(av[1]);
                  if (ac < 3 || !StrIsInt(av[2]))
                  {
                      TxError("Must provide two coordinates\n");
                      goto usage;
                  }
                  editPoint.p_y = atoi(av[2]);
                  av += 3;
                  ac -= 3;
                }
                else
                {
                  p = Lookup(av[1], refPointNames);
                  if (p == 0) /* lower left */
                  {
                      editPoint.p_x = def->cd_bbox.r_ll.p_x;
                      editPoint.p_y = def->cd_bbox.r_ll.p_y;
                  }
                  else if (p == 1) /* lower right */
                  {
                      editPoint.p_x = def->cd_bbox.r_ur.p_x;
                      editPoint.p_y = def->cd_bbox.r_ll.p_y;
                  }
                  else if (p == 2) /* upper left */
                  {
                      editPoint.p_x = def->cd_bbox.r_ll.p_x;
                      editPoint.p_y = def->cd_bbox.r_ur.p_y;
                  }
                  else if (p == 3) /* upper right */
                  {
                      editPoint.p_x = def->cd_bbox.r_ur.p_x;
                      editPoint.p_y = def->cd_bbox.r_ur.p_y;
                  }
                  else
                  {
                      editPoint = TiPlaneRect.r_ur;
                      (void) DBSrLabelLoc(dummy, av[1], cmdDumpFunc,
                                    &editPoint);
                      if (editPoint.p_x == TiPlaneRect.r_xtop &&
                        editPoint.p_y == TiPlaneRect.r_ytop)
                      {
                        TxError("Couldn't find label \"%s\" in cell \"%s\".\n",
                              av[1], cmd->tx_argv[1]);
                        return FALSE;
                      }
                  }
                  av += 2;
                  ac -= 2;
                }
                {
                  Point p;

                  GeoTransPoint(tx_cell, &editPoint, &p);
                  GeoTranslateTrans(tx_cell, editPoint.p_x - p.p_x,
                                editPoint.p_y - p.p_y, &trans_cell);
                }
            }
            hasTrans = TRUE;
            break;
          case  3:      /* 180 */
            tx_cell = &Geo180Transform;
            goto transform_cell;
          case  4:      /* 270 */
            tx_cell = &Geo270Transform;
            goto transform_cell;
          case  5:      /* v */
            tx_cell = &GeoUpsideDownTransform;
            goto transform_cell;
          case  6:      /* 90v */
            tx_cell = &GeoRef45Transform;
            goto transform_cell;
          case  7:      /* 180v */
            tx_cell = &GeoSidewaysTransform;
            goto transform_cell;
          case  8:      /* 270v */
            tx_cell = &GeoRef135Transform;
            goto transform_cell;
          case  9:      /* h */
            tx_cell = &GeoSidewaysTransform;
            goto transform_cell;
          case 10:      /* 90h */
            tx_cell = &GeoRef135Transform;
            goto transform_cell;
          case 11:      /* 180h */
            tx_cell = &GeoUpsideDownTransform;
            goto transform_cell;
          case 12:      /* 270h */
            tx_cell = &GeoRef45Transform;
            goto transform_cell;
      }
    }

    /*
     * Use the default values if explicit reference points weren't
     * provided.
     */
    if (!hasChild)
      childPoint = def->cd_bbox.r_ll;
    if (!hasRoot)
    {
      if (!ToolGetBox(&rootDef, &rootBox))
      {
box_error:
          TxError("The box's lower-left corner must point to the place\n");
          TxError("    in the edit cell where you'd like to put \"%s\".\n",
            cmd->tx_argv[1]);
          return FALSE;
      }
      else if (rootDef != EditRootDef)
      {
          TxError("The box is in cell \"%s\", not in the edit cell \"%s\"\n",
                  rootDef->cd_name, EditRootDef->cd_name);
          return FALSE;
      }
      rootPoint = rootBox.r_ll;
    }
    if (!hasTrans)
    {
      trans_cell = GeoIdentityTransform;
    }

    scx->scx_use = dummy;

    GeoTranslateTrans(&trans_cell, rootPoint.p_x - childPoint.p_x,
          rootPoint.p_y - childPoint.p_y,
          &scx->scx_trans);
    scx->scx_area = def->cd_bbox;
    return TRUE;

usage:
    TxError(
      "Usage: %s cellName [child refPointChild] [parent refPointParent]\n",
      cmdName);
    TxError("       [transform [refPointTrans]],\n");
    TxError("       where the refPoints are either a single label name,\n");
    TxError(
    "       or ll for lower left corner, or lr for lower right corner\n");
    TxError(
    "       or ul for upper left corner, or ur for upper right corner\n");
    TxError(
    "       or a pair of integer coordinates, and the transform is one of\n");
    TxError(
    "       90, 180, 270, v, 90v, 180v, 270v, h, 90h, 180h, 270h.\n");
    return FALSE;
}

/*
 * cmdDumpFunc --
 *
 * Search function used to locate positioning label.  It just computes
 * the lower-left corner of the label and aborts the search.
 *
 * Results:
 *    Always returns 1.
 *
 * Side effects:
 *    Sets *point to the lower-left corner of the label.
 */

    /* ARGSUSED */
int
cmdDumpFunc(rect, name, label, point)
    Rect *rect;               /* Root coordinates of the label. */
    char *name;               /* Label name (not used). */
    Label *label;       /* Pointer to label (not used). */
    Point *point;       /* Place to store label's lower-left. */
{
    *point = rect->r_ll;
    return 1;
}

Generated by  Doxygen 1.6.0   Back to index