home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / CPM / TURBOPAS / TP / UTL3 / INDEXBPP.PZS / INDEXBPP.PAS
Pascal/Delphi Source File  |  2000-06-30  |  16KB  |  590 lines

  1. {$V-}
  2. program FileIndex;
  3.  
  4. const
  5.   IndexMax          = 1000;
  6.   RecCountErr       =   -2;
  7.   NewFileCreated    =   -1;
  8.   NoError           =    0;
  9.   RecordNotFound    =    1;
  10.   NoMoreRoom        =    2;
  11.   AlreadyExists     =    3;
  12.   OutOfRange        =    4;
  13.  
  14. type
  15.  
  16.   Keytype      = string[40];
  17.   FileStr      = string[80];
  18.   Whatever     = string[12];
  19.  
  20.   DataRec = record
  21.     case Boolean of
  22.       True     : (NumRecs     : Integer);
  23.       False    : (Key         : Keytype;
  24.                   theRest     : Whatever);
  25.   end;
  26.  
  27.   IndexRec = record
  28.     Key        : Keytype;
  29.     Num        : Integer
  30.   end;
  31.  
  32.   IndexList    = array[1..IndexMax] of IndexRec;
  33.  
  34. var
  35.   KList        : IndexList;
  36.   DFile        : file of DataRec;
  37.   MaxRec       : Integer;
  38.  
  39. {            compiler-specific file I/O routines                  }
  40. {      these procedures are specific to TURBO Pascal.  If you
  41.        are using another Pascal compiler, you will need to
  42.        modify them appropriately.  Note that TURBO Pascal does
  43.        not support the standard routines GET and PUT, but instead
  44.        uses READ and WRITE.                                       }
  45.  
  46. {$I-} { turn off I/O error checking }
  47.  
  48. procedure FRead(RNum : Integer; var Rec : DataRec; var Error : Integer);
  49. {
  50.      reads record #RNum into Rec
  51. }
  52. begin
  53.   if (RNum < 0) or (RNum > MaxRec)
  54.     then Error := OutOfRange
  55.   else begin
  56.     Seek(DFile,RNum);
  57.     Read(DFile,Rec);
  58.     Error := IOResult;
  59.     if Error > 0
  60.       then Error := 100 + Error
  61.   end
  62. end; { of proc FRead }
  63.  
  64. procedure FWrite(RNum : Integer; Rec : DataRec; var Error : Integer);
  65. {
  66.      writes record #RNum into Rec
  67. }
  68. begin
  69.   if (RNum < 0) or (RNum > MaxRec)
  70.     then Error := OutOfRange
  71.   else begin
  72.     Seek(DFile,RNum);
  73.     Write(DFile,Rec);
  74.     Error := IOResult;
  75.     if Error > 0
  76.       then Error := 100 + Error
  77.   end
  78. end; { of proc FRead }
  79.  
  80. procedure FOpen(FileName : FileStr; var Error : Integer);
  81. {
  82.        tries to open FileName; if it doesn't exist, creates
  83.        it with the appropriate header record
  84. }
  85. const
  86.   TurboNoFile  =  1; { "no file" error code for TURBO Pascal }
  87.   NoIOError    =  0;
  88. var
  89.   IOCode       : Integer;
  90.   TRec         : DataRec;
  91. begin
  92.   Assign(DFile,FileName);
  93.   Reset(DFile);
  94.   IOCode := IOResult;
  95.   if IOCode = TurboNoFile then begin { file doesn't exist }
  96.     FillChar(TRec,SizeOf(TRec),0);
  97.     Rewrite(DFile);
  98.     TRec.NumRecs := 0;
  99.     Write(DFile,TRec);
  100.     Close(DFile);
  101.     Assign(DFile,Filename);
  102.     Reset(DFile);
  103.     IOCode := IOResult;
  104.     if IOCode = NoIOError
  105.       then Error := NewFileCreated
  106.   end;
  107.   if IOCode <> NoIOError
  108.     then Error := 100 + IOCode;
  109. end; { of proc FOpen }
  110.  
  111. procedure FClose(var Error : Integer);
  112. {
  113.        closes file
  114. }
  115. begin
  116.   Close(DFile);
  117.   Error := IOResult;
  118.   if Error > 0
  119.     then Error := Error + 100
  120. end; { of proc FClose }
  121.  
  122. {$I+} { turn on I/O error checking }
  123.  
  124. {               initialization and cleanup routines                  }
  125.  
  126. procedure SortIndexList;
  127. {
  128.      sorts the array KList using a selection sort technique
  129. }
  130. var
  131.   I,J,Min      : Integer;
  132.   Temp         : IndexRec;
  133. begin
  134.   for I := 1 to MaxRec-1 do begin
  135.     Min := I;
  136.     for J := I+1 to MaxRec do
  137.       if KList[J].Key < KList[Min].Key
  138.         then Min := J;
  139.     Temp := KList[I];
  140.     KList[I] := KList[Min];
  141.     KList[Min] := Temp
  142.   end
  143. end; { of proc SortIndexList }
  144.  
  145. procedure InitStuff(FileName : FileStr; var Error : Integer);
  146. {
  147.      sets everything up for indexing system.  This assumes that
  148.      there are no more than IndexMax (=1000) records, and that the
  149.      records are numbered 1..IndexMax.  Record #0 is the header
  150.      record and is used to store the current number of records
  151.      actively being used in the file
  152. }
  153. var
  154.   Indx,TErr           : Integer;
  155.   TRec                : DataRec;
  156. begin
  157.   Error := NoError;
  158.   FOpen(FileName,Error);
  159.   if Error <= NoError then begin
  160.     MaxRec := 0;
  161.     FRead(0,TRec,TErr);
  162.     Error := TErr;
  163.     MaxRec := TRec.NumRecs;
  164.     for Indx := 1 to MaxRec do begin
  165.       FRead(Indx,TRec,TErr);
  166.       if TErr > 0
  167.         then Error := TErr;
  168.       KList[Indx].Key := TRec.Key;
  169.       KList[Indx].Num := Indx
  170.     end;
  171.     SortIndexList
  172.   end
  173. end; { of proc InitStuff }
  174.  
  175. procedure CleanUpStuff(var Error : Integer);
  176. {
  177.      this just does an orderly shutdown and should be called
  178.      before you leave your program (or open another data file)
  179. }
  180. var
  181.   TRec                : DataRec;
  182. begin
  183.   TRec.NumRecs := MaxRec; { save out # of records }
  184.   FWrite(0,TRec,Error);
  185.   FClose(Error)
  186. end; { of proc CleanUpStuff }
  187.  
  188. function FindKey(Key : Keytype) : Integer;
  189. {
  190.      looks for Key in KList; returns location in KList
  191.      if found; otherwise returns - 1
  192. }
  193. var
  194.   L,R,Mid      : Integer;
  195. begin
  196.   L := 1; R := MaxRec;
  197.   repeat
  198.     Mid := (L+R) div 2;
  199.     if Key < KList[Mid].Key
  200.       then R := Mid-1
  201.       else L := Mid+1
  202.   until (Key = KList[Mid].Key) or (L > R);
  203.   if Key = KList[Mid].Key
  204.     then FindKey := Mid
  205.     else FindKey :=  -1
  206. end; { of proc FindKey }
  207.  
  208. procedure GetRecord(Key : Keytype; var Rec : DataRec;
  209.                     var Error : Integer);
  210. {
  211.      looks through KList for Key; if found, returns in Rec.
  212.      It and the routines that follow assume the procedure Seek
  213.      for random access of the file of records.
  214. }
  215. var
  216.   Item                : Integer;
  217. begin
  218.   Error := NoError;
  219.   Item := FindKey(Key);
  220.   if Item > 0
  221.     then FRead(KList[Item].Num,Rec,Error)
  222.     else Error := RecordNotFound
  223. end; { of proc GetRecord }
  224.  
  225. procedure PutRecord(Rec : DataRec; var Error : Integer);
  226. {
  227.      writes Rec out to the file.  If a record with that
  228.      key already exists, then overwrites that record;
  229.      otherwise, adds the record to the end of the file.
  230.      If there's no more room for records, exits with an
  231.      error code
  232. }
  233. var
  234.   Item         : Integer;
  235. begin
  236.   Error := NoError;
  237.   Item := FindKey(Rec.Key);
  238.   if Item >= 0
  239.     then FWrite(KList[Item].Num,Rec,Error)
  240.   else if MaxRec < IndexMax then begin
  241.     MaxRec := MaxRec + 1;
  242.     FWrite(MaxRec,Rec,Error);
  243.     KList[MaxRec].Key := Rec.Key;
  244.     KList[MaxRec].Num := MaxRec;
  245.     SortIndexList
  246.   end
  247.   else Error := NoMoreRoom
  248. end; { of proc PutRecord }
  249.  
  250. procedure AddRecord(Rec : DataRec; var Error : Integer);
  251. {
  252.      adds a record to the file.  If a record with the same
  253.      key already exists, then exits with an error code
  254. }
  255. var
  256.   Item         : Integer;
  257. begin
  258.   Error := NoError;
  259.   Item := FindKey(Rec.Key);
  260.   if Item > 0
  261.     then Error := AlreadyExists
  262.     else PutRecord(Rec,Error)
  263. end; { of proc AddRecord }
  264.  
  265. procedure DeleteRecord(Key : Keytype; var Error : Integer);
  266. {
  267.      deletes the record with 'Key' by copying the last record
  268.      in the file to that slot, then modifies KList by shuffling
  269.      all the key entries up
  270. }
  271. var
  272.   Item,Last,Max,MVal     : Integer;
  273.   TRec                   : DataRec;
  274. begin
  275.   Error := NoError;
  276.   Item := FindKey(Key);
  277.   if Item = -1
  278.     then Error := RecordNotFound
  279.   else begin
  280.     Max := 1; MVal := KList[Max].Num;
  281.     for Last := 2 to MaxRec do
  282.       if KList[Last].Num > MVal then begin
  283.         Max := Last; MVal := KList[Last].Num
  284.       end;
  285.     if Max <> Item then begin
  286.       FRead(MVal,TRec,Error);             { get last record in file }
  287.       FWrite(KList[Item].Num,TRec,Error); { write over it }
  288.       KList[Max].Num := KList[Item].Num
  289.     end;
  290.     for Last := Item to MaxRec-1 do     { delete KList[Item] }
  291.       KList[Last] := KList[Last+1];
  292.     MaxRec := MaxRec - 1                { adjust # of records }
  293.   end
  294. end; { of proc DeleteRecord }
  295.  
  296. {                         USERIO.LIB
  297.  
  298.                  procedure and functions in this library
  299.  
  300.   WriteStr           write message out at (Col,Line)
  301.   Error              writes message out at (1,1), waits for character
  302.   GetChar            prompt user for one of a set of characters
  303.   Yes                gets Y/N answer from user
  304.   GetString          prompt user for a string
  305.   IOCheck            checks for I/O error; prints message if necessary
  306.  
  307. }
  308.  
  309. type
  310.   MsgStr             = string[80];
  311.   CharSet            = set of Char;
  312.  
  313. var
  314.   IOErr              : Boolean;
  315.   IOCode             : Integer;
  316.  
  317. procedure WriteStr(Col,Line : Integer; TStr : MsgStr);
  318. {
  319.        purpose       writes message out at spot indicated
  320.        last update   23 Jun 85
  321. }
  322. begin
  323.   GoToXY(Col,Line); ClrEol;
  324.   Write(TStr)
  325. end; { of proc WriteStr }
  326.  
  327. procedure Error(Msg : MsgStr);
  328. {
  329.        purpose       writes error message out at (1,1); waits for character
  330.        last update   05 Jul 85
  331. }
  332. const
  333.   Bell               = ^G;
  334. var
  335.   Ch                 : Char;
  336. begin
  337.   WriteStr(1,1,Msg+Bell+' (hit any key) ');
  338.   Read(Kbd,Ch)
  339. end; { of proc Error }
  340.  
  341. procedure GetChar(var Ch : Char; Prompt : MsgStr; OKSet : CharSet);
  342. {
  343.        purpose       let user enter command
  344.        last update   23 Jun 85
  345. }
  346. begin
  347.   WriteStr(1,1,Prompt);
  348.   repeat
  349.     Read(Kbd,Ch);
  350.     Ch := UpCase(Ch)
  351.   until Ch in OKSet;
  352.   WriteLn(Ch)
  353. end; { of proc GetChar }
  354.  
  355. function Yes(Question : MsgStr) : Boolean;
  356. {
  357.        purpose       asks user Y/N question
  358.        last update   03 Jul 85
  359. }
  360. var
  361.   Ch                 : Char;
  362. begin
  363.   GetChar(Ch,Question+' (Y/N) ',['Y','N']);
  364.   Yes := (Ch = 'Y')
  365. end; { of func Yes }
  366.  
  367. procedure GetString(var NStr : MsgStr; Prompt : MsgStr; MaxLen : Integer;
  368.                     OKSet : CharSet);
  369. {
  370.        purpose       get string from user
  371.        last update   09 Jul 85
  372. }
  373. const
  374.   BS                 = ^H;
  375.   CR                 = ^M;
  376.   ConSet             : CharSet = [BS,CR];
  377. var
  378.   TStr               : MsgStr;
  379.   TLen,X             : Integer;
  380.   Ch                 : Char;
  381. begin
  382.   {$I-} { turn off I/O checking }
  383.   TStr := '';
  384.   TLen := 0;
  385.   WriteStr(1,1,Prompt);
  386.   X := 1 + Length(Prompt);
  387.   OKSet := OKSet + ConSet;
  388.   repeat
  389.     GoToXY(X,1);
  390.     repeat
  391.       Read(Kbd,Ch)
  392.     until Ch in OKSet;
  393.     if Ch = BS then begin
  394.       if TLen > 0 then begin
  395.         TLen := TLen - 1;
  396.         X := X - 1;
  397.         GoToXY(X,1); Write(' ');
  398.       end
  399.     end
  400.     else if (Ch <> CR) and (TLen < MaxLen) then begin
  401.       Write(Ch);
  402.       TLen := TLen + 1;
  403.       TStr[TLen] := Ch;
  404.       X := X + 1;
  405.     end
  406.   until Ch = CR;
  407.   if TLen > 0 then begin
  408.     TStr[0] := Chr(TLen);
  409.     NStr := TStr
  410.   end
  411.   else Write(NStr)
  412.   {$I+}
  413. end; { of proc GetString }
  414.  
  415. procedure IOCheck(IOCode : Integer);
  416. {
  417.        purpose       check for IO error; print message if needed
  418.        last update   19 Feb 86
  419. }
  420. var
  421.   TStr               : string[4];
  422. begin
  423.   IOErr  := (IOCode <> 0);
  424.   if IOErr then case IOCode of
  425.     $01  : Error('IOERROR> File does not exist');
  426.     $02  : Error('IOERROR> File not open for input');
  427.     $03  : Error('IOERROR> File not open for output');
  428.     $04  : Error('IOERROR> File not open');
  429.     $10  : Error('IOERROR> Error in numeric format');
  430.     $20  : Error('IOERROR> Operation not allowed on logical device');
  431.     $21  : Error('IOERROR> Not allowed in direct mode');
  432.     $22  : Error('IOERROR> Assign to standard files not allowed');
  433.     $90  : Error('IOERROR> Record length mismatch');
  434.     $91  : Error('IOERROR> Seek beyond end of file');
  435.     $99  : Error('IOERROR> Unexpected end of file');
  436.     $F0  : Error('IOERROR> Disk write error');
  437.     $F1  : Error('IOERROR> Directory is full');
  438.     $F2  : Error('IOERROR> File size overflow');
  439.     $FF  : Error('IOERROR> File disappeared')
  440.     else   Str(IOCode:3,TStr);
  441.            Error('IOERROR> Unknown I/O error:  '+TStr)
  442.   end
  443. end; { of proc IOCheck }
  444.  
  445.  
  446. {              declarations and code for test program            }
  447. const
  448.   CmdPrompt           : MsgStr =
  449.              'TEST> A)dd, D)elete, F)ind, L)ist, I)ndex, C)lose, Q(uit: ';
  450.   FilePrompt          : MsgStr = 'TEST> Enter file name: ';
  451.   DonePrompt          : MsgStr = 'TEST> Another file?';
  452.  
  453.   CmdSet              : CharSet = ['A','D','F','L','I','C','Q'];
  454.   NameSet             : CharSet = [' '..'~'];
  455.   PhoneSet            : CharSet = ['0'..'9','-','/','(',')'];
  456.  
  457. var
  458.   Cmd                 : Char;
  459.   ErrVal              : Integer;
  460.   FileName            : FileStr;
  461.   Done                : Boolean;
  462.  
  463.  
  464. procedure FileError(ErrVal : Integer);
  465. begin
  466.   if ErrVal < 100 then case ErrVal of
  467.     RecCountErr       : Error('Record count mismatch');
  468.     NewFileCreated    : Error('Creating new file');
  469.     RecordNotFound    : Error('Record not found');
  470.     NoMoreRoom        : Error('No more room');
  471.     AlreadyExists     : Error('Record already exists')
  472.   end
  473.   else begin
  474.     IOCheck(ErrVal-100)
  475.   end
  476. end; { of proc FileError }
  477.  
  478. procedure DoAdd;
  479. {
  480.        purpose        add a record to the file
  481.        last update    19 Feb 86
  482. }
  483. var
  484.   TStr                : MsgStr;
  485.   TRec                : DataRec;
  486. begin
  487.   FillChar(TRec,SizeOf(TRec),0);
  488.   with TRec do begin
  489.     TStr := '';
  490.     GetString(TStr,'ADD> Enter name:  ',40,NameSet);
  491.     if TStr <> '' then begin
  492.       Key := TStr;  TStr := '';
  493.       GetString(TStr,'ADD> Enter phone #: ',12,PhoneSet);
  494.       theRest := TStr;
  495.       AddRecord(TRec,ErrVal);
  496.       Flush(DFile);
  497.       FileError(ErrVal)
  498.     end
  499.   end;
  500. end; { of proc DoAdd }
  501.  
  502. procedure DoDelete;
  503. {
  504.        purpose        delete a record from the file
  505.        last update    19 Feb 86
  506. }
  507. var
  508.   Key                 : Keytype;
  509. begin
  510.   GetString(Key,'DELETE> Enter name:  ',40,NameSet);
  511.   DeleteRecord(Key,ErrVal);
  512.   FileError(ErrVal)
  513. end; { of proc DoDelete }
  514.  
  515. procedure DoFind;
  516. {
  517.        purpose        find a record in the file
  518.        last update    19 Feb 86
  519. }
  520. var
  521.   Key                 : Keytype;
  522.   TRec                : DataRec;
  523. begin
  524.   GetString(Key,'FIND> Enter name:  ',40,NameSet);
  525.   GetRecord(Key,TRec,ErrVal);
  526.   if ErrVal = NoError then begin
  527.     WriteStr(1,2,'The phone number is ');
  528.     Writeln(TRec.theRest)
  529.   end
  530.   else FileError(ErrVal)
  531. end; { of proc DoDelete }
  532.  
  533. procedure DoList;
  534. {
  535.        purpose        list out contents of the file
  536.        last update    19 Feb 86
  537. }
  538. var
  539.   TRec                : DataRec;
  540.   Indx                : Integer;
  541. begin
  542.   ClrScr; Writeln;
  543.   for Indx := 1 to MaxRec do with KList[Indx] do begin
  544.     WriteStr(1,Indx+1,Key); Write(' ':(45-Length(Key)));
  545.     GetRecord(Key,TRec,ErrVal);
  546.     if ErrVal = NoError then with TRec do
  547.       Writeln(theRest)
  548.     else FileError(ErrVal)
  549.   end
  550. end; { of proc DoList }
  551.  
  552. procedure ShowIndex;
  553. {
  554.        purpose        list out contents of the key list
  555.        last update    19 Feb 86
  556. }
  557. var
  558.   Indx                : Integer;
  559. begin
  560.   ClrScr; Writeln;
  561.   for Indx := 1 to MaxRec do with KList[Indx] do
  562.     Writeln(Key,' ':(45-Length(Key)),Num:5)
  563. end; { of proc DoList }
  564.  
  565. begin
  566.   repeat
  567.     Done := False;
  568.     ClrScr;
  569.     GetString(FileName,FilePrompt,80,NameSet);
  570.     InitStuff(FileName,ErrVal);
  571.     FileError(ErrVal);
  572.     repeat
  573.       GetChar(Cmd,CmdPrompt,CmdSet);
  574.       case Cmd of
  575.         'A'    : DoAdd;
  576.         'D'    : DoDelete;
  577.         'F'    : DoFind;
  578.         'L'    : DoList;
  579.         'I'    : ShowIndex;
  580.         'Q'    : Done := True
  581.       end
  582.     until (Cmd = 'C') or Done;
  583.     CleanUpStuff(ErrVal);
  584.     FileError(ErrVal);
  585.     ClrScr;
  586.     if not Done
  587.       then Done := not Yes(DonePrompt)
  588.   until Done
  589. end. { of program TestIndex }
  590.