/* after years of fiddling..
  'v1.0  September 89
'Jim Butterfield  */

   /* compile with lc -Lcd -v <filename> */
#include <proto/exec.h>
#include <proto/dos.h>
#include <stdio.h>
#define BUFFERLINE 50
#define INFOSIZE sizeof(struct FileInfoBlock)
#define TRAILLEN 100

/* File Support structures */
struct CityData {
                char *CityName;              /* full name  w/state */
                char *StateName;             /* state/province only */
                struct CityData *CityLink;   /* init letter chain */
                struct CityData *StateLink;  /* init letter chain */
                struct RouteData *RoadList;  /* roads to/from city */
                struct CityData *WorkList;   /* temp job queue */
                struct CityData *NextCity[2];  /* result links */
      /* Mindist[0] doubles as CityDupe flag during file input */
                unsigned short MinDist[2];   /* closest so far */
                unsigned char StateDupe;     /* one name per state */
                unsigned char Colum;         /* goes with RoadList */
                };
struct RouteData {
                 struct CityData *CityNum[2];  /* cities each end */
                 unsigned short Dist[2];       /* distance, time */
                 char *Hiway;                  /* name of Highway */
                 struct RouteData *RLink[2];   /* link for each city */
                 unsigned char Colm[2];        /* goes with RLink */
                 };
struct RouteData *Route0, *Route9;
struct CityData *City0, *City9;
struct CityData *CharLink[0x20];
struct CityData *ChStLink[0x20];
static char InBuff[BUFFERLINE];
static struct CityData *Trail[TRAILLEN][2];
unsigned short TrailPoint[2];
void TimeShow(unsigned short,char *);
int CityMatch(struct CityData *,char *);
unsigned short ParseCities(char *,long);
unsigned short ParseRoutes(char *,long);
struct CityData * AskCity();
void Navigate(struct CityData *,struct CityData *);
void main()
  {
  long fh, lok;
  unsigned short Column, ColStart, ColEnd;
  unsigned short NextCol, NewCol;
  unsigned short AllocCount, errnum;
  unsigned short TrackTrail;
  struct FileInfoBlock *fibb;
  long CitySize, RouteSize;
  char *CityBlox, *RouteBlox, *DataScan, *Active;
  char *StringStart, *FieldStart;
  char *CityPoint, *RoutePoint;
  struct CityData *From, *Dest, *Via, *SearchCity;
  struct CityData *FLink;
  struct RouteData *NextRoute, *NewRoute;
  unsigned short Cities, Roads;
  unsigned short CityPSize, RoutePSize;
  unsigned char Ct0;             /* general character work value */
  static unsigned short Result[2][2];
  static unsigned short tTime, tMiles;
  static char ActCity[]="Cities";
  static char ActRoad[]="Roads";
  for (Ct0=0; Ct0 < 0x20; Ct0++) CharLink[Ct0]= 0;
  for (Ct0=0; Ct0 < 0x20; Ct0++) ChStLink[Ct0]= 0;

  AllocCount=0;
  errnum=2;                   /* no memory */
  if ( (long) (fibb = (struct FileInfoBlock *) AllocMem(INFOSIZE,0L))
                                                                != 0L)
    {
    AllocCount=1;
    errnum=3;                   /* file problems */
    fprintf(stderr,"RoadRoute V1.5  ..   Jim Butterfield\n");
    fprintf(stderr,"   Release version:  1990 04 09\n");
    Cities=0;
    Active=ActCity;
    if ( (lok = Lock("Cities",ACCESS_READ)) != 0L)
      {
      if ( (fh=Examine(lok,fibb)) !=0L)
        {
        CitySize=fibb->fib_Size;
        if ( (long) (CityBlox = AllocMem(CitySize,0L)) != 0L)
	  {
          AllocCount=2;
          if ( (fh = Open("Cities",MODE_OLDFILE)) != 0L)
            {
            errnum=0;
            Read(fh,CityBlox,CitySize);
            Close(fh);
	    }                      /* City file opened   */
          }                        /* data alloc OK */
	}                          /* examine OK    */
        UnLock(lok);
      }                            /* lock OK          */
    }                              /* examine alloc OK */
  if (errnum == 0)
    {
    for (DataScan=CityBlox;DataScan < CityBlox+CitySize;DataScan++)
      if (*DataScan == 0x0A) Cities++;
    CityPSize=Cities*sizeof(struct CityData);
    if ( (long) (CityPoint = AllocMem(CityPSize,0L)) != 0L)
      AllocCount=3;
    else
      errnum=2;
    }
  if (errnum ==0 )
    {
    errnum=3;                   /* file problems */
    Active=ActRoad;
    Roads=0;
    if ( (lok = Lock("Roads",ACCESS_READ)) != 0L)
      {
      if ( (fh=Examine(lok,fibb)) !=0L)
        {
        RouteSize=fibb->fib_Size;
        if ( (long) (RouteBlox = AllocMem(RouteSize,0L)) != 0L)
          {
          AllocCount=4;
          if ( (fh = Open("Roads",MODE_OLDFILE)) != 0L)
            {
            errnum=0;
            Read(fh,RouteBlox,RouteSize);
            Close(fh);
            }                      /* file opened   */
          }                        /* data alloc OK */
        }                          /* examine OK    */
      UnLock(lok);
      }                            /* lock OK          */
    }
  if (errnum == 0)
    {
    for (DataScan=RouteBlox;DataScan < RouteBlox+RouteSize;DataScan++)
      if (*DataScan == 0x0A) Roads++;
    RoutePSize=Roads*sizeof(struct RouteData);
    if ( (long) (RoutePoint = AllocMem(RoutePSize,0L)) != 0L)
      AllocCount=5;
    else
      errnum=2;
    }
  if (errnum == 0)
    {
    Active=ActCity;
    fprintf(stderr,">> %d Cities\n",Cities);
    City0 = (struct CityData *) CityPoint;
    errnum=ParseCities(CityBlox,CitySize);
    }
  if (errnum == 0)
    {
    Active=ActRoad;
    fprintf(stderr,">> %d Roads\n",Roads);
    Route0 = (struct RouteData *) RoutePoint;
    errnum=ParseRoutes(RouteBlox,RouteSize);
    }
  switch (errnum)
    {
    case 0:                /* no errors, do main job */
      Ct0='Y';
      while (Ct0 == 'Y')
        {
        TrailPoint[0]=0;
        TrailPoint[1]=0;
        fprintf(stderr,"City from: ");
        if (gets(InBuff) == 0 || *InBuff =='\0')  From=City0;
                      else      From=AskCity();
        fprintf(stderr,"From %s to: ",From->CityName);
        if (gets(InBuff) == 0)
          {
          InBuff[0]='?';
          InBuff[1]='\0';
          }
        Dest=AskCity();
        fprintf(stderr,"%s->%s via (or RETURN):  ",
                                  From->CityName,Dest->CityName);
        Via=0;
        if (gets(InBuff) != 0 && *InBuff != '\0')
          {
          Via = AskCity();
          fprintf(stderr,"\n%s->%s via %s\n",
	              From->CityName,Dest->CityName,Via->CityName);
          Navigate(From,Via);
          Navigate(Via,Dest);
          }
        else
          {
          fprintf(stderr,"\n%s->%s DIRECT\n",From->CityName,Dest->CityName);
          Navigate(From,Dest);
          }
        for (Column=0; Column<2; Column++)
          {
          Result[0][Column]=0;   /* dist */
          Result[1][Column]=0;   /* time */
          SearchCity=From;
          for (TrackTrail=0; TrackTrail <TrailPoint[Column]; TrackTrail++)
            {
            FLink=Trail[TrackTrail][Column];
            NextRoute=SearchCity->RoadList;
            NextCol=SearchCity->Colum;
            while (NextRoute !=0 &&
                   FLink != NextRoute->CityNum[1-NextCol]) 
              {
              NewRoute=NextRoute->RLink[NextCol];
              NewCol=NextRoute->Colm[NextCol];
              NextRoute=NewRoute;
              NextCol=NewCol;
              }
            Result[1][Column]=Result[1][Column]+NextRoute->Dist[1];
            Result[0][Column]=Result[0][Column]+NextRoute->Dist[0];
            SearchCity=FLink;
            }  /* next Link */
          }    /* next Column */
        ColStart=0;
        ColEnd=1;
        if (Result[0][0]==Result[0][1])
          {
          ColStart=1;
          ColEnd=1;
          }
        if (Result[1][0]==Result[1][1])
          {
          ColStart=0;
          ColEnd=0;
          }
        for (Column=ColStart; Column<=ColEnd; Column++)
          {
          fprintf(stderr,"%4d Miles, time: ",Result[0][Column]);
          TimeShow(Result[1][Column],InBuff);
          fprintf(stderr,"%s\n",InBuff);
          } /* next Column */
        if (ColStart != ColEnd)
          {
          Ct0='B';
          fprintf(stderr,"FASTEST, SHORTEST (or BOTH) ");
          if ((StringStart=gets(InBuff)) != 0) Ct0=*StringStart;
          if (Ct0 > 'Z') Ct0 -=32;
          if (Ct0 =='S') ColEnd=0;
          if (Ct0 =='F') ColStart=1;
          }    /* fastest/shortest question */
        else
          {
          fprintf(stderr,"Press any key to continue\n");
	  gets(InBuff);
          }
        for (Column=ColStart; Column<=ColEnd; Column++)
          {
          printf ("From: %s\nTo: %s\n",From->CityName,Dest->CityName);
          if (Via != 0) printf ("Via: %s\n",Via->CityName);
          tMiles=0;
          tTime=0;
          SearchCity=From;
          for (TrackTrail=0; TrackTrail <TrailPoint[Column]; TrackTrail++)
            {
            FLink=Trail[TrackTrail][Column];
            NextRoute=SearchCity->RoadList;
            NextCol=SearchCity->Colum;
            while (NextRoute > 0 && 
                   FLink!=NextRoute->CityNum[1-NextCol]) 
              {
              NewRoute=NextRoute->RLink[NextCol];
              NewCol=NextRoute->Colm[NextCol];
              NextRoute=NewRoute;
              NextCol=NewCol;
              }   /* wend, no NextRoute */
            tTime=tTime+NextRoute->Dist[1];
            tMiles=tMiles+NextRoute->Dist[0];
            TimeShow(NextRoute->Dist[1],InBuff);
            /*  Print highway link information */
            printf("%4d %s ",NextRoute->Dist[0],InBuff);
            StringStart=InBuff;
            FieldStart=SearchCity->CityName;
            while (*FieldStart != ',')
              *StringStart++ =*FieldStart++;
            *StringStart++ =' ';
            *StringStart++ ='-';
            *StringStart++ =' ';
            FieldStart=FLink->CityName;
            while (*FieldStart != ',')
              *StringStart++ =*FieldStart++;
            *StringStart++ =0;
            printf("%s via %s\n",InBuff,NextRoute->Hiway);
            SearchCity=FLink;
            }  /* next link */
          printf ("     Total Miles: %d\n",tMiles);
          TimeShow(tTime,InBuff);
          printf ("     Driving Time: %s\n",InBuff);
          }  /* next Column */
        Ct0='N';
        fprintf (stderr,"Another trip? ");
        if ((StringStart=gets(InBuff)) != 0) Ct0=*StringStart;
        if (Ct0 > 'Z') Ct0 -=32;
        }           /* another trip */
      break;
    case 1:         /* trouble in file data */
      fprintf(stderr,"----> Correct data in file %s and try again.\n",Active);
      break;
    case 2:         /* AllocMem refused */
        fprintf(stderr,"----> Not enough memory to run RoadRoute.\n");
	break;
    case 3:         /* real trouble with file */
        fprintf(stderr,"----> Can't proceed due to unreadable file %s.\n",Active);
        break;
    default:
      fprintf(stderr,"System error: %d\n",errnum);
    }  /* error switch area */
  switch (AllocCount)
    {
    case 5:  FreeMem(RoutePoint,RoutePSize);
    case 4:  FreeMem( (char *) RouteBlox,RouteSize);
    case 3:  FreeMem(CityPoint,CityPSize);
    case 2:  FreeMem( (char *) CityBlox,CitySize);
    case 1:  FreeMem( (char *) fibb,INFOSIZE);
    }  /* memory cleanup */
  if (errnum != 0)
    {
    fprintf(stderr,"Press any key to quit\n");
    gets(InBuff);
    }	
  }


void TimeShow(Time, Buffer)
  unsigned short Time;
  char *Buffer;
  {
  unsigned short MinFlag, radix, digit, SuppressLimit, FillChar, Hours[2];
  Hours[0]=Time/60;
  Hours[1]=Time%60;
  SuppressLimit=1;
  FillChar=':';
  radix=10000;
  while (radix>Hours[0] && radix>SuppressLimit)
    {
    radix/=10;
    *Buffer++ =' ';
    }
  for (MinFlag=0;MinFlag<2;MinFlag++)
    {
    while (radix>0)
      {
      digit=Hours[MinFlag]/radix;
      Hours[MinFlag]%=radix;
      *Buffer++ =digit+'0';
      radix/=10;
      }  /* more digits */
    *Buffer++ =FillChar;
    FillChar=0;
    SuppressLimit=10;
    radix=10;
    }  /* next MinFlag */
  }

int CityMatch(TabPoint,CityText)
  struct CityData *TabPoint;
  char *CityText;
  {
  /* Name must match exactly; state is optional */
  int Match, StateMatch;
  char TablChar, TextChar;
  char *TableName;
  TableName=TabPoint->CityName;
  TablChar=*TableName++; if (TablChar > 'Z') TablChar-=0x20;
  TextChar=*CityText++; if (TextChar > 'Z') TextChar-=0x20;
  Match=1;
  StateMatch=0;
  while (Match == 1 && TextChar != ',')
    {
    if (TablChar != TextChar)
      {
      if (TablChar ==',' && TextChar =='/') StateMatch=1;
      else Match=0;
      }
    TablChar=*TableName++; if (TablChar > 'Z') TablChar-=0x20;
    TextChar=*CityText++; if (TextChar > 'Z') TextChar-=0x20;
    }
  if (Match==1)
    {
    if (TablChar != 0 && TablChar !=',') Match=0;
    if (TabPoint->MinDist[0]==1 && StateMatch ==0) Match=0;
    }
  return (Match);
  }

unsigned short ParseCities(CitiesBlock,CBlockSize)
  char *CitiesBlock;
  long CBlockSize;
  {
  struct CityData *CityStruct, *CityScan;
  char *LineBegin, *EndLine, *NewName, *OldName;
  unsigned short CityError;
  unsigned char CityIndex,NewChar,OldChar;
  unsigned char FoundFlag, StateFlag;
  CityError=0;
  CityStruct = City0;
  LineBegin = CitiesBlock;
  FoundFlag=0;
  for (EndLine=CitiesBlock;EndLine < CitiesBlock+CBlockSize;EndLine++)
  if (*EndLine == ',' || *EndLine == 0x0A)
    {
    if (*EndLine == ',')
      {
      FoundFlag=1;
      CityStruct->CityName=LineBegin;
      /* MinDist[0] used as temporary dupe CityName flag */
      CityStruct->MinDist[0]=0;          /* No City Duplicate Name */
      CityIndex = *LineBegin & 0x1F;        /* strip first char */
      CityScan = CharLink[CityIndex];
      while (CityScan != 0)
        {
        NewName=LineBegin;
        OldName=CityScan->CityName;
        NewChar=*NewName++; if (NewChar > 0x60) NewChar -= 0x20;
        OldChar=*OldName++; if (OldChar > 0x60) OldChar -= 0x20;
        while (NewChar == OldChar && NewChar != ',')
          {
          NewChar=*NewName++; if (NewChar > 0x60) NewChar -= 0x20;
          OldChar=*OldName++; if (OldChar > 0x60) OldChar -= 0x20;
          }
        if (NewChar == OldChar)     /* Dupe City Name */
          {
          CityStruct->MinDist[0]=1;
          CityScan->MinDist[0]=1;
          }
        CityScan = CityScan->CityLink;
        }   /* chain search for input city name */
       CityStruct->CityLink = CharLink[CityIndex];
      CityStruct->RoadList = 0;
      CharLink[CityIndex]=CityStruct;
      LineBegin=EndLine+1;
      }    /* end of comma..City parsing */
    else     /* NewLine */
      {
      *EndLine=0;
      if (FoundFlag ==0)
        {
        fprintf(stderr,"*FILE Cities: NO COMMA:\n%s\n",LineBegin);
        CityError=1;
        }
      FoundFlag=0;
      CityStruct->StateName=LineBegin;
      CityStruct->StateDupe=1;
      CityIndex = *LineBegin & 0x1F;        /* strip first char */
      CityScan = ChStLink[CityIndex];
      StateFlag = 0;
      while (CityScan != 0 && StateFlag == 0)
        {
        NewName=LineBegin;
        OldName=CityScan->StateName;
        NewChar=*NewName++; if (NewChar > 0x60) NewChar -= 0x20;
        OldChar=*OldName++; if (OldChar > 0x60) OldChar -= 0x20;
        while (NewChar == OldChar && NewChar !=0 && OldChar !=0)
          {
          NewChar=*NewName++; if (NewChar > 0x60) NewChar -= 0x20;
          OldChar=*OldName++; if (OldChar > 0x60) OldChar -= 0x20;
          }
        if (NewChar == OldChar)
          {
          CityScan->StateDupe=0;
          StateFlag=1;
          }
        CityScan = CityScan->StateLink;
        }   /* more states in chain */
      CityStruct->StateLink = ChStLink[CityIndex];
      ChStLink[CityIndex]=CityStruct;
      CityStruct++;
      LineBegin=EndLine+1;
      }    /* end of NewLine parsing */
    }
  City9=CityStruct;
  return(CityError);
  }

unsigned short ParseRoutes(DataBlock,BlockSize)
  char *DataBlock;
  long BlockSize;
  {
  struct RouteData *RouteStruct;
  struct CityData *CityScan, *FoundCity;
  unsigned short ErrorFlag, CityColm, CityFlag, CityValue, Multiplier;
  unsigned char CtNameKey, Digit;
  char *LineStart,*LineEnd,*FieldStart,*FieldEnd,*NumPtr;
  ErrorFlag=0;
  RouteStruct = Route0;
  LineStart = DataBlock;
  for (LineEnd=DataBlock;LineEnd < DataBlock+BlockSize;LineEnd++)
  if (*LineEnd == 0x0A)
    {
    *LineEnd=0;
    FieldStart=LineStart;
    for (CityColm=0 ; ErrorFlag==0 && CityColm < 2 ; CityColm++)
      {
      for (FieldEnd=FieldStart ;
             *FieldEnd !=',' && FieldEnd < LineEnd ; FieldEnd++);
      if (*FieldEnd !=',')
        {
        fprintf(stderr,"*FILE Roads:  MISSING FIELDS:\n%s\n",LineStart);
        ErrorFlag=1;
        }   /* no comma! */
      /* ***** search city name chain */
      CtNameKey = *FieldStart & 0x1F;        /* strip first char */
      CityScan = CharLink[CtNameKey];
      CityValue =0;
      while (CityScan != 0 && CityValue ==0)
        {
        if (CityMatch(CityScan,FieldStart)==1)
          {
          CityValue=1;
          FoundCity=CityScan;
          }
        CityScan = CityScan->CityLink;
        }   /* chain search for input city name */	  
      if (CityValue ==0)
        {
        fprintf(stderr,"*FILE Roads:  CITY NOT FOUND:\n%s\n",LineStart);
        ErrorFlag=1;
        }
      RouteStruct->CityNum[CityColm]=FoundCity;
      RouteStruct->RLink[CityColm]=FoundCity->RoadList;
      RouteStruct->Colm[CityColm]=FoundCity->Colum;
      FoundCity->RoadList=RouteStruct;
      FoundCity->Colum=CityColm;
      FieldStart=FieldEnd+1;
      }   /* two-city loop */
    /* Now grab mileage, time, and Hiway */
    for (CityColm=0 ; ErrorFlag==0 && CityColm < 2 ; CityColm++)
      {
      for (FieldEnd=FieldStart ;
                *FieldEnd !=',' && FieldEnd < LineEnd ; FieldEnd++);
      if (*FieldEnd !=',')
        {
        fprintf(stderr,"FILE Roads:  MISSING FIELDS:\n%s????\n",LineStart);
        ErrorFlag=1;
        }   /* no comma! */
      CityValue=0 ; CityFlag=0;
      Multiplier=10;
      for (NumPtr=FieldStart ; CityFlag==0 && NumPtr < FieldEnd ;
                                                          NumPtr++)
        {
        Digit=*NumPtr;
        if (Digit >='0' && Digit<='9')
          {
          CityValue=CityValue * Multiplier + Digit - '0';
          Multiplier=10;
          }
        else if (Digit ==':') Multiplier = 6;
        }  /* Scan for colon */
      RouteStruct->Dist[CityColm]=CityValue;
      FieldStart=FieldEnd+1;
      }    /* Two value paramemters */
    RouteStruct->Hiway=FieldStart;
    RouteStruct++;
    LineStart=LineEnd+1;
    }    /* end of routefile scan */
  Route9 = RouteStruct;
  return(ErrorFlag);
  }   /* if no error, table build */

#define MENUSIZE 15
struct CityData * AskCity()
  {
  char *InputString, *InputName, *TableName;
  unsigned char MulKey, CityKey, InChar, CityChar, StateFlag;
  struct CityData *CityStruct, *CityPStruct;
  unsigned short MenuPointer, MenuList;
  static struct CityData *Menu[MENUSIZE];

  InputString=InBuff;
  CityKey = *InputString & 0x1F;        /* strip first char */
  CityStruct = CharLink[CityKey];
  MulKey = 0;              /* Single menu */
  CityPStruct = 0;
  MenuPointer = 0;
  while (CityStruct != 0)
    {
    MenuPointer = 0;
    while ( CityStruct !=0 && MenuPointer < MENUSIZE )
      {
      InputName=InputString;
      TableName=CityStruct->CityName;
      InChar=*InputName++; if (InChar > 0x60) InChar -= 0x20;
      CityChar=*TableName++; if (CityChar > 0x60) CityChar -= 0x20;
      while (InChar == CityChar && InChar != 0)
        {
        InChar=*InputName++; if (InChar > 0x60) InChar -= 0x20;
        CityChar=*TableName++; if (CityChar > 0x60) CityChar -= 0x20;
        }
      if (InChar == 0)
        {
        Menu[MenuPointer++]=CityStruct;
        CityPStruct = CityStruct;
        }
      CityStruct = CityStruct->CityLink;
      }   /* chain search for input city name */
    if ( MenuPointer > 1)
      {
      for (MenuList=0;MenuList < MenuPointer;MenuList++)
        fprintf(stderr,"%d: %s\n",MenuList+1,Menu[MenuList]->CityName);
      fprintf(stderr,">>> Pick 1 to %d:",MenuPointer);
      if (CityStruct != 0)
        {
        fprintf(stderr," or RETURN for more:");
        CityStruct = CityPStruct;
	MulKey=1;           /* Multiple menus */
        }
      else if ( MulKey != 0 )
        {
        fprintf(stderr," or RETURN to restart list:");
        CityStruct = CharLink[CityKey]; /* return to first menu */
        }
      StateFlag=0;
      if ((InputString=gets(InBuff)) != 0)
        {
        InChar=*InputString++;
        while (InChar != 0)
          {
          if (InChar >='0' && InChar<='9')
                  StateFlag=StateFlag * 10 + InChar - '0';
          InChar=*InputString++;
          }
        }     /* endif, not RETURN */
      if (StateFlag < 1 || StateFlag > MenuPointer) StateFlag=1;
        else  CityStruct = 0;
      CityPStruct=Menu[StateFlag-1];      
      }  /* endif cities to pick from */
    }   /* while more cities in list */
  switch (MenuPointer)
    {
    case 0:            /* No city match */
      fprintf(stderr,"Province or State: ");
      if ((InputString=gets(InBuff)) == 0) InputString=City0->CityName;
      /* Search for state as input */
      MenuPointer = 0;
      Menu[MenuPointer]=City0;
      CityKey = *InputString & 0x1F;    /* strip state first char */
      CityStruct = ChStLink[CityKey];
      if (CityStruct == 0) CityStruct = City0;
      while (CityStruct != 0)
        {
        if (CityStruct->StateDupe == 1)   /* one flag set per state*/
          {
          InputName=InputString;
          TableName=CityStruct->StateName;
          InChar=*InputName++; if (InChar > 0x60) InChar -= 0x20;
          CityChar=*TableName++; if (CityChar > 0x60) CityChar -= 0x20;
          while (InChar == CityChar && InChar != 0)
            {
            InChar=*InputName++; if (InChar > 0x60) InChar -= 0x20;
            CityChar=*TableName++; if (CityChar > 0x60) CityChar -= 0x20;
            }
          if (InChar == 0)
            if (MenuPointer < MENUSIZE) Menu[MenuPointer++]=CityStruct;
          }
        CityStruct = CityStruct->StateLink;
        }   /* more states in chain */
      if (MenuPointer == 0)    /* search for first letter state */
        {
        CityStruct = ChStLink[CityKey];
        if (CityStruct == 0) CityStruct = City0;
        while (CityStruct != 0)
          {
          if (CityStruct->StateDupe == 1)
            {
            InputName=InputString;
            TableName=CityStruct->StateName;
            InChar=*InputName++; if (InChar > 0x60) InChar -= 0x20;
            CityChar=*TableName++; if (CityChar > 0x60) CityChar -= 0x20;
            if (InChar == CityChar)
              if (MenuPointer < MENUSIZE) Menu[MenuPointer++]=CityStruct;
            }
          CityStruct = CityStruct->StateLink;
          }   /* while more states in alpha chain */
        }  /* endif, single char state search */
      if (MenuPointer > 1)
        {
        for (MenuList=0;MenuList < MenuPointer;MenuList++)
          fprintf(stderr,"%d: %s\n",MenuList+1,Menu[MenuList]->StateName);
        fprintf(stderr,">>> Pick 1 to %d:",MenuPointer);
        StateFlag=0;
        if ((InputString=gets(InBuff)) != 0)
          {
          InChar=*InputString++;
          while (InChar != 0)
            {
            if (InChar >='0' && InChar<='9')
              StateFlag=StateFlag * 10 + InChar - '0';
            InChar=*InputString++;
            }
          if (StateFlag<1 || StateFlag>MenuPointer) StateFlag=1;
          StateFlag -=1;
          }
        CityStruct=Menu[StateFlag];
        }  /* endif, pick state from list */
      if (MenuPointer == 0) CityStruct=City0;
      if (MenuPointer == 1) CityStruct=Menu[0];
      /* Now track cities within a given state */
      InputString=CityStruct->StateName;
      CityKey = *InputString & 0x1F;        /* strip first char */
        /* Track the state alpha list */
      MulKey=0;             /* not multiple menu */
      CityStruct = ChStLink[CityKey];
      CityPStruct = 0;
      MenuPointer = 0;
      while (CityStruct != 0)  /* search cities in a state */
        {
        MenuPointer = 0;
        while ( CityStruct != 0  && MenuPointer < MENUSIZE )
          {
          InputName=InputString;
          TableName=CityStruct->StateName;
          InChar=*InputName++; if (InChar > 0x60) InChar -= 0x20;
          CityChar=*TableName++; if (CityChar > 0x60) CityChar -= 0x20;
          while (InChar == CityChar && InChar != 0)
            {
            InChar=*InputName++; if (InChar > 0x60) InChar -= 0x20;
            CityChar=*TableName++; if (CityChar > 0x60) CityChar -= 0x20;
            }
          if (InChar == CityChar)
	    {
            Menu[MenuPointer++]=CityStruct;
            CityPStruct = CityStruct;
            }
          CityStruct = CityStruct->StateLink;
          }   /* while more cities and space in menu */
        if (MenuPointer > 1)
          {
          for (MenuList=0;MenuList < MenuPointer;MenuList++)
            fprintf(stderr,"%d: %s\n",MenuList+1,Menu[MenuList]->CityName);
          fprintf(stderr,">>> Pick 1 to %d:",MenuPointer);
          if (CityStruct != 0)
            {
            fprintf(stderr," or RETURN for more:");
            MulKey=1;      /* multiple menus */
            CityStruct = CityPStruct;
            }
          else if ( MulKey != 0 )
            {
            fprintf(stderr," or RETURN for to restart list:");
            CityStruct = ChStLink[CityKey]; /* back to first menu */
            }
          StateFlag=0;
          if ((InputName=gets(InBuff)) != 0)
            {
            InChar=*InputName++;
            while (InChar != 0)
              {
              if (InChar >='0' && InChar<='9')
                StateFlag=StateFlag * 10 + InChar - '0';
              InChar=*InputName++;
              }  /* while more numeric string */
            }  /* endif, non-RETURN */
          }              /* endif, pick city within state */
        if (StateFlag < 1 || StateFlag > MenuPointer) StateFlag =1;
          else  CityStruct = 0;
        CityPStruct=Menu[StateFlag-1];
        }     /* while more cities in state list */
      if (MenuPointer == 0) CityPStruct=City0;
      if (MenuPointer == 1) CityPStruct=Menu[0]; 
      break;
    case 1:             /* unique input city match */
      CityPStruct = Menu[0];
    }    /* endswitch, unique or no match */
  return(CityPStruct);
  }

void Navigate(struct CityData *Start,struct CityData *Finish)
  {
  unsigned short Column, Travel;
  unsigned short NextCol, NewCol;
  struct CityData *CityScan;
  struct CityData *BLink, *SearchCity;
  struct CityData *ELink, *FLink, *OtherCity;
  struct RouteData *NextRoute, *NewRoute;
  static unsigned short MiniVal;
  static unsigned short ThisDist[2];
        for (CityScan=City0; CityScan<City9; CityScan++)
          {
          CityScan->WorkList = 0;
          for (Column=0; Column<2; Column++)
            {
            CityScan->MinDist[Column]=9999;
            CityScan->NextCity[Column]=0;
            }
          }
        ELink=Finish;
        MiniVal=0;
        for (Column=0; Column<2; Column++)
          {
          Finish->MinDist[Column]=0;
          ThisDist[Column]=9999;
          }
        SearchCity=Finish;       
        while (SearchCity != 0)
          {
          for (Column=0; Column<2; Column++)
            {
            if (SearchCity->MinDist[Column] < Start->MinDist[Column])
              {
    /*   if (Column==0)
     *   printf("Scanning map at range: %d\n",SearchCity->MinDist[0]); */
            MiniVal=SearchCity->MinDist[Column];
            NextRoute=SearchCity->RoadList;
            NextCol=SearchCity->Colum;
            while (NextRoute != 0)
                {
                OtherCity=NextRoute->CityNum[1-NextCol];
                Travel=MiniVal+NextRoute->Dist[Column];
                if (OtherCity->MinDist[Column] > Travel)
                  /* found a new shortest path */
                  {
                  OtherCity->MinDist[Column]=Travel;
                  OtherCity->NextCity[Column]=SearchCity;
                  if (OtherCity->WorkList == 0 && ELink!=OtherCity)
                    {
                    BLink=SearchCity;
                    FLink=SearchCity->WorkList;
                    while (FLink!=0 && Travel>FLink->MinDist[Column])
                      {
                      BLink=FLink;
                      FLink=BLink->WorkList;
                      }
                    BLink->WorkList=OtherCity;
                    OtherCity->WorkList=FLink;
                    if (FLink==0) ELink=OtherCity;
                    }  /* build new Worklist */
                  }    /* shorter total distance */
                if (Travel < ThisDist[Column]) ThisDist[Column]=Travel; 
                NewRoute=NextRoute->RLink[NextCol];
                NewCol=NextRoute->Colm[NextCol];
                NextRoute=NewRoute;
                NextCol=NewCol;
                }   /* Try another route from SearchCity */
              }     /* City within target range */
            }       /* Next Column */
          BLink=SearchCity->WorkList;
          SearchCity->WorkList=0;
          SearchCity=BLink;
          }  /* if SearchCity not zero, go back */
        for (Column=0; Column<2; Column++)
          {
          SearchCity=Start;
          FLink=SearchCity->NextCity[Column];
          while (FLink != 0)
            {
            if (TrailPoint[Column]<TRAILLEN)
	       Trail[TrailPoint[Column]++][Column]=FLink;
            SearchCity=FLink;
            FLink=SearchCity->NextCity[Column];
            }  /* wend for next FLink */
          }    /* next Column */
  }