/* after years of fiddling.. 'v1.1 August 90 'Jim Butterfield */ /* compile with lc -Lcd -v */ #include #include #include #define BUFFERLINE 50 #define INFOSIZE sizeof(struct FileInfoBlock) #define TRAILLEN 60 /* 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]; struct CityData *CityLink[0x100]; /* The only 1.1 change! */ struct CityData *CountLink[5]; 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(); unsigned short Navigate(struct CityData *,struct CityData *); void main() { long fh, lok; unsigned short NextCol, NewCol; unsigned short AllocCount, errnum; struct FileInfoBlock *fibb; long CitySize, RouteSize; char *CityBlox, *RouteBlox, *DataScan, *Active; char *CityScan, *CityPoint, *RoutePoint; struct CityData *From, *Dest; struct RouteData *NextRoute, *NewRoute, *ScanRoute; unsigned short Cities, Roads; unsigned short CityPSize, RoutePSize; unsigned char Ct0; /* general character work value */ unsigned short Count, Links; 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; for (Ct0=0; Ct0 < 5; Ct0++) CountLink[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,"RoadScan V1.1 .. Jim Butterfield\n"); fprintf(stderr," Release version: 1990 08 13\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 */ printf("Scanning Cities...\n\n"); From=City0; while (FromCityName; while (*(++CityScan) != ','); *CityScan=0; Count=0; NextRoute=From->RoadList; NextCol=From->Colum; while (NextRoute != 0) { Dest=NextRoute->CityNum[1-NextCol]; for (Links=0; LinksDest ) printf("DUPE: %s,%s TO %s\n", Dest->CityName,Dest->StateName,From->CityName); CityLink[Count]=Dest; Count++; NewRoute=NextRoute->RLink[NextCol]; NewCol=NextRoute->Colm[NextCol]; NextRoute=NewRoute; NextCol=NewCol; } if ( Count<5 ) { From->WorkList=CountLink[Count]; CountLink[Count]=From; } From++; } From = CountLink[0]; if ( From !=0 ) { printf("Following Cities have NO Roads !?!...\n"); printf(">>> connect or eliminate them!\n"); while (From != 0) { printf(" %s\n",From->CityName); From = From->WorkList; } } for (Count=1; Count<=3; Count++) { From = CountLink[Count]; if ( From!=0 ) { printf("Following Cities have %d Roads...\n",Count); printf(">>> you could save space if they were not needed!\n"); while (From != 0) { printf(" %s,%s ",From->CityName,From->StateName); Ct0='['; NextRoute=From->RoadList; NextCol=From->Colum; while (NextRoute != 0) { Dest=NextRoute->CityNum[1-NextCol]; printf("%c%s",Ct0,Dest->CityName); NewRoute=NextRoute->RLink[NextCol]; NewCol=NextRoute->Colm[NextCol]; NextRoute=NewRoute; NextCol=NewCol; Ct0='-'; } /* wend, connecting-city list loop */ printf("]\n"); From = From->WorkList; } /* wend, next city for this count-list */ } /* endif count-list not empty */ } /* next count-list */ Ct0=0; for (ScanRoute=Route0; ScanRouteCityNum[0]; Dest=ScanRoute->CityNum[1]; if ( Navigate(From,Dest)>1 ) { if (Ct0==0) printf("... Following DIRECT routes are not shortest!\n"); Ct0=1; printf("%s,%s->%s \n", From->CityName,From->StateName,Dest->CityName); } } /* next Road to analyze */ 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 30 unsigned short 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], LCount[2]; for (CityScan=City0; CityScanWorkList = 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 */ } /* if Search City within target range */ } /* Next Column (Mileage/Time) */ BLink=SearchCity->WorkList; SearchCity->WorkList=0; SearchCity=BLink; } /* if SearchCity not zero, go back */ for (Column=0; Column<2; Column++) { LCount[Column]=0; SearchCity=Start; FLink=SearchCity->NextCity[Column]; while (FLink != 0) { LCount[Column]+=1; Trail[TrailPoint[Column]++][Column]=FLink; SearchCity=FLink; FLink=SearchCity->NextCity[Column]; } /* wend for next FLink */ } /* next Column */ MiniVal=LCount[0]; if (MiniVal