/* * irRoute.c -- * * Sets up route and calls maze router to do it. * * ********************************************************************* * * Copyright (C) 1987, 1990 Michael H. Arnold, Walter S. Scott, and * * * the 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-8.0/irouter/irRoute.c,v 1.3 2008/12/11 04:20:08 tim Exp $"; #endif /* not lint */ /* --- Includes --- */ #include #include #include "utils/magic.h" #include "utils/geometry.h" #include "utils/hash.h" #include "tiles/tile.h" #include "database/database.h" #include "drc/drc.h" #include "textio/textio.h" #include "windows/windows.h" #include "utils/main.h" #include "dbwind/dbwind.h" #include "debug/debug.h" #include "utils/undo.h" #include "utils/signals.h" #include "utils/malloc.h" #include "utils/list.h" #include "utils/geofast.h" #include "utils/touchingtypes.h" #include "select/select.h" #include "mzrouter/mzrouter.h" #include "irouter/irouter.h" #include "irouter/irInternal.h" /* C99 compat */ #include "select/select.h" /* --- Routines local to this file that are referenced before they are * defined --- */ List *irChooseEndPtLayers(); /* -------------------- Structures Local to this File -------------------- */ /* clientdata structure passed to filter functions when searching for * start and destination labels. */ typedef struct labelSearchData { Rect lsd_locRect; /* set to labels location by filter func */ char *lsd_name; /* label name to search for */ TileType lsd_type; /* layer that label is attached to */ int lsd_result; /* code giving result of search */ } LabelSearchData; /* result codes for lsd_result above */ #define LSR_NOTFOUND 10 #define LSR_NOTUNIQUE 20 #define LSR_FOUND 30 /* * ---------------------------------------------------------------------------- * * irRoute -- * * Top level procedure for the routing code. Initializes things, and * calls the maze router to make a connection. * * Results: * Passes back the result code of MZRoute() (see mzrouter.h for codes) * * Side effects: * Paint route into editcell. * * ---------------------------------------------------------------------------- */ int irRoute(cmdWindow, startType, argStartPt, argStartLabel, argStartLayers, destType, argDestRect, argDestLabel, argDestLayers) MagWindow *cmdWindow; /* window route command issued to */ int startType; /* how start is specified */ Point *argStartPt; /* location to route from (in edit cell coords) */ char *argStartLabel; /* label to route from */ List *argStartLayers; /* OK route layers to start route on */ int destType; /* how dest is specified */ Rect *argDestRect; /* location to route to (in edit cell coords) */ char *argDestLabel; /* label to route to */ List *argDestLayers; /* OK route layers to end route on */ { CellUse *routeUse; /* Toplevel cell visible during routing */ int expansionMask; /* Subcell expansion modes to use during * routing */ Point startPt; /* start and dest terminals */ List *startLayers = NULL; Rect destRect; List *destLayers = NULL; RoutePath *path = NULL; /* resulting path */ TileType startLayer = TT_SPACE; int mzResult = MZ_NO_ACTION; /* determine routeUse and expansionMask for this route. */ { MagWindow *window = NULL; /* find global reference window */ if(irRouteWid>=0) { window = WindSearchWid(irRouteWid); if (window == NULL) { TxError("Couldn't find route window (%d),", irRouteWid); TxError("using command window as reference.\n"); } } /* if no global reference window, use window command issued from */ if(window == NULL) { window = cmdWindow; } /* If reference window is nil, complain and exit */ if(window==NULL) { TxError("Point to a layout window first.\n"); return mzResult; } /* Set expansion mask to window route cmd issued to. Used * during searches. Subcells are treated as expanded if expanded in * window cmd issued to. */ expansionMask = ((DBWclientRec *)(window->w_clientData))->dbw_bitmask; /* Set routeUse to rootuse of reference window - * everything "visible" in * the reference window is visible * during routing. * But resulting route * is painted into edit cell. This distinction is important only * in the case of a subedit. If the user subedits a cell, the * context of the parent(s) will guide the route, but the route * will be painted into the edit cell. */ routeUse = (CellUse *) (window->w_surfaceID); /* make sure cmd issued from window in which edit cell * is being edited */ if (!EditCellUse || EditRootDef != routeUse->cu_def) { TxError("Nothing being edited in route window.\n"); return mzResult; } } /* initialize mzrouter */ MZInitRoute(irMazeParms, routeUse, expansionMask); /* Figure out start coordinates */ { Point irGetStartPoint(); startPt = irGetStartPoint(startType, argStartPt, argStartLabel, &startLayer, routeUse); /* check for failure */ if(startPt.p_x == MINFINITY) goto abort; } /* Set maze router dest area(s) */ if(destType == DT_SELECTION) /* add destination area for each selected area on an appropriate layer */ { int irSelectedTileFunc(); if(argDestLayers == NULL) /* no layer arg specified, generate dest areas for each active * route layer */ { RouteLayer *rL; for (rL = irRouteLayers; rL != NULL; rL = rL->rl_next) { if(rL->rl_routeType.rt_active) { /* set dest area for each selected tile * of type connecting to rL */ SelEnumPaint( &(DBConnectTbl[rL->rl_routeType.rt_tileType]), FALSE, /* TRUE = restricted to edit cell */ NULL, /* not used */ irSelectedTileFunc, (ClientData *) rL /* type of destarea */ ); } } } else /* generate dest areas for layers that are both specified and active */ { List *l; for(l=argDestLayers; l!=NULL; l=LIST_TAIL(l)) { RouteLayer *rL = (RouteLayer *) LIST_FIRST(l); if(rL->rl_routeType.rt_active) { /* set dest area for each selected tile * of type connecting to rL */ SelEnumPaint( &(DBConnectTbl[rL->rl_routeType.rt_tileType]), FALSE, /* TRUE = restricted to edit cell */ NULL, /* not used */ irSelectedTileFunc, (ClientData *) rL /* type of destarea */ ); } } } } else /* dest is defined by rectangle */ { Rect irGetDestRect(); TileType destLayer = TT_SPACE; destRect = irGetDestRect(destType, argDestRect, argDestLabel, &destLayer, routeUse); /* check for failure */ if(destRect.r_xtop == MINFINITY) goto abort; /* set a maze router dest area for extent of destRect * on each permitted dest layer. */ if (destLayer != TT_SPACE) { RouteLayer *rL; /* layer type returned by irGetDestRect */ for(rL = irRouteLayers; rL != NULL; rL = rL->rl_next) { if (rL->rl_routeType.rt_active && TTMaskHasType(&(DBConnectTbl[destLayer]), rL->rl_routeType.rt_tileType)) { MZAddDest(&destRect, rL->rl_routeType.rt_tileType); break; } } } else if (argDestLayers == NULL) { /* no layer arg specified, permit all active route layers */ RouteLayer *rL; for (rL = irRouteLayers; rL != NULL; rL = rL->rl_next) { if(rL->rl_routeType.rt_active) { MZAddDest(&destRect,rL->rl_routeType.rt_tileType); } } } else /* permit only layers that are both specified and active */ { List *l; for(l=argDestLayers; l!=NULL; l=LIST_TAIL(l)) { RouteLayer *rL = (RouteLayer *) LIST_FIRST(l); if(rL->rl_routeType.rt_active) { MZAddDest(&destRect,rL->rl_routeType.rt_tileType); } } } } if (startLayer != TT_SPACE) { RouteLayer *rL; /* layer type returned by irGetStartPoint */ for (rL = irRouteLayers; rL != NULL; rL = rL->rl_next) { if (rL->rl_routeType.rt_active && TTMaskHasType(&(DBConnectTbl[startLayer]), rL->rl_routeType.rt_tileType)) { MZAddStart(&startPt, rL->rl_routeType.rt_tileType); break; } } } else { /* Determine OK start layers */ List *l; startLayers = irChooseEndPtLayers( routeUse, expansionMask, &startPt, argStartLayers, "start"); if(SigInterruptPending) goto abort; if(DebugIsSet(irDebugID,irDebEndPts)) { TxPrintf("----- startLayers:\n"); MZPrintRLListNames(startLayers); } /* Set maze router start point(s) - one for each ok layer */ for(l=startLayers; l!=NULL;l=LIST_TAIL(l)) { RouteLayer *rL = (RouteLayer *)LIST_FIRST(l); TileType type = rL->rl_routeType.rt_tileType; MZAddStart(&startPt, type); } } /* Do the Route */ path = MZRoute(&mzResult); /* If MZRoute is interrupted it returns best path * found so far. */ if(SigInterruptPending) { if(path==NULL) { goto abort; } else { TxError("Search Interrupted!\n"); TxPrintf("Using best path found prior to interrupt.\n"); /* Clear interrupt to allow paint back of path */ SigInterruptPending = FALSE; } } /* paint route back into edit cell */ if (path) { RouteLayer *finalRL = path->rp_rLayer; CellUse *resultUse; /* Have MazeRouter paint path into resultCell */ resultUse = MZPaintPath(path); if(SigInterruptPending) goto abort; /* Copy to edit cell transforming from root to edit * coords. * Also select the entire route. * This paint job is undoable. */ { SearchContext scx; scx.scx_use = resultUse; scx.scx_area = resultUse->cu_def->cd_bbox; scx.scx_trans = RootToEditTransform; (void) DBCellCopyPaint(&scx, &DBAllButSpaceBits, 0, EditCellUse); DBReComputeBbox(EditCellUse->cu_def); } /* Select the route */ { SearchContext scx; /* Clear selection, and set selection display for reference * window and other windows containing routeUse as root. */ SelectClear(); if(SelectRootDef != routeUse->cu_def) { SelectRootDef = routeUse->cu_def; SelSetDisplay(SelectUse, SelectRootDef); } /* Copy route to selection cell, notifying undo of change */ scx.scx_use = resultUse; scx.scx_area = resultUse->cu_def->cd_bbox; scx.scx_trans = GeoIdentityTransform; SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL); (void) DBCellCopyPaint(&scx, &DBAllButSpaceBits, 0, SelectUse); SelRememberForUndo(FALSE, SelectRootDef, &(scx.scx_area)); /* Setup redisplay */ DBReComputeBbox(SelectDef); DBWHLRedraw(SelectRootDef, &(scx.scx_area), TRUE); DBWAreaChanged(SelectDef, &SelectDef->cd_bbox, DBW_ALLWINDOWS, &DBAllButSpaceBits); } /* Notify dbwind module (for redisplay), and DRC module * of changed area */ { Rect changedArea; GeoTransRect( &RootToEditTransform, &(resultUse->cu_def->cd_bbox), &changedArea); DBWAreaChanged(EditCellUse->cu_def, &changedArea, DBW_ALLWINDOWS, &DBAllButSpaceBits); DRCCheckThis(EditCellUse->cu_def, TT_CHECKPAINT, &changedArea); } /* Make sure we got here without interruption */ if(SigInterruptPending) goto abort; TxPrintf("Done Routing.\n"); TxFlushOut(); } else { TxError("Route Failed.\n"); } abort: if(SigInterruptPending) { TxError("Route Interrupted!\n"); } ListDealloc(startLayers); ListDealloc(destLayers); /* reclaim storage used by mzrouter */ if(!DebugIsSet(irDebugID, irDebNoClean)) { MZClean(); } return mzResult; } /* * --------------------------------------------------------------------- * * irGetStartPoint -- * * Compute start point. * * Results: * Returns point. * * Side effects: * Returns start layer type in startLayerPtr. * * --------------------------------------------------------------------- */ Point irGetStartPoint(startType, argStartPt, argStartLabel, startLayerPtr, routeUse) int startType; /* how start is specified */ Point *argStartPt; /* location to route from * (in edit cell coords) */ char *argStartLabel; /* label to route from */ TileType *startLayerPtr; /* layer type (returned value) */ CellUse *routeUse; /* toplevel cell visible to router */ { Point startPt; switch (startType) { case ST_POINT: /* start point coords given */ { /* convert from edit to routeUse coords (= root coordinates) */ GeoTransPoint(&EditToRootTransform,argStartPt,&startPt); } break; case ST_CURSOR: /* use cursor */ { MagWindow *pointWindow; pointWindow = ToolGetPoint(&startPt, (Rect *) NULL); if (pointWindow == NULL) { TxError("Can not use cursor as start:"); TxError(" cursor not in layout window.\n"); goto abort; } if (routeUse->cu_def != ((CellUse *)pointWindow->w_surfaceID)->cu_def) { TxError("Can not use cursor as start:"); TxError("cursor not in routecell.\n"); goto abort; } } break; case ST_LABEL: /* label name given */ { int irSelLabelsFunc(); int irAllLabelsFunc(); LabelSearchData lSD; lSD.lsd_name = argStartLabel; /* name to match */ lSD.lsd_result = LSR_NOTFOUND; /* first search selection */ (void) SelEnumLabels(&DBAllTypeBits, FALSE, /* TRUE = search only edit cell */ (bool *) NULL, irSelLabelsFunc, (ClientData) &lSD); if(SigInterruptPending) goto abort; if (lSD.lsd_result == LSR_NOTUNIQUE) { TxError("Warning: Start label '%s' not unique.\n", argStartLabel); } else if (lSD.lsd_result == LSR_NOTFOUND) /* No selected label matched, so search all labels */ { (void) DBSrLabelLoc(routeUse, argStartLabel, irAllLabelsFunc, (ClientData) &lSD); if(SigInterruptPending) goto abort; if (lSD.lsd_result == LSR_NOTUNIQUE) { TxError("Warning: Start label '%s' not unique.\n", argStartLabel); } else if (lSD.lsd_result == LSR_NOTFOUND) { TxError("Start label '%s' not found.\n", argStartLabel); goto abort; } } startPt = lSD.lsd_locRect.r_ll; if (startLayerPtr) *startLayerPtr = lSD.lsd_type; } break; default: /* shouldn't happen */ { ASSERT(FALSE,"irGetStartPoint"); goto abort; } break; } return startPt; abort: startPt.p_x = MINFINITY; startPt.p_y = MINFINITY; return startPt; } /* * --------------------------------------------------------------------- * * irGetDestRect -- * * Compute destination rectangle. * * Results: * Returns rect. * * Side effects: * Returns layer type in destLayerPtr. * * --------------------------------------------------------------------- */ Rect irGetDestRect(destType, argDestRect, argDestLabel, destLayerPtr, routeUse) int destType; /* how dest is specified */ Rect *argDestRect; /* location to route to * (in edit cell coords) */ char *argDestLabel; /* label to route to */ TileType *destLayerPtr; /* layer type (returned value) */ CellUse *routeUse; /* toplevel cell visible to router */ { Rect destRect; switch (destType) { case DT_RECT: /* dest rect coords given */ { /* convert from edit to routeUse coords (= root coordinates) */ GeoTransRect(&EditToRootTransform,argDestRect,&destRect); } break; case DT_LABEL: /* dest rect given as label */ { int irSelLabelsFunc(); int irAllLabelsFunc(); LabelSearchData lSD; lSD.lsd_name = argDestLabel; /* name to match */ lSD.lsd_result = LSR_NOTFOUND; /* first search selection */ (void) SelEnumLabels(&DBAllTypeBits, FALSE, /* TRUE = search only edit cell */ (bool *) NULL, irSelLabelsFunc, (ClientData) &lSD); if(SigInterruptPending) goto abort; if (lSD.lsd_result == LSR_NOTUNIQUE) { TxError("Warning: Destination label '%s' not unique.\n", argDestLabel); } else if (lSD.lsd_result == LSR_NOTFOUND) { /* No selected label matched, so search all labels */ (void) DBSrLabelLoc(routeUse, argDestLabel, irAllLabelsFunc, (ClientData) &lSD); if(SigInterruptPending) goto abort; if (lSD.lsd_result == LSR_NOTUNIQUE) { TxError("Warning: Destination label '%s' not unique.\n", argDestLabel); } else if (lSD.lsd_result == LSR_NOTFOUND) { TxError("Destination label '%s' not found.\n", argDestLabel); goto abort; } } destRect = lSD.lsd_locRect; if (destLayerPtr) *destLayerPtr = lSD.lsd_type; } break; case DT_BOX: /* use box as dest rect */ { CellDef *boxDef; Rect box; if(!ToolGetBox(&boxDef,&box)) { TxError("Can not use box for dest: No Box.\n"); goto abort; } if (boxDef != routeUse->cu_def) { TxError("Can not use box for dest: "); TxError("box not in route cell.\n"); goto abort; } destRect = box; } break; default: /* shouldn't happen */ { ASSERT(FALSE,"irGetDestRect"); goto abort; } break; } return destRect; abort: destRect.r_xbot = MINFINITY; destRect.r_ybot = MINFINITY; destRect.r_xtop = MINFINITY; destRect.r_ytop = MINFINITY; return destRect; } /* * --------------------------------------------------------------------- * * irSelLabelsFunc -- * * Called by SelEnumLabels on behalf of irRoute above, to find selected * label of given name. * * Results: * Returns 0 on first match, 1 on second match (to terminate search) * * Side effects: * Sets locRect in clientdata arg location off matching label. * * --------------------------------------------------------------------- */ int irSelLabelsFunc(label, cellUse, transform, clientData) Label *label; CellUse *cellUse; Transform *transform; ClientData clientData; { LabelSearchData *lsd = (LabelSearchData *)clientData; CellDef *cellDef = cellUse->cu_def; if (strcmp(lsd->lsd_name, label->lab_text) != 0) { /* this label doesn't match, continue search */ return 0; } else if (lsd->lsd_result == LSR_FOUND) { /* second match, set result and terminate search */ lsd->lsd_result = LSR_NOTUNIQUE; return 1; } else { /* first match, set location, result, and continue search */ GeoTransRect(transform, &(label->lab_rect), &(lsd->lsd_locRect)); lsd->lsd_result = LSR_FOUND; lsd->lsd_type = label->lab_type; return 0; } } /* * --------------------------------------------------------------------- * * irAllLabelsFunc -- * * Called by DBSrLabelLoc on behalf of irRoute above, to convert labelName * to location of label with matching name. * * Results: * Returns 0 on first match, 1 on second match (to terminate search) * * Side effects: * Sets locRect in clientdata arg to location of matching label. * * --------------------------------------------------------------------- */ int irAllLabelsFunc(rect, name, label, clientData) Rect *rect; char *name; Label *label; ClientData clientData; { LabelSearchData *lsd = (LabelSearchData *)clientData; if (lsd->lsd_result == LSR_FOUND) { if (GEO_SAMERECT(lsd->lsd_locRect, *rect)) return 0; /* second match, so set result and terminate search */ lsd->lsd_result = LSR_NOTUNIQUE; return 1; } else { /* first match, so set location, result, and continue search */ lsd->lsd_locRect = *rect; lsd->lsd_type = label->lab_type; lsd->lsd_result = LSR_FOUND; return 0; } } /* * --------------------------------------------------------------------- * * irSelectedTileFunc -- * * Called by SelEnumPaint on behalf of irRoute above, to process tile * associated with selection rect. * * Results: * Always returns 0 to continue search. * * Side effects: * Call MzAddDest on selected area. * * --------------------------------------------------------------------- */ int irSelectedTileFunc(rect, type, c) Rect *rect; TileType type; ClientData c; { RouteLayer *rL = (RouteLayer *) c; MZAddDest(rect, rL->rl_routeType.rt_tileType); /* return 0 to continue search */ return 0; } /* * ---------------------------------------------------------------------------- * * LayerInTouchingContact -- * * A Predicate. Checks whether the given routeLayer is a component of a * contact type in touchingTypes. Used by irChooseEndPtLayers below. * * Results: * TRUE if the RouteLayer is a component of a contact type in * touchingTypes, else FALSE. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ bool LayerInTouchingContact(rL,touchingTypes) RouteLayer *rL; TileTypeBitMask touchingTypes; { RouteContact *rC; for(rC=irRouteContacts; rC!=NULL; rC=rC->rc_next) { if(TTMaskHasType(&touchingTypes,rC->rc_routeType.rt_tileType) && (rC->rc_rLayer1==rL || rC->rc_rLayer2==rL)) return(TRUE); } return(FALSE); } /* * ---------------------------------------------------------------------------- * * irChooseEndPtLayers -- * * Figure out what layers are ok at an endpoint - if ambiguous ask user. * * Results: * List of ok layers at endpoint. * * Side effects: * May query user about intended layers at an endpoint. * * ---------------------------------------------------------------------------- */ List * irChooseEndPtLayers(routeUse,expansionMask,endPt,argLayers,endPtName) CellUse *routeUse; int expansionMask; /* mask of expanded subcells */ Point *endPt; List *argLayers; char *endPtName; { List *activeLayers; List *presentLayers; List *presentContacts; List *presentContactLayers; static const char * const actionNames[] = { "no", "yes", 0 }; List *l; RouteLayer *rL; if (DebugIsSet(irDebugID, irDebEndPts)) { TxPrintf("----- argLayers:\n"); MZPrintRLListNames(argLayers); } /* find activeLayers among argLayers (or irRouteLayers if argLayers NULL) */ activeLayers = NULL; if(argLayers) { /* layers given in arg, search these */ for(l=argLayers; l!=NULL; l=LIST_TAIL(l)) { rL = (RouteLayer*) LIST_FIRST(l); if(rL->rl_routeType.rt_active) { LIST_ADD(rL,activeLayers); } } } else { /* no layers given as arg, so search all route layers */ for(rL=irRouteLayers; rL!=NULL; rL=rL->rl_next) if(rL->rl_routeType.rt_active) { LIST_ADD(rL,activeLayers); } } if (DebugIsSet(irDebugID, irDebEndPts)) { TxPrintf("----- activeLayers:\n"); MZPrintRLListNames(activeLayers); } /* make lists of contacts (connecting two active layers) and * active layers which are present at the end point. */ { TileTypeBitMask touchingTypes; RouteContact *rC; touchingTypes = TouchingTypes(routeUse, expansionMask, endPt); /* Make list of present and active contacts */ presentContacts = NULL; presentContactLayers = NULL; for(rC =irRouteContacts; rC!=NULL; rC=rC->rc_next) { if(TTMaskHasType(&touchingTypes,rC->rc_routeType.rt_tileType) && ListContainsP(rC->rc_rLayer1, activeLayers) && ListContainsP(rC->rc_rLayer2, activeLayers)) { LIST_ADD(rC, presentContacts); LIST_ADD(rC->rc_rLayer1, presentContactLayers); LIST_ADD(rC->rc_rLayer2, presentContactLayers); } } if (DebugIsSet(irDebugID, irDebEndPts)) { TxPrintf("----- presentContacts:\n"); MZPrintRCListNames(presentContacts); TxPrintf("----- presentContactLayers:\n"); MZPrintRLListNames(presentContactLayers); } /* make list of present layers that are not constituents of contacts * above. If a contact is touching the endpt but one of * its constituent layers in not active, the other constituent * layer is treated as a presentLayer. */ presentLayers = NULL; for(l=activeLayers; l!=NULL; l=LIST_TAIL(l)) { rL = (RouteLayer *) LIST_FIRST(l); if((TTMaskHasType(&touchingTypes,rL->rl_routeType.rt_tileType) || LayerInTouchingContact(rL,touchingTypes)) && !ListContainsP(rL, presentContactLayers)) { LIST_ADD(rL,presentLayers); } } if (DebugIsSet(irDebugID, irDebEndPts)) { TxPrintf("----- presentLayers:\n"); MZPrintRLListNames(presentLayers); } } /* return appropriate layer list. */ { int numContacts, numLayers; numContacts = ListLength(presentContacts); numLayers = ListLength(presentLayers); if(numLayers == 0 && numContacts == 0) { /* No Layers present at endpt, return list of all active layers */ ListDealloc(presentLayers); ListDealloc(presentContacts); ListDealloc(presentContactLayers); return(activeLayers); } else if(numLayers == 1 && numContacts == 0) { /* Exactly one layer is both active and present, return list * containing only this layer. */ ListDealloc(activeLayers); ListDealloc(presentContacts); ListDealloc(presentContactLayers); return(presentLayers); } else if(numLayers == 0 && numContacts == 1) { /* Just one active contact under endpoint, * return layers connecting to that contact. */ List *l; RouteContact *rC; rC = (RouteContact *) LIST_FIRST(presentContacts); l = (List *) NULL; LIST_ADD(rC->rc_rLayer1,l); LIST_ADD(rC->rc_rLayer2,l); ListDealloc(activeLayers); ListDealloc(presentLayers); ListDealloc(presentContacts); ListDealloc(presentContactLayers); return(l); } else { /* Multiple nodes active and present, ask user which one * he wants to route to. */ char answer[100]; RouteLayer *rL; RouteLayer *pickedRL; RouteContact *rC; RouteContact *pickedRC; TxPrintf("Multiple nodes present at %s point:", endPtName); for(l=presentContacts; l!=NULL; l=LIST_TAIL(l)) { rC=(RouteContact *) LIST_FIRST(l); TxPrintf(" %s", DBTypeLongNameTbl[rC->rc_routeType.rt_tileType]); } for(l=presentLayers; l!=NULL; l=LIST_TAIL(l)) { rL=(RouteLayer *) LIST_FIRST(l); TxPrintf(" %s", DBTypeLongNameTbl[rL->rl_routeType.rt_tileType]); } TxPrintf("\n"); for(pickedRC=NULL,l=presentContacts; l && !pickedRC; l=LIST_TAIL(l)) { rC = (RouteContact *) LIST_FIRST(l); if (!LIST_TAIL(l) && !presentLayers) { /* last choice, so take it */ pickedRC = rC; } else { /* ask user */ TxPrintf("Connect to %s? [yes] ", DBTypeLongNameTbl[rC->rc_routeType.rt_tileType]); if (TxGetLine(answer, sizeof answer) == NULL || answer[0] == '\0') (void) strcpy(answer,"yes"); if(Lookup(answer, actionNames) == 1) { /* Yes */ pickedRC = rC; } } } if(pickedRC) { List *l; l=NULL; LIST_ADD(rC->rc_rLayer1,l); LIST_ADD(rC->rc_rLayer2,l); ListDealloc(activeLayers); ListDealloc(presentLayers); ListDealloc(presentContacts); ListDealloc(presentContactLayers); return(l); } for(pickedRL=NULL,l=presentLayers; l && !pickedRL; l=LIST_TAIL(l)) { rL = (RouteLayer *) LIST_FIRST(l); if(!LIST_TAIL(l)) { /* Last choice so choose it automatically */ pickedRL=rL; } else { /* Ask user */ TxPrintf("Connect to %s? [yes] ", DBTypeLongNameTbl[rL->rl_routeType.rt_tileType]); if (TxGetLine(answer, sizeof answer) == NULL || answer[0] == '\0') (void) strcpy(answer,"yes"); if(Lookup(answer, actionNames) == 1) { /* Yes */ pickedRL = rL; } } } if(pickedRL) { l=NULL; LIST_ADD(rL,l); ListDealloc(activeLayers); ListDealloc(presentLayers); ListDealloc(presentContacts); ListDealloc(presentContactLayers); return(l); } /* User didn't pick anything, return null list */ { ListDealloc(activeLayers); ListDealloc(presentLayers); ListDealloc(presentContacts); ListDealloc(presentContactLayers); return(NULL); } } } }