MODBASE3.MOD 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158
  1. IMPLEMENTATION MODULE ModBase3;
  2. (*
  3. * ModBase
  4. * Release 3.0
  5. * (c) Copyright 1986 - 1991 Donald G. Fletcher
  6. * (c) Copyright 1986 - 1991 PMI
  7. * P.O. Box 8402
  8. * Green Bay Wi 53308
  9. * All Rights Reserved
  10. * August 6, 1987 - modifications to use Logitech 3.0
  11. *)
  12. (* This Module exports a type called DBFile which contains
  13. pertinent information concerning the structure of the dBase
  14. file. All operations on a dBase data file must specify this
  15. parameter usually as an "alias" using the first 2 or 3 letters
  16. of the dBase Filename. Date of Last Modification: April 16,
  17. 1987. Repertoire Input Output routines used.
  18. *)
  19. (* 5/11/88 added safety to modbase; if TRUE file integrity should
  20. be preserved as long as the power doesn't fail during a write.
  21. also added changes concerning memos to be finished later*)
  22. (* 6/88 Changed to add transparent handling of memo files *)
  23. (* 10/20/88 changes to add following:
  24. Automatic updating of dbindexes
  25. Made fieldlist a Pointer and only allocate as needed
  26. saving quite a bit of memory
  27. Added appending flag to speed append operations *)
  28. (* Logitech modules*)
  29. FROM M2Strings IMPORT
  30. Assign,Pos;
  31. FROM StrEdit IMPORT
  32. Append;
  33. FROM SYSTEM IMPORT
  34. BYTE,ADDRESS, ADR, TSIZE;
  35. (* Repertoire modules *)
  36. IMPORT
  37. EnvironUtils;
  38. FROM StringIO IMPORT
  39. ErrorMessage, NoError, PrintMessage;
  40. FROM HandleIO IMPORT
  41. BlockRead, BlockWrite, CloseHandle, OpenFile, SetFilePtr,
  42. CreateFile,UpdateDisk, FileOffSet,GetFilePtr;
  43. FROM LowLevel IMPORT
  44. Address8086, Fill, Move, AddAddr;
  45. FROM MiscFunctions IMPORT FieldNameChar,Alph;
  46. FROM Numbers IMPORT
  47. Min;
  48. FROM ErrorManager IMPORT
  49. WARN;
  50. FROM VStorage IMPORT
  51. DosAlloc, DosDealloc;
  52. IMPORT Locks,FAPI;
  53. IMPORT PosUtils;
  54. PROCEDURE DEALLOCATE(VAR loc:ADDRESS;size:CARDINAL);
  55. BEGIN
  56. DosDealloc(loc,size);
  57. END DEALLOCATE;
  58. PROCEDURE ALLOCATE(VAR loc:ADDRESS;size:CARDINAL);
  59. BEGIN
  60. DosAlloc(loc,size);
  61. END ALLOCATE;
  62. CONST
  63. Blank = " ";
  64. Null = 0C;
  65. EndOfHeader = 0DH;
  66. HdrLenPos = 8;
  67. DatePosition = 1; (* position of the first byte of the last update *)
  68. RecNumLoPos = 4;
  69. RecNumHiPos = 6;
  70. FieldNameLength = 10;
  71. InitCode =61353;
  72. one=VAL( LONGINT, 1 );
  73. (* ************************* EXPORTED PROCEDURES **************************)
  74. TYPE
  75. DBFileRec =
  76. RECORD
  77. fileID,
  78. MemoHandle: CARDINAL;
  79. open,
  80. MemoOpen,
  81. (* will open only if accessed *)
  82. Safety: BOOLEAN;
  83. (* if true keeps disk up to date *)
  84. autolock,
  85. exclusive,
  86. appending,
  87. hasmemo: BOOLEAN;
  88. recordmode:RecordModeType;
  89. fixup:FixUpProcedure;
  90. ErrorCode,
  91. Init,
  92. numberoffields: CARDINAL;
  93. lastupdate: ARRAY[0..2] OF CARDINAL;
  94. length: CARDINAL; (* of records in BYTES *)
  95. fieldlist: DBFieldPtr;
  96. numofrecords: LONGINT;
  97. currentrecnum: LONGINT;
  98. headerlength: CARDINAL;
  99. ReReadPtr,SavePtr,currentrec,BufferPtr: POINTER TO ARRAY
  100. [1..MaxRecLength] OF CHAR;
  101. name,
  102. MemoName: ARRAY [0..NameLen] OF CHAR;
  103. dbbuffer : ADDRESS;
  104. buffersize, (* requested size *)
  105. size : CARDINAL; (* size of buffer in bytes *)
  106. start : LONGINT; (* first record number *)
  107. MaxRecords, NumRecords : CARDINAL;
  108. IndexList:ADDRESS;
  109. END;
  110. DBFile=POINTER TO DBFileRec;
  111. PROCEDURE DBError( alias:DBFile):CARDINAL;
  112. BEGIN
  113. RETURN alias^.ErrorCode;
  114. END DBError;
  115. PROCEDURE FileName(alias:DBFile;VAR Name:ARRAY OF CHAR);
  116. BEGIN
  117. Assign(alias^.name,Name);
  118. END FileName;
  119. PROCEDURE SafetySet(alias:DBFile):BOOLEAN;
  120. BEGIN
  121. RETURN alias^.Safety;
  122. END SafetySet;
  123. PROCEDURE RecordLength(alias:DBFile):CARDINAL;
  124. BEGIN
  125. RETURN alias^.length;
  126. END RecordLength;
  127. PROCEDURE HasMemo(alias:DBFile):BOOLEAN;
  128. BEGIN
  129. RETURN alias^.hasmemo;
  130. END HasMemo;
  131. PROCEDURE NumberOfFields(alias:DBFile):CARDINAL;
  132. BEGIN
  133. RETURN alias^.numberoffields;
  134. END NumberOfFields;
  135. PROCEDURE Appending(alias:DBFile):BOOLEAN;
  136. BEGIN
  137. RETURN alias^.appending;
  138. END Appending;
  139. PROCEDURE RecordPtr(alias:DBFile):ADDRESS;
  140. BEGIN
  141. RETURN alias^.currentrec;
  142. END RecordPtr;
  143. PROCEDURE IndexList(alias:DBFile):ADDRESS;
  144. BEGIN
  145. RETURN alias^.IndexList;
  146. END IndexList;
  147. PROCEDURE SetIndexList(alias:DBFile;ndx:ADDRESS);
  148. BEGIN
  149. alias^.IndexList:=ndx;
  150. END SetIndexList;
  151. PROCEDURE Record(alias:DBFile):LONGINT;
  152. BEGIN
  153. RETURN alias^.currentrecnum;
  154. END Record;
  155. PROCEDURE FieldList(alias:DBFile):DBFieldPtr;
  156. BEGIN
  157. RETURN alias^.fieldlist;
  158. END FieldList;
  159. PROCEDURE BufferSize(alias:DBFile):CARDINAL;
  160. BEGIN
  161. RETURN alias^.buffersize;
  162. END BufferSize;
  163. PROCEDURE NumberRecords(alias:DBFile):LONGINT;
  164. BEGIN
  165. IF NOT alias^.exclusive THEN
  166. SetFilePtr( alias^.fileID, FromStart, VAL(LONGINT,4 ));
  167. PrintMessage(Locks.ReadRetry( alias^.fileID, ADR(alias^.numofrecords),
  168. 4,10,alias^.name ));
  169. END;
  170. RETURN alias^.numofrecords;
  171. END NumberRecords;
  172. PROCEDURE InitDBF(filename: ARRAY OF CHAR; VAR alias:
  173. DBFile; BufferSize:CARDINAL; safety,Exclusive,AutoLock:BOOLEAN;
  174. FixUp:FixUpProcedure );
  175. BEGIN
  176. NEW(alias);
  177. IF Exclusive THEN
  178. AutoLock:= FALSE
  179. END;
  180. WITH alias^ DO
  181. open:=FALSE;
  182. exclusive:=Exclusive OR Locks.ExclusiveOnly;
  183. autolock:=AutoLock;
  184. fixup:=FixUp;
  185. MemoOpen:=FALSE;
  186. Safety:=safety;
  187. Init:=InitCode;
  188. Assign(filename,name);
  189. fieldlist:=NIL;
  190. currentrec:=NIL;
  191. BufferPtr:=NIL;
  192. SavePtr:=NIL;
  193. ReReadPtr:=NIL;
  194. dbbuffer:=NIL;
  195. IndexList:=NIL;
  196. size:=0;
  197. buffersize:=BufferSize;
  198. recordmode:=CurrentRec;
  199. END;
  200. END InitDBF;
  201. PROCEDURE NilDBF(VAR alias:DBFile);
  202. BEGIN
  203. alias:=NIL;
  204. END NilDBF;
  205. PROCEDURE Initialized(alias:DBFile):BOOLEAN;
  206. BEGIN
  207. IF alias=NIL THEN RETURN FALSE END;
  208. IF alias^.Init=InitCode THEN
  209. RETURN TRUE
  210. END;
  211. RETURN FALSE;
  212. END Initialized;
  213. PROCEDURE DisposeDBF(VAR alias:DBFile);
  214. BEGIN
  215. IF alias=NIL THEN RETURN END;
  216. IF alias^.open THEN CloseDBF(alias) END;
  217. DISPOSE(alias);
  218. END DisposeDBF;
  219. PROCEDURE OpenMemo(alias:DBFile):BOOLEAN;
  220. BEGIN
  221. IF alias^.MemoOpen THEN RETURN TRUE END;
  222. alias^.MemoOpen:=OpenFile(alias^.MemoHandle,alias^.MemoName)=0;
  223. RETURN alias^.MemoOpen;
  224. END OpenMemo;
  225. PROCEDURE MemoHandle(alias:DBFile):CARDINAL;
  226. BEGIN
  227. RETURN alias^.MemoHandle;
  228. END MemoHandle;
  229. PROCEDURE SetDBSafetyOn( alias: DBFile);
  230. BEGIN
  231. UpdateDBFile(alias);
  232. alias^.Safety:=TRUE;
  233. END SetDBSafetyOn;
  234. PROCEDURE SetDBSafetyOff( alias: DBFile);
  235. BEGIN
  236. alias^.Safety:=FALSE;
  237. END SetDBSafetyOff;
  238. PROCEDURE SetRecordMode(alias: DBFile;Mode:RecordModeType);
  239. BEGIN
  240. IF alias^.recordmode=Mode THEN RETURN END;
  241. IF alias^.recordmode=CurrentRec
  242. THEN
  243. alias^.SavePtr:=alias^.currentrec;
  244. END;
  245. CASE Mode OF
  246. CurrentRec: alias^.currentrec:=alias^.SavePtr;
  247. |Buffer: alias^.currentrec:=alias^.BufferPtr;
  248. |ReRead: alias^.currentrec:=alias^.ReReadPtr
  249. END;
  250. alias^.recordmode:=Mode;
  251. END SetRecordMode;
  252. PROCEDURE OpenDBF
  253. (alias : DBFile ):BOOLEAN;
  254. VAR
  255. firstbyte : CHAR;
  256. i :CARDINAL;
  257. ActionTaken:CARDINAL;
  258. filemode:BITSET;
  259. PROCEDURE MakeDBFile;
  260. VAR
  261. dbh : ARRAY[ 0 .. MaxHeaderLen - 1 ] OF CHAR;
  262. PROCEDURE ReadDBHeader;
  263. (* HeaderLength must always be 32n+2 where n is a number equal to one
  264. more than the number of fields in the record *)
  265. BEGIN
  266. SetFilePtr( alias^.fileID, FromStart, VAL( LONGINT, HdrLenPos ) );
  267. PrintMessage( Locks.ReadRetry( alias^.fileID, ADR( alias^.headerlength ), 2 ,
  268. 10,alias^.name));
  269. SetFilePtr( alias^.fileID, FromStart, VAL( LONGINT, 0 ) );
  270. PrintMessage(Locks.ReadRetry( alias^.fileID, ADR( dbh ), alias^.headerlength,
  271. 10,alias^.name ));
  272. END ReadDBHeader;
  273. PROCEDURE GetLastUpdate;
  274. VAR
  275. i : CARDINAL;
  276. BEGIN
  277. FOR i := 0 TO 2 DO
  278. alias^.lastupdate[i] := ORD( dbh[1 + i] );
  279. END; (* for *)
  280. END GetLastUpdate;
  281. PROCEDURE GetNumberOfRecords;
  282. BEGIN
  283. Move( ADR( dbh[4] ), ADR( alias^.numofrecords ), 4 )
  284. END GetNumberOfRecords;
  285. PROCEDURE GetHeaderLength;
  286. BEGIN
  287. alias^.headerlength := ( ORD( dbh[9] ) * 100H ) + ORD( dbh[8] );
  288. END GetHeaderLength;
  289. PROCEDURE GetRecordLength;
  290. BEGIN
  291. alias^.length := ( ORD( dbh[11] ) * 100H ) + ORD( dbh[10] );
  292. END GetRecordLength;
  293. PROCEDURE GetFieldList;
  294. VAR
  295. j,
  296. k,
  297. fieldindex : CARDINAL;
  298. finished : BOOLEAN;
  299. PROCEDURE InitFieldList;
  300. BEGIN
  301. FOR j := 1 TO alias^.numberoffields DO
  302. Fill( ADR( alias^.fieldlist^[j].name ), FieldNameLength, 0C );
  303. alias^.fieldlist^[j].fldtype := " ";
  304. alias^.fieldlist^[j].size := 0;
  305. alias^.fieldlist^[j].decplaces := 0;
  306. alias^.fieldlist^[j].offset := 0;
  307. (* index position in CurrentRecord *)
  308. END; (* FOR *)
  309. END InitFieldList;
  310. BEGIN
  311. alias^.numberoffields:= (alias^.headerlength DIV 32)-1;
  312. ALLOCATE(alias^.fieldlist,
  313. (TSIZE(DBFieldArray)DIV MaxField)*(alias^.numberoffields));
  314. InitFieldList;
  315. alias^.fieldlist^[1].offset := 2;
  316. finished := FALSE;
  317. FOR j:=1 TO alias^.numberoffields DO
  318. fieldindex := ( j - 1 ) * 32;
  319. k := 0;
  320. Move( ADR( dbh[32 + fieldindex] ), ADR( alias^.fieldlist^[j].name ),
  321. FieldNameLength );
  322. alias^.fieldlist^[j].fldtype := dbh[43 + fieldindex];
  323. alias^.fieldlist^[j].size := ORD( dbh[48 + fieldindex] );
  324. (* Get fieldsize *)
  325. alias^.fieldlist^[j].decplaces := ORD( dbh[49 + fieldindex] );
  326. (* Get number of decimal places *)
  327. IF j > 1
  328. THEN
  329. alias^.fieldlist^[j].offset := alias^.fieldlist^[j - 1].offset +
  330. alias^.fieldlist^[j - 1].size;
  331. END; (* IF *)
  332. END; (* for *)
  333. END GetFieldList;
  334. BEGIN (* MakeDBFile *)
  335. ReadDBHeader;
  336. (* Fill out the Record *)
  337. GetNumberOfRecords;
  338. GetLastUpdate;
  339. GetRecordLength;
  340. GetHeaderLength;
  341. GetFieldList;
  342. END MakeDBFile;
  343. (* Procedure Description -- OpendBF -- Looks up a file with the parameter
  344. given as a name -- Checks to see if it is a dBaseIII type file --
  345. Creates a record of the type DBFile containing the pertinent information
  346. from the header of the dBase File -- If the file is already opened it
  347. does nothing *)
  348. BEGIN (* OpenDBF *)
  349. IF alias^.Init=InitCode
  350. THEN
  351. IF alias^.open
  352. THEN
  353. RETURN TRUE;
  354. END;
  355. ELSE
  356. WARN('UnInititalized DBFile In OpenDBF');
  357. END;
  358. IF alias^.exclusive THEN
  359. filemode:={1,4}
  360. ELSE
  361. filemode:={1,6} (* allow all *)
  362. END;
  363. alias^.ErrorCode := FAPI.DOSOPEN( ADR(alias^.name),
  364. ADR(alias^.fileID), ADR(ActionTaken), VAL(LONGINT,0), FAPI.FILE_NORMAL,
  365. CARDINAL({0}), CARDINAL(filemode), VAL(LONGINT,0) );
  366. IF alias^.ErrorCode # NoError
  367. THEN
  368. RETURN FALSE;
  369. END;
  370. IF (NOT alias^.exclusive) AND Locks.NoLocking(alias^.fileID) THEN
  371. alias^.exclusive:=TRUE
  372. END;
  373. (* make sure the file is a dBase file *)
  374. IF NoError # Locks.ReadRetry( alias^.fileID, ADR( firstbyte ), 1,10,alias^.name )
  375. THEN
  376. RETURN FALSE
  377. END;
  378. IF ( firstbyte = CHR( 03H ) )
  379. THEN
  380. alias^.hasmemo := FALSE;
  381. alias^.open := TRUE;
  382. ELSIF ( firstbyte = CHR( 83H ) )
  383. THEN
  384. alias^.hasmemo := TRUE;
  385. alias^.open := TRUE;
  386. ELSE
  387. alias^.ErrorCode := CloseHandle( alias^.fileID );
  388. alias^.open := FALSE;
  389. RETURN FALSE;
  390. (* WARN( "File is not a dBase III - type file -- proc-OpenDBF3" );*)
  391. END; (* IF *)
  392. (* construct a dBFileDesc *)
  393. IF alias^.open
  394. THEN
  395. MakeDBFile;
  396. IF alias^.autolock THEN
  397. ALLOCATE( alias^.ReReadPtr, alias^.length );
  398. END;
  399. ALLOCATE( alias^.currentrec, alias^.length );
  400. IF alias^.numofrecords=VAL(LONGINT,0)
  401. THEN
  402. alias^.currentrecnum:=VAL(LONGINT,0);
  403. ELSE
  404. alias^.currentrecnum:=VAL(LONGINT,1);
  405. END;
  406. alias^.NumRecords := 0 ;
  407. alias^.start := VAL( LONGINT, 0 );
  408. alias^.size := 0;
  409. SetDBBuffer(alias,alias^.buffersize);
  410. IF alias^.hasmemo THEN
  411. alias^.MemoOpen:=FALSE;
  412. Assign(alias^.name,alias^.MemoName);
  413. i:=Pos( ".", alias^.MemoName);
  414. IF i<=HIGH(alias^.MemoName) THEN
  415. alias^.MemoName[i]:=0C;
  416. END;
  417. Append(alias^.MemoName,'.DBT' );
  418. END;
  419. END; (* IF alias^.open *)
  420. RETURN TRUE;
  421. END OpenDBF;
  422. PROCEDURE SetDBBuffer(alias:DBFile ;BufferSize:CARDINAL );
  423. BEGIN
  424. alias^.buffersize:=BufferSize;
  425. IF alias^.open THEN
  426. IF alias^.size#0
  427. THEN
  428. DEALLOCATE(alias^.dbbuffer, alias^.size );
  429. END;
  430. (* calculate buffer size *)
  431. alias^.MaxRecords := BufferSize DIV alias^.length ;
  432. IF alias^.MaxRecords=0
  433. THEN
  434. alias^.MaxRecords:=1;
  435. END;
  436. alias^.size := alias^.MaxRecords *
  437. alias^.length;
  438. ALLOCATE( alias^.dbbuffer, alias^.size );
  439. alias^.start:=VAL(LONGINT,0);
  440. alias^.NumRecords:=0;
  441. IF alias^.numofrecords > VAL( LONGINT, 0 )
  442. THEN
  443. ReadDBRec( alias, alias^.currentrecnum );
  444. (* read the currentrecord *)
  445. ELSE
  446. alias^.BufferPtr:=alias^.dbbuffer;
  447. Fill( alias^.currentrec, alias^.length, 0C );
  448. Fill( alias^.BufferPtr, alias^.length, 0C );
  449. END (* if alias^.numofrecords *);
  450. END;
  451. END SetDBBuffer;
  452. PROCEDURE UpdateDBHeader( alias :DBFile );
  453. VAR
  454. month,
  455. day,
  456. year : CARDINAL;
  457. datestr : ARRAY[ 1 .. 3 ] OF CHAR;
  458. dumstr : ARRAY[ 0 .. 15 ] OF CHAR;
  459. lock:Locks.RangeRec;
  460. BEGIN
  461. IF NOT alias^.open
  462. THEN
  463. RETURN;
  464. END (* if not alias^.open *);
  465. IF NOT alias^.exclusive THEN
  466. lock.FileOffset := VAL( LONGINT,0);
  467. lock.RangeLength:=VAL(LONGINT,32);
  468. alias^.ErrorCode:=Locks.LockRetry(alias^.fileID,lock,10,alias^.name);
  469. IF alias^.ErrorCode#0 THEN WARN('Unable to lock in UpdateDBHeader') END;
  470. END;
  471. EnvironUtils.GetDate( month, day, year, dumstr, dumstr );
  472. alias^.numofrecords := NumberRecords(alias);
  473. SetFilePtr( alias^.fileID, FromStart, VAL( LONGINT, DatePosition ) );
  474. datestr[1] := CHR( year MOD 100 );
  475. datestr[2] := CHR( month );
  476. datestr[3] := CHR( day );
  477. alias^.ErrorCode := BlockWrite( alias^.fileID, ADR( datestr ), 3 );
  478. alias^.ErrorCode := BlockWrite( alias^.fileID, ADR( alias^.numofrecords ), 4 );
  479. alias^.ErrorCode := BlockWrite( alias^.fileID, ADR( alias^.headerlength ), 2 );
  480. IF NOT alias^.exclusive THEN
  481. alias^.ErrorCode:=Locks.UnLock(alias^.fileID,lock);
  482. IF alias^.ErrorCode#0 THEN WARN('Unable to Unlock in UpdateDBHeader') END;
  483. END;
  484. END UpdateDBHeader;
  485. PROCEDURE UpdateDBFile(alias :DBFile);
  486. BEGIN
  487. IF alias^.open
  488. THEN
  489. UpdateDBHeader(alias);
  490. UpdateDisk(alias^.fileID);
  491. IF alias^.MemoOpen
  492. THEN
  493. UpdateDisk(alias^.MemoHandle);
  494. END;
  495. END;
  496. END UpdateDBFile;
  497. PROCEDURE CloseDBF
  498. ( alias : DBFile );
  499. (* updates the header and closes the file *)
  500. BEGIN
  501. IF alias = NIL THEN
  502. RETURN
  503. END;
  504. IF NOT alias^.open
  505. THEN
  506. RETURN;
  507. END (* if not alias^.open *);
  508. UpdateDBHeader(alias);
  509. DEALLOCATE(alias^.fieldlist,
  510. (TSIZE(DBFieldArray)DIV MaxField)*(alias^.numberoffields));
  511. DEALLOCATE( alias^.dbbuffer, alias^.size );
  512. DEALLOCATE( alias^.currentrec, alias^.length );
  513. IF alias^.autolock THEN
  514. DEALLOCATE( alias^.ReReadPtr, alias^.length );
  515. END;
  516. alias^.ErrorCode := CloseHandle( alias^.fileID );
  517. IF alias^.MemoOpen
  518. THEN
  519. alias^.ErrorCode := CloseHandle( alias^.MemoHandle );
  520. alias^.MemoOpen := FALSE;
  521. END;
  522. alias^.open := FALSE;
  523. END CloseDBF;
  524. (*$O- *)
  525. PROCEDURE ReadDBRec
  526. (alias : DBFile;
  527. recnum : LONGINT );
  528. (* deposits the fetched string in the currentrec field of alias *)
  529. CONST
  530. one=VAL( LONGINT, 1 );
  531. VAR
  532. recordpos : LONGINT;
  533. i,
  534. temp :CARDINAL;
  535. long1,long2 :LONGINT;
  536. test :BOOLEAN;
  537. BEGIN
  538. IF NOT alias^.open
  539. THEN (* check to make sure the file is open *)
  540. WARN('DBF file not open in ReadDBFile');
  541. END;
  542. alias^.appending:=FALSE;
  543. (* compiler bug forced braking down *)
  544. long1:= recnum - alias^.start;
  545. long2:=VAL(LONGINT,alias^.NumRecords) - VAL(LONGINT,1);
  546. test:=(long1 >
  547. long2 );
  548. IF ( long1<VAL(LONGINT,0) ) OR test
  549. THEN (* is not in buffer *)
  550. IF ( recnum > alias^.numofrecords ) OR
  551. (recnum=VAL(LONGINT,0))
  552. THEN
  553. WARN( "Record number out of range in ReadDBRec" )
  554. END; (* IF *)
  555. IF VAL(LONGINT,alias^.MaxRecords) > alias^.numofrecords
  556. THEN (* underflow *)
  557. alias^.start := one;
  558. alias^.NumRecords := VAL(CARDINAL,alias^.numofrecords);
  559. ELSE
  560. alias^.NumRecords := alias^.MaxRecords;
  561. IF recnum < alias^.start
  562. THEN (* currec at top going down *)
  563. IF recnum > VAL(LONGINT,alias^.MaxRecords)
  564. THEN
  565. alias^.start := recnum - VAL(LONGINT,alias^.MaxRecords) + one;
  566. (* 2 to give 1 overlap ??*)
  567. ELSE
  568. alias^.start := one;
  569. END (* if recnum *);
  570. ELSE (* recnum at bottom going up*)
  571. IF ( recnum + VAL(LONGINT,alias^.MaxRecords) - one ) >
  572. alias^.numofrecords
  573. THEN
  574. alias^.start := alias^.numofrecords - VAL(LONGINT,alias^.MaxRecords)
  575. + one;
  576. ELSE
  577. alias^.start := recnum;
  578. END ;
  579. END ;
  580. END;
  581. recordpos := ( alias^.start - one ) *
  582. VAL( LONGINT,alias^.length ) + VAL( LONGINT, alias^.headerlength );
  583. SetFilePtr( alias^.fileID, FromStart, VAL( LONGINT, recordpos ) );
  584. PrintMessage( Locks.ReadRetry( alias^.fileID, alias^.dbbuffer ,
  585. alias^.length * alias^.NumRecords ,10,alias^.name ));
  586. END (* if *);
  587. recordpos:=recnum - alias^.start;
  588. i:=VAL( CARDINAL, recordpos );
  589. temp:=alias^.length * i;
  590. alias^.BufferPtr := AddAddr( alias^.dbbuffer, temp);
  591. (* Make copy of buffer *)
  592. Move(alias^.BufferPtr,alias^.currentrec,alias^.length);
  593. alias^.currentrecnum := recnum;
  594. alias^.appending:=FALSE;
  595. END ReadDBRec;(*$O= *)
  596. PROCEDURE CompareBlock( adr1,adr2:ADDRESS;size:CARDINAL):CARDINAL;
  597. VAR
  598. count:CARDINAL;
  599. p1,p2:Address8086;
  600. BEGIN
  601. count:=0;
  602. p1.a:=adr1;
  603. p2.a:=adr2;
  604. WHILE count<size
  605. DO
  606. IF p1.b^#p2.b^
  607. THEN
  608. RETURN count;
  609. END;
  610. INC(count);
  611. INC(p1.off);
  612. INC(p2.off);
  613. END (* while *);
  614. RETURN count;
  615. END CompareBlock;
  616. PROCEDURE WriteDBRec
  617. ( alias : DBFile );
  618. VAR
  619. lock:Locks.RangeRec;
  620. recordpos : LONGINT;
  621. NeedToWrite:BOOLEAN;
  622. BEGIN
  623. IF NOT alias^.open
  624. THEN (* check to make sure the file is open *)
  625. WARN('DBFile not open in WriteDBRec');
  626. END;
  627. IF alias^.appending THEN (* if appending we have to prepare*)
  628. IF NOT alias^.exclusive THEN
  629. lock.FileOffset := VAL( LONGINT,0);
  630. lock.RangeLength:=VAL(LONGINT,32);
  631. alias^.ErrorCode:=Locks.LockRetry(alias^.fileID,lock,10,alias^.name);
  632. IF alias^.ErrorCode#0 THEN WARN('Unable to lock in Header in WriteDBRec') END;
  633. SetFilePtr( alias^.fileID, FromStart, VAL(LONGINT,4 ));
  634. alias^.ErrorCode := BlockRead( alias^.fileID, ADR(alias^.numofrecords),
  635. 4 );
  636. END;
  637. INC( alias^.numofrecords );
  638. alias^.currentrecnum := alias^.numofrecords;
  639. alias^.start:=alias^.numofrecords;
  640. IF NOT alias^.exclusive THEN
  641. SetFilePtr( alias^.fileID, FromStart, VAL(LONGINT,4 ));
  642. alias^.ErrorCode := BlockWrite( alias^.fileID, ADR(alias^.numofrecords),
  643. 4 );
  644. END;
  645. IF alias^.Safety
  646. THEN
  647. UpdateDisk(alias^.fileID);
  648. END;
  649. END;
  650. IF alias^.autolock AND (LockRec(alias,alias^.currentrecnum)#0) THEN
  651. WARN('LockRec Failure in WriteDBRec')
  652. END;
  653. recordpos := ( alias^.currentrecnum - one ) * VAL( LONGINT,alias^.length
  654. ) + VAL( LONGINT, alias^.headerlength );
  655. IF alias^.appending THEN
  656. NeedToWrite:=TRUE;
  657. (* unlock header only after locking record *)
  658. IF NOT alias^.exclusive THEN
  659. alias^.ErrorCode:=Locks.UnLock(alias^.fileID,lock);
  660. IF alias^.ErrorCode#0 THEN WARN('Unable to unlock Header in WriteDBRec') END;
  661. END;
  662. ELSE
  663. NeedToWrite:=CompareBlock(alias^.currentrec,alias^.BufferPtr,alias^.length)<
  664. alias^.length;
  665. IF alias^.autolock AND NeedToWrite THEN
  666. SetFilePtr( alias^.fileID, FromStart, recordpos );
  667. PrintMessage( BlockRead( alias^.fileID, alias^.ReReadPtr ,
  668. alias^.length ));
  669. IF CompareBlock(alias^.ReReadPtr,alias^.BufferPtr,alias^.length)<
  670. alias^.length
  671. THEN
  672. NeedToWrite:=alias^.fixup(alias);
  673. (* we update BufferPtr so Update of indexes works *)
  674. Move(alias^.ReReadPtr,alias^.BufferPtr,alias^.length);
  675. ELSE
  676. NeedToWrite:=TRUE; (*File was not changed *)
  677. END;
  678. END;
  679. END (*alias^.appendinng*);
  680. IF NeedToWrite THEN
  681. (* Find the file position for the BEGINNING of the record *)
  682. SetFilePtr( alias^.fileID, FromStart, recordpos );
  683. (* Write the current record *)
  684. alias^.ErrorCode := BlockWrite( alias^.fileID, alias^.currentrec ,
  685. alias^.length );
  686. IF alias^.IndexList#NIL
  687. THEN (* most do this before modifying Buffer*)
  688. UpDateIndexes(alias);
  689. END;
  690. (* copy into buffer *)
  691. Move(alias^.currentrec,alias^.BufferPtr,alias^.length);
  692. IF alias^.Safety
  693. THEN
  694. UpdateDisk(alias^.fileID);
  695. IF alias^.MemoOpen
  696. THEN
  697. UpdateDisk(alias^.MemoHandle);
  698. END;
  699. END;
  700. END(* needtowrite*);
  701. IF alias^.autolock AND (UnLockRec(alias,alias^.currentrecnum)#0) THEN
  702. WARN('UnLockRec Failure in WriteDBF')
  703. END;
  704. alias^.appending:=FALSE;
  705. END WriteDBRec;
  706. PROCEDURE GetField
  707. ( alias : DBFile;
  708. fieldnumber : CARDINAL;
  709. VAR field : ARRAY OF CHAR );
  710. (* operates on the currently active record *)
  711. VAR
  712. limit : CARDINAL;
  713. BEGIN
  714. (* Find offset of field *)
  715. IF ( fieldnumber <= alias^.numberoffields ) AND ( fieldnumber > 0 )
  716. THEN
  717. WITH alias^.fieldlist^[fieldnumber] DO
  718. limit := Min( HIGH( field )+1, size );
  719. Move( ADR( alias^.currentrec^[ offset] ),
  720. ADR( field ), limit );
  721. END;
  722. IF ( limit <= HIGH( field ) )
  723. THEN
  724. field[limit] := 0C
  725. END (* if *);
  726. ELSE
  727. WARN('Ilegal field number in GetField');
  728. END; (* IF *)
  729. END GetField;
  730. PROCEDURE Replace(alias: DBFile; fieldnumber: CARDINAL;
  731. field: ARRAY OF CHAR);
  732. VAR j, k: CARDINAL;
  733. EndOfStr: BOOLEAN;
  734. BEGIN
  735. EndOfStr := FALSE;
  736. k := 0;
  737. WITH alias^.fieldlist^[fieldnumber] DO
  738. FOR j := (offset) TO
  739. (offset
  740. + size - 1) DO
  741. IF NOT EndOfStr THEN
  742. IF (k <= HIGH(field)) AND (field[k] # 0C) THEN
  743. alias^.currentrec^[j] := field[k];
  744. ELSE
  745. EndOfStr := TRUE;
  746. alias^.currentrec^[j] := ' ';
  747. END;
  748. ELSE
  749. alias^.currentrec^[j] := ' ';
  750. END;
  751. INC(k);
  752. END; (* FOR *)
  753. END;
  754. END Replace;
  755. PROCEDURE PosOfField(alias: DBFile; fieldname: ARRAY OF CHAR): CARDINAL;
  756. VAR i: CARDINAL;
  757. BEGIN
  758. i := 1;
  759. (* field names are null terminated *)
  760. WHILE (i <= alias^.numberoffields) AND (NOT PosUtils.Equal(fieldname,
  761. alias^.fieldlist^[i].name)) DO
  762. INC(i);
  763. END;
  764. IF i > alias^.numberoffields THEN
  765. i := 0;
  766. END;
  767. RETURN i;
  768. END PosOfField;
  769. (* $O- *)
  770. PROCEDURE AppendBlank
  771. ( alias : DBFile );
  772. BEGIN
  773. IF NOT alias^.open
  774. THEN (* check to make sure the file is open *)
  775. WARN('DBFile not open in Append Blank');
  776. END;
  777. (* set buffer values *)
  778. alias^.appending:=TRUE;
  779. alias^.NumRecords:=1;
  780. alias^.BufferPtr:=alias^.dbbuffer;
  781. alias^.currentrecnum:=MAX(LONGINT);
  782. (* Write Recordsize number of blanks *)
  783. Fill( alias^.currentrec, alias^.length, Blank );
  784. END AppendBlank;
  785. (* $O= *)
  786. PROCEDURE DeleteRecord
  787. ( alias : DBFile );
  788. BEGIN
  789. alias^.currentrec^[1] := '*';
  790. WriteDBRec( alias );
  791. END DeleteRecord;
  792. PROCEDURE UnDeleteRecord
  793. ( alias : DBFile );
  794. BEGIN
  795. alias^.currentrec^[1] := Blank;
  796. WriteDBRec( alias );
  797. END UnDeleteRecord;
  798. PROCEDURE Deleted
  799. ( alias : DBFile ) : BOOLEAN;
  800. BEGIN
  801. RETURN alias^.currentrec^[1] = '*';
  802. END Deleted;
  803. PROCEDURE LockRec(alias: DBFile;recnum:LONGINT):CARDINAL;
  804. VAR
  805. lock:Locks.RangeRec;
  806. BEGIN
  807. lock.FileOffset := ( recnum -one) *
  808. VAL( LONGINT,alias^.length ) + VAL( LONGINT, alias^.headerlength );
  809. lock.RangeLength:=VAL(LONGINT,alias^.length);
  810. RETURN Locks.LockRetry(alias^.fileID,lock,10,alias^.name);
  811. END LockRec;
  812. PROCEDURE UnLockRec(alias: DBFile;recnum:LONGINT):CARDINAL;
  813. VAR
  814. lock:Locks.RangeRec;
  815. BEGIN
  816. lock.FileOffset := ( recnum -one) *
  817. VAL( LONGINT,alias^.length ) + VAL( LONGINT, alias^.headerlength );
  818. lock.RangeLength:=VAL(LONGINT,alias^.length);
  819. RETURN Locks.UnLock(alias^.fileID,lock);
  820. END UnLockRec;
  821. PROCEDURE InitMemo(handle:CARDINAL) ;
  822. VAR
  823. buf:ARRAY[0..511] OF CHAR;
  824. i :CARDINAL;
  825. BEGIN
  826. FOR i:=1 TO 511 DO
  827. buf[i]:=0C;
  828. END (* for *);
  829. buf[0]:=1C;
  830. PrintMessage(BlockWrite(handle,ADR(buf),512));
  831. END InitMemo;
  832. PROCEDURE BuildDBF( fields: ARRAY OF
  833. DBFieldDescriptor;NumFields:CARDINAL; alias: DBFile):CARDINAL;
  834. (* THIS PROCEDURE WILL SILENTLY OVERWRITE ANY FILE WITH THE
  835. SAME NAME AS filename -- When used in a program, the
  836. existing files must be checked and an appropriate warning
  837. should be given. *)
  838. (* THIS PROCEDURE DOES NOT CLOSE THE CREATED FILE -- CloseDBF
  839. must be called to close the file *)
  840. (* The procedure is to read each field descriptor in the
  841. array until an illegal name (ie. any name that doesn't
  842. begin with a letter) is encountered) *)
  843. (* NOTE THAT FIELDNAMES IN DBASE3 ARE PADDED WITH 0C *)
  844. VAR month, day, year, i, j,
  845. ActionTaken, offset: CARDINAL;
  846. dumstr, zstr: ARRAY [1..50] OF CHAR;
  847. (* zstr is initialized to nulls (0C) and used with
  848. HandleIO.BlockWrite to write blanks to the
  849. file *)
  850. tmpchar: CHAR; (* used to write CHAR values with BlockWrite *)
  851. filemode:BITSET;
  852. longtmp: LONGINT; (* used to avoid Function Type Coercion *)
  853. BEGIN
  854. IF alias^.Init#InitCode
  855. THEN
  856. WARN('Unitalized DBF in DBCreate')
  857. END;
  858. alias^.exclusive:=TRUE;
  859. alias^.hasmemo := FALSE;
  860. alias^.length := 1; (* even with no fields, the length is 1 *)
  861. alias^.headerlength := 0;
  862. alias^.numofrecords := VAL(LONGINT,0);
  863. alias^.currentrecnum:= VAL(LONGINT,0);
  864. alias^.open := TRUE;
  865. EnvironUtils.GetDate( month, day, year, dumstr, dumstr );
  866. (* open exclusive *)
  867. (* create if the file does not exist; truncate if it does exist *)
  868. IF alias^.exclusive THEN
  869. filemode:={1,4}
  870. ELSE
  871. filemode:={1,6} (* allow all *)
  872. END;
  873. alias^.ErrorCode := FAPI.DOSOPEN( ADR(alias^.name),
  874. ADR(alias^.fileID), ADR(ActionTaken), VAL(LONGINT,0),
  875. FAPI.FILE_NORMAL, CARDINAL({1,4}), CARDINAL(filemode),
  876. VAL(LONGINT,0) );
  877. IF alias^.ErrorCode#0 THEN RETURN alias^.ErrorCode END;
  878. Fill(ADR(zstr),HIGH(zstr), 0C);
  879. alias^.ErrorCode := BlockWrite(alias^.fileID, ADR(zstr),32);
  880. (* Initialize the first 32 bytes of the header structure *)
  881. i := 0;
  882. offset := 2;
  883. WHILE (i <= HIGH(fields)) AND
  884. (i<NumFields) AND Alph(fields[i].name[0]) DO
  885. fields[i].offset := offset;
  886. FOR j := 0 TO 9 DO
  887. IF FieldNameChar(fields[i].name[j]) THEN
  888. alias^.ErrorCode := BlockWrite(alias^.fileID,
  889. ADR(fields[i].name[j]), 1);
  890. ELSE
  891. alias^.ErrorCode := BlockWrite(alias^.fileID,
  892. ADR(zstr), 1);
  893. END;
  894. END; (* for *)
  895. alias^.ErrorCode := BlockWrite(alias^.fileID, ADR(zstr),
  896. 1); (* puts a byte in the 11th space *)
  897. IF CAP(fields[i].fldtype) = 'M' THEN
  898. alias^.hasmemo := TRUE;
  899. fields[i].size := 10;
  900. ELSIF CAP(fields[i].fldtype) = 'L' THEN
  901. fields[i].size := 1;
  902. ELSIF CAP(fields[i].fldtype) = 'D' THEN
  903. fields[i].size := 8;
  904. ELSIF (CAP(fields[i].fldtype) # 'C') AND
  905. (CAP(fields[i].fldtype) # 'N') THEN
  906. WARN('Illegal type encountered in BuildDBF');
  907. END;
  908. tmpchar := CAP(fields[i].fldtype);
  909. alias^.ErrorCode := BlockWrite(alias^.fileID, ADR(tmpchar), 1);
  910. alias^.ErrorCode := BlockWrite(alias^.fileID, ADR(zstr), 4);
  911. alias^.ErrorCode := BlockWrite(alias^.fileID,
  912. ADR(fields[i].size), 1);
  913. offset := offset + fields[i].size;
  914. IF CAP(fields[i].fldtype) = 'N' THEN
  915. alias^.ErrorCode := BlockWrite(alias^.fileID,
  916. ADR(fields[i].decplaces), 1);
  917. ELSE
  918. alias^.ErrorCode := BlockWrite(alias^.fileID, ADR(zstr), 1)
  919. END;
  920. alias^.ErrorCode := BlockWrite(alias^.fileID, ADR(zstr), 14);
  921. alias^.length := alias^.length + fields[i].size;
  922. INC(i);
  923. END; (* while *)
  924. alias^.numberoffields := i ;
  925. IF NumFields<i THEN
  926. alias^.numberoffields:=NumFields
  927. END;
  928. ALLOCATE(alias^.fieldlist,
  929. (TSIZE(DBFieldArray)DIV MaxField)*(alias^.numberoffields));
  930. FOR i := 0 TO alias^.numberoffields-1 DO
  931. alias^.fieldlist^[i+1] := fields[i];
  932. END;
  933. tmpchar := CHR(0DH);
  934. alias^.ErrorCode := BlockWrite(alias^.fileID, ADR(tmpchar), 1);
  935. (* write the header terminator *)
  936. longtmp := GetFilePtr(alias^.fileID);
  937. alias^.headerlength := VAL(INTEGER,longtmp);
  938. (*
  939. alias^.headerlength := VAL(INTEGER,HandleIO.GetFilePtr(alias^.fileID));
  940. *)
  941. (* Next, set the first byte of the file to 03H or 83H *)
  942. SetFilePtr(alias^.fileID,FromStart,VAL(LONGINT,0));
  943. IF alias^.hasmemo THEN
  944. tmpchar := CHR(83H); (* the file has memo fields *)
  945. alias^.ErrorCode := BlockWrite(alias^.fileID, ADR(tmpchar), 1);
  946. Assign(alias^.name,alias^.MemoName);
  947. i:=Pos( ".", alias^.MemoName);
  948. IF i<=HIGH(alias^.MemoName) THEN
  949. alias^.MemoName[i]:=0C;
  950. END;
  951. Append(alias^.MemoName,'.DBT' );
  952. PrintMessage(CreateFile(alias^.MemoHandle,alias^.MemoName));
  953. alias^.MemoOpen:=TRUE;
  954. InitMemo(alias^.MemoHandle);
  955. ELSE
  956. tmpchar := 03C;
  957. alias^.ErrorCode := BlockWrite(alias^.fileID, ADR(tmpchar), 1);
  958. (* the file doesn't have memos *)
  959. END; (* if *)
  960. tmpchar := CHR(year MOD 100);
  961. alias^.ErrorCode := BlockWrite(alias^.fileID, ADR(tmpchar), 1);
  962. (* Write the year *)
  963. tmpchar := CHR(month);
  964. alias^.ErrorCode := BlockWrite(alias^.fileID, ADR(tmpchar), 1);
  965. (* Write the month *)
  966. tmpchar := CHR(day);
  967. alias^.ErrorCode := BlockWrite(alias^.fileID, ADR(tmpchar), 1);
  968. (* Write the day *)
  969. SetFilePtr(alias^.fileID, FromStart, VAL(LONGINT,8));
  970. alias^.ErrorCode := BlockWrite(alias^.fileID,
  971. ADR(alias^.headerlength), 2);
  972. alias^.ErrorCode := BlockWrite(alias^.fileID,
  973. ADR(alias^.length), 2);
  974. alias^.size:=0;
  975. ALLOCATE(alias^.currentrec,alias^.length);
  976. SetDBBuffer(alias,1);(* minnimum size *)
  977. RETURN 0;
  978. END BuildDBF;
  979. PROCEDURE DefaultFixUp(alias:DBFile):BOOLEAN;
  980. (* what we do here is determine if we want to write
  981. in which case we return TRUE. If we need to write we will
  982. have to fix up any conflicts in the changed data.
  983. Here is what the default does:
  984. The record is fixed up on a field by field basis as follows:
  985. If all same nochange.
  986. If currentrec field # current buffer field then use current.
  987. if currentrec field = current buffer field then use disk version;
  988. We write always.
  989. *)
  990. VAR
  991. field:ARRAY[CurrentRec..ReRead] OF ARRAY[0..(MaxField-1)] OF CHAR;
  992. RM:RecordModeType;
  993. fld:CARDINAL;
  994. BEGIN
  995. FOR fld:=1 TO alias^.numberoffields DO
  996. FOR RM:=CurrentRec TO ReRead DO
  997. SetRecordMode(alias,RM);
  998. GetField(alias,fld,field[RM]);
  999. END (*for*);
  1000. IF NOT PosUtils.Equal(field[ReRead],field[Buffer])
  1001. THEN (* disk and buffer copys are different so we have a problem*)
  1002. IF NOT PosUtils.Equal(field[ReRead],field[CurrentRec])
  1003. THEN (* is new the same as on the disk?*)
  1004. IF PosUtils.Equal(field[Buffer],field[CurrentRec])
  1005. THEN (* did this transaction actually changer the field?*)
  1006. (* if not set to disk version*)
  1007. SetRecordMode(alias,CurrentRec);
  1008. Replace(alias,fld,field[ReRead]);
  1009. END;
  1010. END;
  1011. END;
  1012. END(*for *);
  1013. SetRecordMode(alias,CurrentRec);
  1014. RETURN TRUE(* this version always does the write*)
  1015. END DefaultFixUp;
  1016. END ModBase3.
  1017.