home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / database / dbffix / dbffix.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1992-01-21  |  31.3 KB  |  867 lines

  1. {Program to allow a dbf file to be edited to fix problems in file structure}
  2. uses dos;
  3.  
  4. {Information related to dBase file formats.  Written in pascal, rather than C}
  5. {since it may eventually be useful in SCHED                                  }
  6.  
  7. {Information below courtesy Mark Sadler}
  8. (* dBASE III DATABASE FILE HEADER:
  9. +---------+-------------------+---------------------------------+
  10. |  BYTE   |     CONTENTS      |          MEANING                |
  11. +---------+-------------------+---------------------------------+
  12. |  0      |  1 byte           | dBASE III version number        |
  13. |         |                   |  (03H without a .DBT file)      |
  14. |         |                   |  (83H with a .DBT file)         |
  15. +---------+-------------------+---------------------------------+
  16. |  1-3    |  3 bytes          | date of last update             |
  17. |         |                   |  (YY MM DD) in binary format    |
  18. +---------+-------------------+---------------------------------+
  19. |  4-7    |  32 bit number    | number of records in data file  |
  20. +---------+-------------------+---------------------------------+
  21. |  8-9    |  16 bit number    | length of header structure      |
  22. +---------+-------------------+---------------------------------+
  23. |  10-11  |  16 bit number    | length of the record            |
  24. +---------+-------------------+---------------------------------+
  25. |  12-31  |  20 bytes         | reserved bytes (version 1.00)   |
  26. +---------+-------------------+---------------------------------+
  27. |  32-n   |  32 bytes each    | field descriptor array          |
  28. |         |                   |  (see below)                    | --+
  29. +---------+-------------------+---------------------------------+   |
  30. |  n+1    |  1 byte           | 0DH as the field terminator     |   |
  31. +---------+-------------------+---------------------------------+   |
  32. |                                                                   |
  33. |                                                                   |
  34. A FIELD DESCRIPTOR:      <------------------------------------------+
  35. +---------+-------------------+---------------------------------+
  36. |  BYTE   |     CONTENTS      |          MEANING                |
  37. +---------+-------------------+---------------------------------+
  38. |  0-10   |  11 bytes         | field name in ASCII zero-filled |
  39. +---------+-------------------+---------------------------------+
  40. |  11     |  1 byte           | field type in ASCII             |
  41. |         |                   |  (C N L D or M)                 |
  42. +---------+-------------------+---------------------------------+
  43. |  12-15  |  32 bit number    | field data address              |
  44. |         |                   |  (address is set in memory)     |
  45. +---------+-------------------+---------------------------------+
  46. |  16     |  1 byte           | field length in binary          |
  47. +---------+-------------------+---------------------------------+
  48. |  17     |  1 byte           | field decimal count in binary   |
  49. +---------+-------------------+--------------------------------
  50. |  18-31  |  14 bytes         | reserved bytes (version 1.00)   |
  51. +---------+-------------------+---------------------------------+
  52. The data records are layed out as follows:
  53. 1. Data records are preceeded by one byte that is a
  54. space (20H) if the record is not deleted and an
  55. asterisk (2AH) if it is deleted.
  56. 2. Data fields are packed into records with no field
  57. separators or record terminators.
  58. 3. Data types are stored in ASCII format as follows:
  59. DATA TYPE      DATA RECORD STORAGE
  60. ---------      --------------------------------------------
  61. Character      (ASCII characters)
  62. Numeric        - . 0 1 2 3 4 5 6 7 8 9
  63. Logical        ? Y y N n T t F f  (? when not initialized)
  64. Memo           (10 digits representing a .DBT block number)
  65. Date           (8 digits in YYYYMMDD format, such as
  66. 19840704 for July 4, 1984)
  67.  
  68. This information came directly from the Ashton-Tate Forum. It can also be
  69. found in the Advanced Programmer's Guide available from Ashton-Tate.
  70.  
  71. One slight difference occurs between files created by dBASE III and those
  72. created by dBASE III Plus.  In the earlier files, there is an ASCII NUL
  73. character between the $0D end of header indicator and the start of the
  74. data. This NUL is no longer present in Plus, making a Plus header one byte
  75. smaller than an identically structured III file.  The functions included
  76. here will work with either version of dBASE III and writes files which may
  77. be used by either.
  78. *)
  79.  
  80. type
  81.  
  82. dbfHdr =                             {32 byte file header}
  83.  record
  84.   dBIIIvers : byte;                  {03h without .dbt file}
  85.   lrevYear  : byte;                  {last revision year modulo 100}
  86.   lrevMo    : byte;
  87.   lrevDay   : byte;
  88.   numRecs      : longint;               {number of records in data file}
  89.   hdrLen    : word;                  {size of header in bytes}
  90.   recLen    : word;                  {length of data record}
  91.   v100res   : array[1..20] of byte;  {reserved bytes in version 1.00 set nul}
  92.  end;
  93.  
  94.  
  95. dbfFldDes =                          {32 byte descriptor one for each field}
  96.  record
  97.   field_name : array[1..11] of char; {name of field in ascii}
  98.   field_typ  : char;                 {one of C,N,L,D, or M}
  99.   f_dta_adr  : longint;              {used in memory - set zero in file}
  100.   f_len      : byte;                 {length of field}
  101.   dec_pl     : byte;                 {number of decimals in binary}
  102.   v100res    : array[1..14] of byte; {reserved bytes in version 1.00 set nul}
  103.  end; 
  104.  
  105. dbIIIhdrTail =                       {goes at end of header & counts in size}
  106.  record                              {for dbIII this is two bytes}
  107.   ODhbyte : byte;
  108.   OOhbyte : byte;
  109.  end;
  110.  
  111. dbIIIplushdrTail =                   {for dbIII+ only one byte}
  112.  record
  113.   ODhbyte : byte;
  114.  end;  
  115.  
  116.  
  117. {The entire header, then is a dbfHdr followed by an array of dbfFldDes of}
  118. {unknown size (max 128 fields)                                           }
  119. {For reading an unknown .dbf file, use an array of 128 and overlay the   }
  120. {actual header with the formal data type.  For setting up a new one,     }
  121. {dimension the array in a type declaration.  Rembember the tail.         }
  122. {for convenience, put the file variable in the header                    }
  123.  
  124. genericHdr =
  125.  record
  126.   fil : file;
  127.   h   : dbfHdr;
  128.   f   : array[1..128] of dbfFldDes;
  129.  end;
  130.  
  131. ghPtr = ^genericHdr;
  132.  
  133. {fielding a record is a bit more of a job.  Would be a lot easier in C and }
  134. {a whole lot easier in C++ where we could overload operators.  However     }
  135. {we can fudge it in pascal by declaring an array [1..128] of appropriate   }
  136. {structures to handle the records                                          }
  137.  
  138. field_ary = array[1..254] of char;
  139. f_ap      = ^field_ary;
  140.  
  141. field_des =
  142.  record
  143.   f_name : string[11];    {copy name of field here for convenience}
  144.   f_len  : byte;          {maximum length of field}
  145.   f_dec  : byte;          {number of decimal places}
  146.   f_typ  : char;          {field type}
  147.   f_pos  : byte;          {same purpose as first byte of pascal string}
  148.   case integer of
  149.   0:(f_ptr  : f_ap);      {pointer to the actual character array}
  150.   1:(f_o:word; f_s:word); {components of pointer}
  151.  end;
  152.  
  153. rec_des = 
  154.  record
  155.   f_num  : word;                         {number of fields}
  156.   dealoc : boolean;                      {set true if memory was allocated by getmem}
  157.   b_siz  : word;
  158.   del_f  : ^char;                        {points to "delete" field marker}
  159.   f_d    : array[1..128] of field_des;   {actual field descriptors}
  160.  end;
  161.  
  162. rdptr = ^rec_des;
  163.  
  164. ptrParts = record
  165.             o:word;
  166.             s:word;
  167.            end;
  168. {------------------------------------------------------------------------------}
  169. function DBFreset(var s:string):ghPtr;
  170. {Reset a DBF file as untyped with a record size of 1 byte}
  171. {Allocate space for the header on the heap and return a  }
  172. {pointer to the header.  Return nil if open failed       }
  173. {"s" is a string containing the file path                }
  174.  
  175. var  lf  : file;
  176.      g   : ghPtr;
  177.      dh  : dbfHdr;
  178.      msz : word;    
  179.      
  180. begin
  181.  {$I-}
  182.  DBFreset := nil;
  183.  assign(lf,s);
  184.  if ioresult <> 0 then exit;
  185.  reset(lf,1);
  186.  if ioresult <> 0 then exit;
  187.  blockread(lf,dh,sizeOf(dbfHdr)); {read in the 32 byte header}
  188.  {$I+}
  189.  close(lf);                       {we will want to move the file variable anyway}
  190.  msz := sizeOf(lf) + dh.hdrLen;   {total amount of memory to get}
  191.  getmem(g,msz);                   {allocate it}
  192.  DBFreset := g;                   {and return it}
  193.  assign(g^.fil,s);
  194.  reset(g^.fil,1);
  195.  blockRead(g^.fil,g^.h,dh.hdrLen);  {get entire header into memory block}
  196. end; {function DBFreset}
  197. {------------------------------------------------------------------------------}
  198. function DBFrecRead(g:ghPtr; r:rdptr; recNum:word):boolean;
  199. {Read record "recNum" from dbf file with header at g^ into buffer}
  200. {referenced by initialized rdptr "r"                             }
  201. {true if read succeeds, else return false.                       }
  202.  
  203. var fptr : longint;
  204.     i    : integer;
  205.     
  206. begin
  207.  DBFrecRead := false;
  208.  if recNum > g^.h.numRecs then exit;
  209.  fptr := g^.h.recLen;
  210.  fptr := fptr*(recNum-1);
  211.  fptr := fptr + g^.h.hdrLen;
  212.  {$I-}
  213.  seek(g^.fil,fptr);
  214.  if ioresult <> 0 then exit;
  215.  blockRead(g^.fil,r^.del_f^,g^.h.recLen);
  216.  if ioresult <> 0 then exit;
  217.  DBFrecRead := true;
  218.  with r^ do                                  {set all the string counters to full}
  219.   for i := 1 to f_num do
  220.    f_d[i].f_pos := f_d[i].f_len;
  221.  {$I+}
  222. end; {function DBFrecRead}
  223. {------------------------------------------------------------------------------}
  224. function DBFrecWrite(g:ghPtr; r:rdptr; recNum:word; deleted:boolean):boolean;
  225. {Write data referenced by "r" to file referenced by "g" as    }
  226. {record "recNum".  If "recNum" = file size + 1, appends the   }
  227. {record and returns true.  If larger than that, returns false }
  228. {also returns false if IO error                               }
  229. {if if "deleted" is true, then record is written as deleted   }
  230.  
  231. var fptr : longint;
  232.  
  233. begin
  234.  DBFrecWrite := false;
  235.  if recNum > g^.h.numRecs then
  236.   begin
  237.    if (recNum - 1) > g^.h.numrecs then exit;
  238.    inc(g^.h.numrecs);
  239.   end; {if recNum > g^.h.numRecs}
  240.  fptr := g^.h.recLen;
  241.  fptr := fptr*(recNum-1);
  242.  fptr := fptr + g^.h.hdrLen;
  243.  seek(g^.fil,fptr);
  244.  if ioresult <> 0 then exit;
  245.  if deleted then r^.del_f^ := '*'
  246.  else r^.del_f^ := ' ';                     {fill "deleted" field appropriately}
  247.  blockWrite(g^.fil,r^.del_f^,g^.h.recLen);
  248.  if ioresult <> 0 then exit;
  249.  DBFrecWrite := true;
  250. end; {function DBFrecWrite}
  251. {------------------------------------------------------------------------------}
  252. procedure DBFclose(g:ghPtr; r:rdptr; altered:boolean);
  253. {close the file referenced by "g".  If altered is true, update the}
  254. {date and write out the header - which may have been modified if  }
  255. {any records were appended.  Closes the file and frees memory used}
  256. {by the header for it.  Also frees memory used by "r" if the flag }
  257. {dealoc is set                                                    }
  258.  
  259. const trailer : word = $000d;
  260.  
  261. var yr,mn,dy,dow,msz : word;
  262.     tail             : ^byte;
  263.     wt               : ^word absolute tail;
  264.     pp               : ptrParts absolute tail;
  265.     
  266. begin
  267.  if altered then
  268.   begin
  269.    getDate(yr,mn,dy,dow);                  {set the date in the header}
  270.    with g^.h do
  271.     begin
  272.      lrevYear := lo(yr mod 100);           {low two digits of year}
  273.      lrevMo   := lo(mn);
  274.      lrevDay  := lo(dy);
  275.      msz := 32*(numRecs + 1);              {get size of header data}
  276.      pointer(tail) := g;                   {make pointer to the trailing seperator}
  277.      inc(pp.s,msz div 16);
  278.      inc(pp.o,msz mod 16);                 {advance pointer}
  279.      msz := hdrLen - msz;                  {see how many trailing bytes}
  280.      if msz > 1 then wt^ := trailer        {if two, write a word}
  281.      else tail^ := lo(trailer);            {if one, write a byte}
  282.     end; {with g^.h}
  283.    seek(g^.fil,0);                         {top of the file}
  284.    blockWrite(g^.fil,g^.h,sizeOf(dbfhdr)); {write out the altered header} 
  285.   end; {if altered}
  286.  close(g^.fil); 
  287.  msz := sizeOf(g^.fil) + g^.h.hdrLen;
  288.  freemem(g,msz);                           {free the memory used by g}
  289.  if r^.dealoc then 
  290.   freemem(r,sizeOf(rec_des));              {if flag set, free memory used by recdes}
  291. end; {procedure DBFclose}
  292. {------------------------------------------------------------------------------}
  293. procedure fillRec_des(g:ghPtr; var r:rdptr; var buf:pointer);
  294. {Fill in the rec_des referenced by "r" from the data in "g" which }
  295. {must, of course, be initiaized.  if "buf" is not nil then "r"    }
  296. {is set up to reference it and b_siz is left zero.  If nil, space }
  297. {is allocated on heap and the b_siz field of "r" is filled in     }
  298. {BE SURE NOT TO CALL THIS WITH AN UNINITALIZED POINTER IN BUF OR R}
  299.  
  300. var i,numfld,j  : integer;
  301.     bofs        : word;
  302.     pp          : ptrParts absolute buf;
  303.     
  304. begin
  305.  if r = nil then
  306.   begin
  307.    getmem(r,sizeOf(rec_des));                         {initialize r if nil}
  308.    fillchar(r^,sizeOf(rec_des),0);                    {initialize to nulls}
  309.    r^.dealoc := true;                                 {set flag to deallocate}
  310.   end; {if r = nil}
  311.  if buf = nil then                                    {allocate buffer if needed}
  312.   begin
  313.    getmem(buf,g^.h.recLen);
  314.    r^.b_siz := g^.h.recLen; 
  315.   end; {if buf = nil}
  316.  with pp do                                           {normalize "buf" pointer}
  317.   begin
  318.    if o > 16 then
  319.     begin
  320.      inc(s,o div 16);
  321.      o := o mod 16;
  322.     end; {if o > 16}
  323.   end; {with pp}
  324.  numfld := g^.h.hdrLen;                               {calculate number of fields from header size}
  325.  dec(numfld,numfld mod 32);                           {delete trailer}
  326.  numfld := (numfld div 32) - 1;                       {number of 32 byte entries - dbfHdr}
  327.  pointer(r^.del_f) := buf;                            {the delete field is at base of buffer}
  328.  bofs := 1;                                           {and we are now offset 1 in the buffer}
  329.  r^.f_num := numfld;
  330.  for i := 1 to numfld do                              {now, initialize "r" field by field}
  331.   begin
  332.    with r^.f_d[i] do
  333.     begin
  334.      j := 1;                                          {copy field name}
  335.      while g^.f[i].field_name[j] <> #0 do
  336.       begin
  337.        f_name := f_name + g^.f[i].field_name[j];
  338.        inc(j);
  339.       end; {while g^.f[i].field_name[j] <> #0}
  340.      f_typ := g^.f[i].field_typ;                      {copy field type} 
  341.      f_dec := g^.f[i].dec_pl;                         {decimal specifier}
  342.      f_len := g^.f[i].f_len;                          {length}
  343.      f_o := pp.o + bofs mod 16;                       {set offset of pointer}
  344.      f_s := pp.s + bofs div 16;                       {and segment}
  345.      inc(bofs,f_len);                                 {ready offset for next round}     
  346.     end; {with r^.f_d[i]}
  347.   end; {for i := 1 to numfld}
  348. end; {procedure fillRec_des}
  349. {------------------------------------------------------------------------------}
  350. procedure setCfield(var fd:field_des; s:string);
  351. {set a character field to contain the passed string parameter}
  352.  
  353. var  len : integer;
  354.  
  355. begin
  356.  with fd do
  357.   begin
  358.    if f_typ <> 'C' then exit;         {quit if field type wrong}
  359.    fillchar(f_ptr^,f_len,$20);        {fill field with spaces  }
  360.    len := length(s);
  361.    if len > f_len then len := f_len;  {don't overfill}
  362.    move(s[1],f_ptr^,len);             {move string into field}
  363.    f_pos := len;
  364.   end; {with fd}
  365. end; {procedure setCfield}
  366. {------------------------------------------------------------------------------}
  367. procedure setNfield(var fd:field_des; val:real);
  368. {set a numeric field from given value which must be a real}
  369.  
  370. var  nstr : string[20];
  371.      
  372. begin
  373.  with fd do
  374.   begin
  375.    if f_typ <> 'N' then exit;
  376.    fillchar(f_ptr^,f_len,$20);
  377.    str(val:f_len:f_dec,nstr);          {make a string of the value}
  378.    if length(nstr) > f_len then exit;  {leave empty if overflow}
  379.    move(nstr[1],f_ptr^,f_len);         {otherwise, length is ok - just move}
  380.    f_pos := f_len;
  381.   end; {with fd}
  382. end; {procedure setNfield}
  383. {------------------------------------------------------------------------------}
  384. procedure setLfield(var fd:field_des; val:boolean);
  385. {put the given value into the logical field}
  386.  
  387. begin
  388.  with fd do
  389.   begin
  390.    if f_typ <> 'L' then exit;
  391.    if val then f_ptr^[1] := 'T'
  392.    else f_ptr^[1] := 'F';
  393.    f_pos := 1;
  394.   end; {with fd}
  395. end; {procedure setLfield}
  396. {------------------------------------------------------------------------------}
  397. procedure setDfield(var fd:field_des; year,month,day:integer);
  398. {put the given year, month, and day into date field}
  399.  
  400. var nst : string[4];
  401.     st  : string[10];
  402.     
  403. begin
  404.  with fd do
  405.   begin
  406.    if f_typ <> 'D' then exit;
  407.    str(year:4,st);
  408.    str(month,nst);
  409.    if (length(nst) < 2) then insert('0',nst,1);
  410.    st := st + nst;
  411.    str(day,nst);
  412.    if (length(nst) < 2) then insert('0',nst,1);
  413.    st := st + nst;
  414.    move(st[1],f_ptr^,10);
  415.    f_pos := 10;
  416.   end; {with fd}
  417. end; {procedure setDfield}
  418. {------------------------------------------------------------------------------}
  419. procedure setMfield(var fd:field_des; blkNum:longint);
  420. {set the memo field like a numeric field with block number blkNum}
  421.  
  422. var nst : string[10];
  423.  
  424. begin
  425.  with fd do
  426.   begin
  427.    if f_typ <> 'M' then exit;
  428.    str(blkNum:10,nst);
  429.    move(nst[1],f_ptr^,10);
  430.    f_pos := 10;
  431.   end; {with fd}
  432. end; {procedure setMfield}
  433. {------------------------------------------------------------------------------}
  434. procedure getCfield(var fd:field_des; var s:string);
  435. {get contents of field into string                                  }
  436. {does not type check the field, so any field can be read as a string}
  437.  
  438. var  len : integer;
  439.  
  440. begin
  441.  with fd do
  442.   begin
  443.    len := f_pos;
  444.    while (f_ptr^[len] = ' ')AND(len > 0) do dec(len);  {hunt last non white space char}
  445.    s[0] := chr(len);
  446.    move(f_ptr^,s[1],len);
  447.   end; {with fd}
  448. end; {procedure getCfield}
  449. {------------------------------------------------------------------------------}
  450. procedure getNfield(var fd:field_des; var valu:real);
  451. {return value of the numeric field in valu}
  452.  
  453. var  nst : string[10];
  454.      b   : byte absolute nst;
  455.      cod : integer;
  456.        
  457. begin
  458.  valu := 0;
  459.  with fd do
  460.   begin
  461.    if f_typ <> 'N' then exit;
  462.    getCfield(fd,nst);                                {get the field as a string}
  463.    while(nst[1] = ' ')AND(b>0) do delete(nst,1,1);   {delete leading spaces}
  464.    while(nst[b] = ' ')AND(b>0) do dec(b);            {trim trailing spaces}
  465.    val(nst, valu, cod);                              {evaluate the string}
  466.    if cod <> 0 then valu := 0;                       {set back to zero if error}
  467.   end; {with fd}
  468. end; {procedure getNfield}
  469. {------------------------------------------------------------------------------}
  470. procedure getLfield(var fd:field_des; var val:boolean);
  471. {return the boolean state in val}
  472.  
  473. begin
  474.  with fd do
  475.   begin
  476.    if f_typ <> 'L' then exit;
  477.    val := (f_ptr^[1] in ['T','t','Y','y']);
  478.   end; {with fd}
  479. end; {procedure getLfield}
  480. {------------------------------------------------------------------------------}
  481. procedure getDfield(var fd:field_des; var year,month,day:integer);
  482. var  dst : string[10];
  483.      nst : string[4];
  484.      cod : integer;  
  485. begin
  486.  with fd do
  487.   begin
  488.    if f_typ <> 'D' then exit;
  489.    getCfield(fd,dst);                        {get date as string}
  490.    nst := copy(dst,1,4);                     {get year}
  491.    val(nst,year,cod);
  492.    if cod <> 0 then exit;
  493.    nst := copy(dst,5,2);
  494.    val(nst,month,cod);
  495.    if cod <> 0 then exit;
  496.    nst := copy(dst,8,2);
  497.    val(nst,day,cod);
  498.   end; {with fd}
  499. end; {procedure getDfield}
  500. {------------------------------------------------------------------------------}
  501. procedure getMfield(var fd:field_des; var blkNum:longint);
  502. {get the block number in a memo field as a longint}
  503.  
  504. var  nst : string[10];
  505.      b   : byte absolute nst;
  506.      cod : integer;
  507.        
  508. begin
  509.  blkNum := 0;
  510.  with fd do
  511.   begin
  512.    if f_typ <> 'N' then exit;
  513.    getCfield(fd,nst);                                {get the field as a string}
  514.    while(nst[1] = ' ')AND(b>0) do delete(nst,1,1);   {delete leading spaces}
  515.    while(nst[b] = ' ')AND(b>0) do dec(b);            {trim trailing spaces}
  516.    val(nst, blkNum, cod);                            {evaluate the string}
  517.    if cod <> 0 then blkNum := 0;                     {set back to zero if error}
  518.   end; {with fd}
  519. end; {procedure getMfield}
  520. {------------------------------------------------------------------------------}
  521.  
  522. type charCel = record
  523.                 case integer of
  524.                 0:(c:char; a:byte);
  525.                 1:(b:byte; atr:byte);
  526.                 2:(w:word);
  527.                end;
  528.      hexByte = record
  529.                 hi : charCel;
  530.                 lo : charCel;
  531.                 sp : charCel;
  532.                end;
  533.      scrLine = record
  534.                 hexb  : array[0..15] of hexByte;
  535.                 spa   : array[0..9]  of charCel;
  536.                 litrl : array[0..15] of charCel;
  537.                 spb   : array[0..5]  of charCel;
  538.                end;
  539.  
  540.      screen = record
  541.               case integer of
  542.               0:(scrn : array[1..25] of scrLine);
  543.               1:(clr  : array[1..2000] of word);
  544.               2:(cc   : array[0..1999] of charCel);
  545.               end;
  546.  
  547.      pgraf = array[0..15] of byte;
  548.      pgp   = ^ pgraf;
  549.  
  550.      ptrstuf = record
  551.                 case integer of
  552.                 0:(p:pointer);
  553.                 1:(o:word; s:word);
  554.                 2:(a:pgp);
  555.                 end;
  556.  
  557.  
  558.  
  559.  
  560.  
  561. type bufr =  array[1..$fffe] of byte;
  562.      bp   = ^bufr;
  563.  
  564. const hexChr : array[0..15] of char =
  565. ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
  566.  
  567.  
  568. var  iofil  : file;
  569.      iobuf  : bp;
  570.      buflen : word;
  571.      bufpos : word;
  572. {------------------------------------------------------------------------------}
  573.  function getkey:word;
  574.  inline(
  575.         $B4/$00/   {mov ah,0}
  576.         $CD/$16    {int 16h}
  577.         );
  578. {------------------------------------------------------------------------------}
  579. procedure clrScr;
  580. inline(
  581.        $B8/>$B800/  {mov   ax,0b800h}
  582.        $8E/$C0/   {mov   es,ax}
  583.        $33/$FF/   {xor   di,di}
  584.        $B9/>$07D0/  {mov   cx,2000}
  585.        $B8/>$0720/  {mov   ax,0720h}
  586.        $9C/       {pushf}
  587.        $FC/       {cld}
  588.        $F3/$AB/   {rep   stoss}
  589.        $9D        {popf}
  590.        );
  591. {------------------------------------------------------------------------------}
  592. procedure Help;
  593. {show a help screen}
  594. begin
  595. clrscr;
  596. writeln('DBFFIX is a simple binary file editor that will also show the file as');
  597. writeln('a dBase III format header to allow repair of corrupted files in this format.');
  598. writeln('It is limited to files of 65530 bytes in length.');
  599. writeln('                                         ');
  600. writeln('EDITING KEYSTROKES                     | STANDARD KEYSTROKES');
  601. writeln('                                       | Keystokes that send an ascii code');
  602. writeln('F1: Display this help screen           | overwrite the active byte like this:');
  603. writeln('F2: Show the buffer as a dBase header  | ');
  604. writeln('    with the active field specification| On the Hex side:');
  605. writeln('    shown directly below the header    | 0-1 and A-F (case insensitive)');
  606. writeln('F5: Switch from Hex side to Ascii side | can be used to generate a 2 char hex');
  607. writeln('F10:Exit and save file                 | string.  If the string is not valid');
  608. writeln('Right Arrow: Move forward  1 byte      | you get a beep and nothing else');
  609. writeln('Left  Arrow: Move backward 1 byte      |');
  610. writeln('Up    Arrow: Move up a line            | On the Ascii side:');
  611. writeln('Down  Arrow: Move down a line          | The input ascii code overwrites');
  612. writeln('Page Up    : Move up 24 lines          | the buffer directly.  Alt-keypad');
  613. writeln('Page Down  : Move down 24 lines        | input works also.');
  614. writeln('alt X:       Exit without save.        |');
  615. writeln('Del:         Delete byte at cursor     | The active side is the red cursor.');
  616. writeln('Ins:         Insert nul byte at cursor |');
  617. writeln(^J'            <Hit any key to continue>');
  618. if getkey = 0 then ;
  619. end; {procedure Help}                    
  620. {------------------------------------------------------------------------------}
  621. procedure shoBufrAsHdr(p:bp; pos:word; startfield:integer);
  622. {show the buffer as a dbf header}
  623.  
  624. type hstruc = record
  625.                h : dbfHdr;
  626.                f : array[1..128] of dbfFldDes;
  627.               end;
  628.      hsptr = ^ hstruc;
  629.  
  630. var  hp : hsptr absolute p;
  631.      i  : integer;
  632.  
  633. begin
  634.  clrscr;
  635.  with hp^ do
  636.   begin
  637.    writeln('Version           : ',h.dBIIIvers);
  638.    writeln('Year              : ',h.lrevYear);
  639.    writeln('Month             : ',h.lrevMo);
  640.    writeln('Day               : ',h.lrevDay);
  641.    writeln('Number of records : ',h.numRecs);
  642.    writeln('Header Length     : ',h.hdrLen);
  643.    writeln('Record Length     : ',h.recLen);
  644.    for i := 0 to 3 do
  645.     begin
  646.      with f[i+startFIeld] do
  647.       begin
  648.        writeln('Field Name     : ',field_name);
  649.        writeln('Field Type     : ',field_typ);
  650.        writeln('Field Length   : ',f_len);
  651.        writeln('Decimal Places : ',dec_pl);
  652.       end; {with f[i+startFIeld]}
  653.     end; {for i := 0 to 3}
  654.   end; {with hp^}
  655. end; {procedure shoBufrAsHdr}
  656. {------------------------------------------------------------------------------}
  657. function getFile(s:string; var len:word; var p:bp):boolean;
  658. begin
  659.  getFile := false;
  660.  if pos('.',s) = 0 then s := s + '.dbf';
  661.  {$I-}
  662.  assign(iofil,s);
  663.  if ioresult <> 0 then exit;
  664.  reset(iofil,1);
  665.  if ioresult <> 0 then exit;
  666.  getmem(p,$fffe);
  667.  blockRead(iofil,p^,$fffe,len);
  668.  if ioresult <> 0 then exit;
  669.  getFile := true;
  670. end; {function getFile}
  671. {------------------------------------------------------------------------------}
  672. procedure paintBufr(base,pos:pointer;hside:boolean);
  673. {paint the buffer on the screen and mark "pos" in inverse video}
  674.  
  675.  
  676. var  b,p  : ptrstuf;
  677.      disp : screen absolute $b800:0;
  678.      i,j  : integer;
  679.  
  680. begin
  681.  for i := 1 to 2000 do disp.clr[i] := $1720;  {clear the screen}
  682.  b.p := base;
  683.  for i := 1 to 25 do
  684.   begin
  685.    for j := 0 to 15 do
  686.     begin
  687.      with disp.scrn[i] do
  688.       begin
  689.        hexb[j].hi.c := hexChr[b.a^[j] shr 4];
  690.        hexb[j].lo.c := hexChr[b.a^[j] and $0f];
  691.        litrl[j].b   := b.a^[j];
  692.       end; {with disp.scrn[i]}
  693.     end; {for j := 0 to 15}
  694.    inc(b.s);
  695.   end; {for i := 1 to 25}
  696.  b.p := base;
  697.  p.p := pos;
  698.  i := (p.s-b.s);
  699.  j := p.o-b.o;
  700.  i := i + j div 16;
  701.  j := j mod 16;
  702.  inc(i);
  703.  
  704.  if j < 0 then
  705.   begin
  706.    inc(j,16);
  707.    dec(i);
  708.   end; {if j < 0}
  709.  with disp.scrn[i] do
  710.   begin
  711.    if hside then
  712.     begin
  713.      hexb[j].hi.a := $40;
  714.      hexb[j].lo.a := $40;
  715.      litrl[j].a   := $1f;
  716.     end   {if hside}
  717.    else
  718.     begin
  719.      hexb[j].hi.a := $1f;
  720.      hexb[j].lo.a := $1f;
  721.      litrl[j].a   := $40;
  722.     end; {else for if hside}
  723.   end; {with disp.scrn[i]}
  724. end; {procedure paintBufr}
  725. {------------------------------------------------------------------------------}
  726. function HexEdit(buf:bp):byte;
  727. {allow edit of buffer in hex mode}
  728. {returns scan code of key that was hit to exit}
  729.  
  730. type ksparts = (asci,scan);
  731.  
  732. var bufpos   : word;
  733.     shoBase  : ptrStuf;
  734.     kw       : word;
  735.     kp       : array[ksparts] of byte absolute kw;
  736.     kc       : array[ksparts] of char absolute kw;
  737.     hside,ok : boolean;
  738.     poslin   : integer;
  739.     posDelt  : word;
  740.     hexinp   : array[1..2] of char;
  741.     i,j      : integer;
  742.  
  743. begin
  744.  for i := 1 to 24 do writeln;  {move cursor to bottom of screen}
  745.  posDelt := 0;
  746.  hside := true;
  747.  shoBase.p := buf;
  748.  bufpos := 1;
  749.  repeat
  750.   paintBufr(shoBase.p,@buf^[bufpos],hside);
  751.   kw := getkey;
  752.   hexEdit := kp[scan];
  753.   case kp[scan] of  {looking for scan codes to implement editing functions}
  754.    $3b : help;
  755.    $44 : exit;                                {exit on F10}
  756.    $3c : begin                                {F2 shows buffer as header}
  757.           shoBufrAsHdr(buf,bufPos,bufPos div 32);
  758.           kw := getkey;
  759.          end;
  760.    $2d : if kp[asci] = 0 then exit;           {also exit on alt X}
  761.    $3f : hside := not(hside);                 {toggle sides on F5}
  762.    $48 : if bufpos > 16 then dec(bufpos,16);  {up arrow goes up 16 bytes}
  763.    $50 : if buflen - bufpos > 16 then
  764.           inc(bufpos,16);                     {down arrow goes down 16}
  765.    $4b : if bufpos > 1 then dec(bufpos);      {left arrow goes back 1}
  766.    $4d : if bufpos < buflen then inc(bufpos); {right arrow goes forward 1}
  767.    $49 : if bufpos > 16*24 then               {page up moves up 24 lines}
  768.           begin
  769.            dec(bufpos,16*24);
  770.            dec(posDelt,24);
  771.           end; {if bufpos > 16*24}
  772.    $51 : if (buflen - bufpos) > 16*24 then    {page down goes forward 24 lines}
  773.           begin
  774.            inc(bufpos,16*24);
  775.            inc(posDelt,24);
  776.           end; {if (buflen - bufpos) > 16*24}
  777.    $53 : begin                                {del}
  778.           dec(bufLen);
  779.           move(buf^[bufpos+1],buf^[bufpos],bufLen-bufpos); {delete byte at bufpos}
  780.          end;
  781.    $52 : begin                                {ins}
  782.           move(buf^[bufpos],buf^[bufpos+1],bufLen-bufpos); {move up to insert}
  783.           inc(bufLen);
  784.           buf^[bufpos] := 0;                  {insert a nul at bufpos}
  785.          end;
  786.  
  787.    else if kp[asci] <> 0 then begin           {input keyboard character}
  788.           if hside then
  789.            begin
  790.             hexinp[1] := upcase(kc[asci]);    {accumulate chars in string}
  791.             kw := getkey;
  792.             hexinp[2] := upcase(kc[asci]);
  793.             ok := true;
  794.             for i := 1 to 2 do
  795.              begin
  796.               j := 0;
  797.               while (hexinp[i] <> hexChr[j])AND
  798.                     (j <= 15) do inc(j);
  799.               if j < 16 then hexinp[i] := chr(j)
  800.               else ok := false;
  801.              end; {for i := 1 to 2}
  802.             if ok then
  803.              begin
  804.               buf^[bufPos] := ord(hexinp[1]) shl 4 + ord(hexinp[2]);
  805.              end  {if ok}
  806.             else write(^G);       {beep on error}
  807.            end  {if hside}
  808.           else buf^[bufPos] := kp[asci];      {overwrite buffer with input keystroke}
  809.          end;
  810.   end; {case}
  811.   poslin := (bufpos - 1) div 16;
  812.   if poslin < posDelt then  posDelt := poslin;
  813.   if poslin > (posDelt + 24) then posDelt := poslin - 24;
  814.   shoBase.p := buf;
  815.   shoBase.s := shoBase.s + posDelt;
  816.  until false;
  817. end; {function HexEdit}
  818. {------------------------------------------------------------------------------}
  819. procedure writeFile(s:string; len:word; p:pointer);
  820. begin
  821.  close(iofil);
  822.  if pos('.',s) = 0 then s := s + '.dbf';
  823.  assign(iofil,s);
  824.  rewrite(iofil,1);
  825.  blockWrite(iofil,p^,len);
  826. end; {procedure writeFile}
  827. {------------------------------------------------------------------------------}
  828. begin
  829.  writeln('Copyright (c) 1992');
  830.  writeln('Quaker Hill Software, Inc.');
  831.  writeln('29 Colonel Wilkins Road');
  832.  writeln('Amherst, NH 03031');
  833.  writeln('603 672 5224');
  834.  writeln(^J'Released to the public domain 1992');
  835.  if paramcount < 2 then
  836.   begin
  837.    writeln(^J'USAGE');
  838.    writeln('DBFFIX inputfile outputfile');
  839.    halt(255);
  840.   end; {if paramcount < 2}
  841.  if getFile(paramstr(1),buflen,iobuf) then
  842.   begin
  843.    if hexedit(iobuf) = $44 then      {if exited on F10, write file}
  844.     writeFile(paramStr(2),buflen,iobuf);
  845.   end; {if getFile(paramstr(1),buflen,iobuf)}
  846.  {$I-}
  847.  close(iofil);
  848.  clrscr;
  849. end.
  850.  
  851.  
  852.                                          
  853.  
  854.  
  855.  
  856.  
  857.  
  858.  
  859.  
  860.  
  861.  
  862.  
  863.  
  864.  
  865.  
  866.  
  867.