home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / UPDATE.ZIP / UPDATE.PAS
Encoding:
Pascal/Delphi Source File  |  1987-12-16  |  16.5 KB  |  503 lines

  1. {-----------------------------------------------------------------------------}
  2.  
  3. Unit Update;
  4. {
  5. | The Update unit supports up to sixteen files in read/write (update) mode.
  6. | Each file may reside on either floppy disk or hard disk, may have a record
  7. | size of up to 64K bytes, and may contain as many records as the disk capacity
  8. | will allow (no 64K limit).  Both random and sequential file access is
  9. | supported.  DOS 2.0 or higher is required.  The sections of this unit that
  10. | have been commented out were used to test this unit prior to release.  Please
  11. | refer to the Test_Update_Unit procedure (at the end of this unit) for
  12. | examples of how to use this unit.
  13. |
  14. | Version 1.0
  15. | Written by Mike Heffron, CyberSoft, Inc.
  16. | CompuServe 72170,126
  17. | Released to the public domain.
  18. }
  19. {-----------------------------------------------------------------------------}
  20.  
  21. interface
  22.  
  23. Uses Dos;
  24. {
  25. | Refer to Peter Norton's book, "Programmer's Guide to the IBM PC,"
  26. | pages 308-310, for an explanation of the DOS functions used by this unit.
  27. |
  28. | Set the following constants as desired.  Note that even if both constants
  29. | are false, error checking will be performed and can be checked via the
  30. | Update_IOresult function (which is similar to the IOresult function).
  31. | An Update_IOresult of zero indicates no error, any other value indicates an
  32. | error as specified in the Identify_Error procedure.
  33. }
  34. Const Halt_On_Update_Error = TRUE;
  35.       Show_Update_Errors   = TRUE;
  36.  
  37. {-----------------------------------------------------------------------------}
  38.  
  39. Procedure Check_Error_Status(Reg : Registers);
  40. Function  Handle_Valid(File_Handle : Integer) : Boolean;
  41. Procedure Identify_Error(Error_Code,File_Handle : Integer);
  42. Procedure Identify_Procedure;
  43. Procedure Process_Error(File_Handle : Integer; Error_Code : Byte);
  44. Procedure Update_Assign(Var File_Handle : Integer; File_Name : String;
  45.                         Record_Size : Word);
  46. Procedure Update_Close(File_Handle : Integer);
  47. Function  Update_FileSize(File_Handle : Integer) : LongInt;
  48. Function  Update_IOresult : Byte;
  49. Procedure Update_Read(File_Handle : Integer; Var One_Record);
  50. Procedure Update_Seek(File_Handle : Integer; Record_Number : LongInt);
  51. Procedure Update_Write(File_Handle : Integer; Var One_Record);
  52.  
  53. {-----------------------------------------------------------------------------}
  54.  
  55. implementation
  56.  
  57. Type  Update_Type = Record
  58.          {
  59.          | 5..20 = DOS 2.0+ file handles.
  60.          }
  61.          Record_Size   : Array[5..20] of Word;
  62.          Procedure_ID,
  63.          IOresult      : Byte;
  64.          File_Name     : Array[5..20] of String[30];
  65.          Temp_Name     : String[30]
  66.          end;
  67.  
  68. Var   Up_Date     : Update_Type;
  69.  
  70. {-----------------------------------------------------------------------------}
  71.  
  72. Procedure Check_DOS;
  73. {
  74. | The Check_DOS procedure prevents the Update unit from being used if an
  75. | incorrect version of DOS is installed.
  76. }
  77. Var   Reg : Registers;
  78.  
  79. begin
  80.    Reg.Ax := $3000;
  81.    Msdos(Reg);
  82.    If Reg.Al<2 then begin
  83.       writeln('*** Error: DOS 2.0 or higher required to use Update unit.');
  84.       halt
  85.       end
  86. end;
  87.  
  88. {-----------------------------------------------------------------------------}
  89.  
  90. Function Update_IOresult : Byte;
  91. {
  92. | The Update_IOresult function returns the IOresult from the most recently
  93. | performed Update unit I/O procedure.
  94. }
  95. begin
  96.    Update_IOresult := Up_Date.IOresult
  97. end;
  98.  
  99. {-----------------------------------------------------------------------------}
  100.  
  101. Procedure Identify_Procedure;
  102. {
  103. | The Identify_Procedure procedure identifies the update procedure that was
  104. | most recently performed (the procedure in which an IO error occured).
  105. }
  106. begin
  107.    Write('*** Error: ');
  108.    Case Up_Date.Procedure_ID of
  109.       0  : Write('Update_Create  ');
  110.       1  : Write('Update_Open    ');
  111.       2  : Write('Update_Close   ');
  112.       3  : Write('Update_Seek    ');
  113.       4  : Write('Update_Read    ');
  114.       5  : Write('Update_Write   ')
  115.       else Write('Update_Unknown ')
  116.       end {Case}
  117. end;
  118.  
  119. {-----------------------------------------------------------------------------}
  120.  
  121. Procedure Identify_Error(Error_Code,File_Handle : Integer);
  122. {
  123. | The Identify_Error procedure identifies the specified Error_Code.  These
  124. | error codes are discussed on page 297 of Norton's book.
  125. }
  126. begin
  127.    Case Error_Code of
  128.       1  : Write('Invalid function number');
  129.       2  : Write('File not found');
  130.       3  : Write('Path not found');
  131.       4  : Write('No handle available');
  132.       5  : Write('Access denied');
  133.       6  : Write('Invalid file handle=',File_Handle);
  134.       7  : Write('Memory control blocks destroyed');
  135.       8  : Write('Insufficient memory');
  136.       9  : Write('Invalid memory block address');
  137.       10 : Write('Invalid environment');
  138.       11 : Write('Invalid format');
  139.       12 : Write('Invalid access code');
  140.       13 : Write('Invalid data');
  141.       15 : Write('Invalid drive specification');
  142.       16 : Write('Attempt to remove current directory');
  143.       17 : Write('Not same device');
  144.       18 : Write('No more files to be found')
  145.       else Write('Unknown DOS error=',error_code)
  146.       end; {Case}
  147.    If Error_Code=6 then Writeln(' (',Up_Date.Temp_Name,').')
  148.                    else Writeln(' (',Up_Date.File_Name[File_Handle],').')
  149. end;
  150.  
  151. {-----------------------------------------------------------------------------}
  152.  
  153. Procedure Process_Error(File_Handle : Integer; Error_Code : Byte);
  154. {
  155. | The Process_Error procedure identifies an update error if present.
  156. }
  157. begin
  158.    If Error_Code>0 then begin
  159.       If Show_Update_Errors then begin
  160.          Identify_Procedure;
  161.          Identify_Error(Error_Code,File_Handle)
  162.          end; {If Show_Update_Errors}
  163.       If Halt_On_Update_Error then halt
  164.       end {If Error_Code>0}
  165. end;
  166.  
  167. {-----------------------------------------------------------------------------}
  168.  
  169. Function Handle_Valid(File_Handle : Integer) : Boolean;
  170. {
  171. | The Handle_Valid function determines whether the specified File_Handle is
  172. | within the range allowed by DOS 2.0+.
  173. }
  174. begin
  175.    If ( (File_Handle<5) or (File_Handle>20) ) then begin
  176.       Up_Date.IOresult := 6;
  177.       Process_Error(File_Handle,6)
  178.       end;
  179.    Handle_Valid := Up_Date.IOresult<>6
  180. end;
  181.  
  182. {-----------------------------------------------------------------------------}
  183.  
  184. Procedure Check_Error_Status(Reg : Registers);
  185. {
  186. | The Check_Error_Status procedure sets the Up_Date.IOresult according to
  187. | whether an IO error has occured.
  188. }
  189. begin
  190.    If odd(Reg.Flags) then Up_Date.IOresult := Reg.Al
  191.                      else Up_Date.IOresult := 0
  192. end;
  193.  
  194. {-----------------------------------------------------------------------------}
  195.  
  196. Procedure Update_Create(Var File_Handle : Integer);
  197. {
  198. | The Update_Create procedure creates and opens a file for read/write (update)
  199. | operations.
  200. }
  201. Var   Reg : Registers;
  202.  
  203. begin
  204.    Up_Date.Procedure_ID := 0;
  205.    Reg.Ax := $3c00;                    {Specify DOS create file function.}
  206.    Reg.Cx := 0;
  207.    Reg.Ds := Seg(Up_Date.Temp_Name);   {Point DS:DX to ASCIIZ file name.}
  208.    Reg.Dx := Ofs(Up_Date.Temp_Name)+1; {Skip size byte (first character).}
  209.    Msdos(Reg);
  210.    File_Handle := Reg.Ax;
  211.    Check_Error_Status(Reg)
  212. end;
  213.  
  214. {-----------------------------------------------------------------------------}
  215. {$V-}
  216. Procedure Update_Assign(Var File_Handle : Integer; File_Name : String;
  217.                         Record_Size : Word);
  218. {
  219. | The Update_Assign procedure opens a file for read/write (update) operations.
  220. }
  221. Const Deny_None  = $40; {File sharing mode -- deny no operation.}
  222.       Read_Write = $02; {File access code -- read/write access allowed.}
  223.  
  224. Var   Reg : Registers;
  225.  
  226. begin
  227.    Up_Date.Temp_Name := File_Name+#0;     {DOS requires an ASCIIZ file name.}
  228.    Up_Date.Procedure_ID := 1;
  229.    Reg.Ax := $3d00+Deny_None+Read_Write;  {Specify DOS open file function.}
  230.    Reg.Ds := Seg(Up_Date.Temp_Name);      {Point DS:DX to ASCIIZ file name.}
  231.    Reg.Dx := Ofs(Up_Date.Temp_Name)+1;    {Skip size byte (first character).}
  232.    Msdos(Reg);
  233.    File_Handle := Reg.Ax;
  234.    Check_Error_Status(Reg);
  235.    {
  236.    | Create the specified file if it doesn't yet exist.
  237.    }
  238.    If ( (Reg.Al=2) or (Reg.Al=4) ) then Update_Create(File_Handle);
  239.    {
  240.    | Set up internal file management variables.
  241.    }
  242.    If Handle_Valid(File_Handle) then begin
  243.       Up_Date.Record_Size[File_Handle] := Record_Size;
  244.       Up_Date.File_Name[File_Handle]   := Up_Date.Temp_Name
  245.       end;
  246.    Process_Error(File_Handle,Up_Date.IOresult)
  247. end;
  248. {$V+}
  249. {-----------------------------------------------------------------------------}
  250.  
  251. Procedure Update_Close(File_Handle : Integer);
  252. {
  253. | The Update_Close procedure closes a file for read/write (update) operations.
  254. }
  255. Var Reg : Registers;
  256.  
  257. begin
  258.    Up_Date.Procedure_ID := 2;
  259.    If Handle_Valid(File_Handle) then begin
  260.       Reg.Ax := $3e00;                      {Specify DOS close file function.}
  261.       Reg.Bx := File_Handle;
  262.       Msdos(Reg);
  263.       Check_Error_Status(Reg)
  264.       end;
  265.    Process_Error(File_Handle,Up_Date.IOresult)
  266. end;
  267.  
  268. {-----------------------------------------------------------------------------}
  269.  
  270. Procedure Update_Seek(File_Handle : Integer; Record_Number : LongInt);
  271. {
  272. | The Update_Seek procedure positions the file pointer over the requested
  273. | record.  NOTE: the first record is record zero.
  274. }
  275. Var Reg  : Registers;
  276.     Temp : LongInt;
  277.  
  278. begin
  279.    Up_Date.Procedure_ID := 3;
  280.    If Handle_Valid(File_Handle) then begin
  281.       Temp := Up_Date.Record_Size[File_Handle] * Record_Number;
  282.  
  283.       Reg.Ax := $4200;       {Specify DOS move file pointer function.}
  284.       Reg.Bx := File_Handle;
  285.       Reg.Cx := Temp shr 16;
  286.       Reg.Dx := Temp and $ffff;
  287.       Msdos(Reg);
  288.       Check_Error_Status(Reg)
  289.       end;
  290.    Process_Error(File_Handle,Up_Date.IOresult)
  291. end;
  292.  
  293. {-----------------------------------------------------------------------------}
  294.  
  295. Function Update_FileSize(File_Handle : Integer) : LongInt;
  296. {
  297. | The Update_FileSize function returns the size of the specified file
  298. | in records.  NOTE: the size of an empty file is zero, the size of a file
  299. | containing one record is one, etc.
  300. }
  301. Var Reg   : Registers;
  302.     Cx,Dx : Word;
  303.     Size  : LongInt;
  304.  
  305. begin
  306.    Up_Date.Procedure_ID := 3;
  307.    If Handle_Valid(File_Handle) then begin
  308.       {
  309.       | Find current file position.
  310.       }
  311.       Reg.Ax := $4201;       {Specify DOS move file pointer function.}
  312.       Reg.Bx := File_Handle;
  313.       Reg.Cx := 0;
  314.       Reg.Dx := 0;
  315.       Msdos(Reg);
  316.       Check_Error_Status(Reg);
  317.       Cx := Reg.Cx;
  318.       Dx := Reg.Dx;
  319.       {
  320.       | Find end of file.
  321.       }
  322.       Reg.Ax := $4202;       {Specify DOS move file pointer function.}
  323.       Reg.Bx := File_Handle;
  324.       Reg.Cx := 0;
  325.       Reg.Dx := 0;
  326.       Msdos(Reg);
  327.       Check_Error_Status(Reg);
  328.       {
  329.       | Determine file size.
  330.       }
  331.       If Up_Date.Record_Size[File_Handle]=0 then Size := 0
  332.       else Size := ((Reg.Cx shl 16)+Reg.Ax) div Up_Date.Record_Size[File_Handle];
  333.       {
  334.       | Restore initial file position.
  335.       }
  336.       Reg.Ax := $4200;       {Specify DOS move file pointer function.}
  337.       Reg.Bx := File_Handle;
  338.       Reg.Cx := Cx;
  339.       Reg.Dx := Dx;
  340.       Msdos(Reg);
  341.       Check_Error_Status(Reg);
  342.       end;
  343.    Process_Error(File_Handle,Up_Date.IOresult);
  344.    Update_FileSize := Size
  345. end;
  346.  
  347. {-----------------------------------------------------------------------------}
  348.  
  349. Procedure Update_Read(File_Handle : Integer; Var One_Record);
  350. {
  351. | The Update_Read procedure reads One_Record from the current file position.
  352. | Note that "One_Record" is an untyped variable, i.e., it is passed to this
  353. | procedure as an address only.  DOS reads the data into the specified
  354. | address, and does not care what the actual data type is.
  355. }
  356. Var Reg : Registers;
  357.  
  358. begin
  359.    Up_Date.Procedure_ID := 4;
  360.    If Handle_Valid(File_Handle) then begin
  361.       Reg.Ax := $3f00;       {Specify DOS read file function.}
  362.       Reg.Bx := File_Handle;
  363.       Reg.Cx := Up_Date.Record_Size[File_Handle];
  364.       Reg.Ds := Seg(One_Record);
  365.       Reg.Dx := Ofs(One_Record);
  366.       Msdos(Reg);
  367.       Check_Error_Status(Reg)
  368.       end;
  369.    Process_Error(File_Handle,Up_Date.IOresult)
  370. end;
  371.  
  372. {-----------------------------------------------------------------------------}
  373.  
  374. Procedure Update_Write(File_Handle : Integer; Var One_Record);
  375. {
  376. | The Update_Write procedure writes One_Record to the current file position.
  377. | Note that "One_Record" is an untyped variable, i.e., it is passed to this
  378. | procedure as an address only.  DOS writes the data from the specified
  379. | address, and does not care what the actual data type is.
  380. }
  381. Var Reg : Registers;
  382.  
  383. begin
  384.    Up_Date.Procedure_ID := 5;
  385.    If Handle_Valid(File_Handle) then begin
  386.       Reg.Ax := $4000;       {Specify DOS write file function.}
  387.       Reg.Bx := File_Handle;
  388.       Reg.Cx := Up_Date.Record_Size[File_Handle];
  389.       Reg.Ds := Seg(One_Record);
  390.       Reg.Dx := Ofs(One_Record);
  391.       Msdos(Reg);
  392.       Check_Error_Status(Reg)
  393.       end;
  394.    Process_Error(File_Handle,Up_Date.IOresult)
  395. end;
  396.  
  397. {-----------------------------------------------------------------------------}
  398. {
  399. | The following section of code (that has been commented out) was used only
  400. | during the initial testing of this unit, and now serves only as an
  401. | example of how to use the Update unit.
  402. }
  403. (*
  404. Procedure Test_Update_Unit;
  405. {
  406. | The Test_Update_Unit procedure tests all the functions and procedures that
  407. | are supplied in the update unit.  NOTE: this is not a rigorous test, i.e.,
  408. | the unit is not pushed to its limits.
  409. }
  410. Const Max_Rec  = 9;
  411.       Max_Rec2 = 7;
  412.  
  413. Type  Rec_Type  = Array[1..5] of Byte;
  414.       Rec2_Type = Array[1..7] of Byte;
  415.  
  416. Var   f,f2,i,j : Integer;
  417.       Last_Rec,Last_Rec2 : LongInt;
  418.       r  : Array[0..9] of Rec_Type;
  419.       r2 : Array[0..9] of Rec2_Type;
  420.  
  421. begin
  422.    {
  423.    | Initialize test program.
  424.    }
  425.    For i := 0 to Max_Rec do
  426.       For j := 1 to 5 do
  427.          r[i][j] := i;
  428.    For i := 0 to Max_Rec2 do
  429.       For j := 1 to 7 do
  430.          r2[i][j] := i;
  431.    {
  432.    | Notice that "Update_Assign" is like "assign," except that "f" is an
  433.    | integer instead of a file variable, and that you must specify the size of
  434.    | ONE of your records (just use the Turbo Pascal "SizeOf" function).
  435.    }
  436.    Update_Assign(f, 'scrap.bin',SizeOf(r[0]));
  437.    Update_Assign(f2,'scrap.too',SizeOf(r2[0]));
  438.    {
  439.    | Notice the absense of an "update_reset" and an "update_rewrite."  Since
  440.    | you may read or write at any time (without changing modes), reset and
  441.    | rewrite are not needed.  The "Update_Assign" procedure initializes the
  442.    | file position pointer to the first record (as would have been done by
  443.    | reset or rewrite).  If it is neccessary to return to the first (or any
  444.    | other) record, just use the Update_Seek procedure.
  445.    |
  446.    | Notice that "Update_Write" is like "write," except that "f" is an
  447.    | integer instead of a file variable.
  448.    }
  449.    For i := 0 to Max_Rec  do Update_Write(f, r[i]);
  450.    For i := 0 to Max_Rec2 do Update_Write(f2,r2[i]);
  451.    {
  452.    | Notice that there is no "update_eof" function.  It is left to the user
  453.    | to use "Update_FileSize" to find out how many records are contained
  454.    | in the file (if not otherwise known).
  455.    |
  456.    | Notice that "Update_FileSize" is like "FileSize," except that "f" is an
  457.    | integer instead of a file variable.
  458.    }
  459.    Last_Rec  := Update_FileSize(f)-1; {-1 because first record is number zero.}
  460.    Last_Rec2 := Update_FileSize(f2)-1;
  461.    {
  462.    | Notice that "Update_Seek" is like "seek," except that "f" is an integer
  463.    | instead of a file variable.
  464.    }
  465.    Update_Seek(f,2);
  466.    Update_Write(f,r[8]);
  467.    Update_Seek(f,8);
  468.    Update_Write(f,r[2]);
  469.    Update_Seek(f,0);
  470.    {
  471.    | Notice that "Update_Read" is like "read," except that "f" is an integer
  472.    | instead of a file variable.
  473.    }
  474.    For i := 0 to Last_Rec do Update_Read(f,r[i]);
  475.    Update_Seek(f2,0);
  476.    For i := 0 to Last_Rec2 do Update_Read(f2,r2[i]);
  477.    {
  478.    | Notice that "Update_Close" is like "close," except that "f" is an
  479.    | integer instead of a file variable.
  480.    }
  481.    Update_Close(f);
  482.    Update_Close(f2);
  483.    {
  484.    | Display file contents to demonstrate that records have been written
  485.    | and read properly.
  486.    }
  487.    For i := 0 to Last_Rec do Write(r[i][1]:4);
  488.    Writeln;
  489.    For i := 0 to Last_Rec2 do Write(r2[i][1]:4);
  490.    Writeln
  491. end;
  492. *)
  493. {-----------------------------------------------------------------------------}
  494.  
  495. begin
  496.    Check_DOS;
  497. (*
  498.    Test_Update_Unit
  499. *)
  500. end. {end of unit}
  501.  
  502. {-----------------------------------------------------------------------------}
  503.