NDXUTILS.MOD 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. IMPLEMENTATION MODULE NdxUtils;
  2. (*
  3. * REPERTOIRE
  4. * Release 1.6
  5. * By Charles Bradford and Cole Brecheen
  6. * (c) Copyright 1985-1992 PMI
  7. * Green Bay, Wisconsin
  8. * All rights reserved
  9. * (414) 468-6040
  10. *
  11. * $Header: D:/logfiles/mods/ndxutils.mov 1.7 10 Mar 1991 15:30:56 coleb $
  12. *
  13. *)
  14. (*EntryDiag:
  15. IMPORT Diagnostics;
  16. :EntryDiag*)
  17. (*
  18. IMPORT BuildLst;
  19. *)
  20. IMPORT EnvironUtils;
  21. IMPORT ErrorManager;
  22. IMPORT ErrorNames;
  23. IMPORT HandleIO;
  24. IMPORT GenLists;
  25. IMPORT ListUtils;
  26. IMPORT LowLevel;
  27. IMPORT M2Strings;
  28. IMPORT NdxBones;
  29. IMPORT NdxFiles;
  30. FROM NdxTypes IMPORT NdxElement;
  31. IMPORT NdxTypes;
  32. IMPORT Numbers;
  33. IMPORT NumTypes;
  34. IMPORT PosUtils;
  35. IMPORT StrConv;
  36. IMPORT StrEdit;
  37. IMPORT StringIO;
  38. IMPORT SYSTEM;
  39. IMPORT VStorage;
  40. VAR
  41. Initialized : BOOLEAN;
  42. PROCEDURE CopyMatchingFields( NdxF1: NdxTypes.NdxFileType; VAR
  43. NdxF2: NdxTypes.NdxFileType; RecName: ARRAY OF CHAR;
  44. MatchingFields: GenLists.GenList );
  45. VAR
  46. spot, TypeCode: CARDINAL;
  47. FieldName1, FieldName2: ARRAY [0..79] OF CHAR;
  48. BufElement: ARRAY [0..1023] OF CHAR;
  49. TmpList1, TmpList2: GenLists.GenList;
  50. PROCEDURE GetPair( index: CARDINAL; TheList: GenLists.GenList;
  51. VAR s1, s2: ARRAY OF CHAR );
  52. VAR
  53. eqspot, TypeCode: CARDINAL;
  54. BEGIN
  55. GenLists.GetElmt( TheList, index, s1, TypeCode );
  56. eqspot := PosUtils.Pos( '=', s1 );
  57. IF eqspot < HIGH(s1) THEN
  58. M2Strings.Copy( s1, eqspot + 1, (M2Strings.Length(s1) - eqspot) - 1,
  59. s2 );
  60. M2Strings.Delete( s1, eqspot, M2Strings.Length(s1) - eqspot );
  61. ELSE
  62. StrEdit.SetLength( s1, 0 );
  63. StrEdit.SetLength( s2, 0 );
  64. END;
  65. END GetPair;
  66. BEGIN (* CopyMatchingFields *)
  67. spot := 1;
  68. GetPair( spot, MatchingFields, FieldName1, FieldName2 );
  69. WHILE spot <= GenLists.ListLength( MatchingFields ) DO
  70. IF (M2Strings.Length(FieldName1) > 0) AND
  71. (M2Strings.Length(FieldName2) > 0) THEN
  72. IF NdxFiles.GetField( NdxF1, RecName, FieldName1,
  73. TypeCode, BufElement ) THEN
  74. IF TypeCode # GenLists.ListCode THEN
  75. IF NOT NdxFiles.PutField( NdxF2, RecName, FieldName2,
  76. TypeCode, BufElement ) THEN
  77. (* You could do something here to account
  78. for fields that do not exist in the
  79. destination structure *)
  80. END;
  81. ELSE
  82. (* copy lists before insertion *)
  83. IF NdxFiles.GetField( NdxF1, RecName, FieldName1,
  84. TypeCode, TmpList1 ) THEN
  85. GenLists.CopyList( TmpList1, TmpList2 );
  86. IF NOT NdxFiles.PutField( NdxF2, RecName, FieldName2,
  87. TypeCode, TmpList2 ) THEN
  88. (* You could do something here to account
  89. for fields that do not exist in the
  90. destination structure *)
  91. END;
  92. END;
  93. END;
  94. END;
  95. END;
  96. INC( spot );
  97. IF spot <= GenLists.ListLength(MatchingFields) THEN
  98. GetPair( spot, MatchingFields, FieldName1, FieldName2 );
  99. END;
  100. END;
  101. IF NOT NdxFiles.WriteRecord( NdxF2, RecName ) THEN
  102. (* You could do something here to account for records
  103. that could not be read from source file, so none of
  104. the GetFields above succeeded *)
  105. END;
  106. END CopyMatchingFields;
  107. PROCEDURE CopyRecord( RecName: ARRAY OF CHAR; SourceFile, DestFile:
  108. NdxTypes.NdxFileType ): BOOLEAN;
  109. VAR
  110. TypeCode: CARDINAL;
  111. TmpList1, TmpList2: GenLists.GenList;
  112. BEGIN
  113. IF NdxFiles.GetField( SourceFile, RecName, 'RECORD', TypeCode,
  114. TmpList1 ) THEN
  115. IF TypeCode = GenLists.ListCode THEN
  116. GenLists.CopyList( TmpList1, TmpList2 );
  117. IF NdxFiles.PutField( DestFile, RecName, 'RECORD', TypeCode,
  118. TmpList2 ) THEN
  119. IF NdxFiles.WriteRecord( DestFile, RecName ) THEN
  120. RETURN TRUE;
  121. END;
  122. END;
  123. END;
  124. END;
  125. RETURN FALSE;
  126. END CopyRecord;
  127. PROCEDURE TransferRecord( RecName: ARRAY OF CHAR; SourceFile, DestFile:
  128. NdxTypes.NdxFileType ): BOOLEAN;
  129. VAR
  130. TypeCode: CARDINAL;
  131. TmpList1, TmpList2: GenLists.GenList;
  132. BEGIN
  133. IF NdxFiles.GetField( SourceFile, RecName, 'RECORD', TypeCode,
  134. TmpList1 ) THEN
  135. IF TypeCode = GenLists.ListCode THEN
  136. GenLists.CopyList( TmpList1, TmpList2 );
  137. IF NdxFiles.PutField( DestFile, RecName, 'RECORD', TypeCode,
  138. TmpList2 ) THEN
  139. IF NdxFiles.WriteRecord( DestFile, RecName ) THEN
  140. IF NdxFiles.DeleteRecord( SourceFile, RecName ) THEN
  141. RETURN TRUE;
  142. END;
  143. END;
  144. END;
  145. END;
  146. END;
  147. RETURN FALSE;
  148. END TransferRecord;
  149. PROCEDURE ExportToFile( NdxFile: NdxTypes.NdxFileType; RecNameList:
  150. GenLists.GenList; fhandle: CARDINAL );
  151. (* exports records to a plain ascii file *)
  152. VAR
  153. lngth, cnt, TypeCode: CARDINAL;
  154. TmpStr: NdxTypes.RecNameStr;
  155. TmpList: GenLists.GenList;
  156. SearchingAll: BOOLEAN;
  157. TmpNdxRec: NdxTypes.NdxElement;
  158. BEGIN
  159. lngth := GenLists.ListLength( RecNameList );
  160. IF lngth = 0 THEN
  161. SearchingAll := TRUE;
  162. lngth := GenLists.ListLength( NdxFile^.Ndx );
  163. ELSE
  164. SearchingAll := FALSE;
  165. END;
  166. FOR cnt := 1 TO lngth DO
  167. IF SearchingAll THEN
  168. GenLists.GetElmt( NdxFile^.Ndx, cnt, TmpNdxRec, TypeCode );
  169. StrEdit.AssignStr( TmpNdxRec.RecName, TmpStr );
  170. ELSE
  171. GenLists.GetElmt( RecNameList, cnt, TmpStr, TypeCode );
  172. (*Put the cnt'th record name in TmpStr.*)
  173. END;
  174. IF (M2Strings.Length( TmpStr ) > 0) AND
  175. (0 # M2Strings.CompareStr( TmpStr, (*BuildLst.ListStorageRec*)
  176. 'qQqQqQqQ' )) THEN
  177. (*Next, get the entire record.*)
  178. IF NOT NdxFiles.GetField( NdxFile, TmpStr, 'RECORD', TypeCode,
  179. TmpList ) THEN
  180. StringIO.WriteStr( fhandle, TmpStr );
  181. StringIO.WriteEol( fhandle, ' is not a record in this file.' );
  182. END;
  183. (*We are assuming TypeCode will always = ListCode here.*)
  184. ListUtils.PrintList( fhandle, TmpList, 0, ',' );
  185. END;
  186. END;
  187. END ExportToFile;
  188. PROCEDURE SizeInfo( VAR NdxFile: NdxTypes.NdxFileType; VAR TotlSize,
  189. GbgSize: LONGINT; VAR PctUsed, NumDataRecs, NumGbgRecs:
  190. CARDINAL);
  191. VAR
  192. CardTotalRecs: CARDINAL;
  193. long2, long1, NdxElemSize, LongTotalRecs : LONGINT;
  194. (*We use these temporary variables to avoid bugs in
  195. Logitech's v3.03 compiler.*)
  196. BEGIN
  197. GarbageSize( NdxFile^.Ndx, GbgSize, NumDataRecs, NumGbgRecs);
  198. (* returns total size of garbage data and number of records *)
  199. CardTotalRecs := NumDataRecs + NumGbgRecs;
  200. NdxElemSize := Numbers.Lc( SYSTEM.TSIZE(NdxElement));
  201. LongTotalRecs := Numbers.Lc( CardTotalRecs);
  202. long1 := (NdxElemSize * LongTotalRecs);
  203. TotlSize := NdxFile^.NdxFilePtr + long1;
  204. INC( TotlSize, 6 );
  205. long2 := Numbers.Lc( NumGbgRecs);
  206. long1 := (NdxElemSize * long2);
  207. GbgSize := GbgSize + long1;
  208. (* add size of garbage index entries to garbage size of data *)
  209. long1 := GbgSize * Numbers.Lc( 100);
  210. long2 := long1 DIV TotlSize;
  211. PctUsed := 100 - Numbers.C( long2);
  212. END SizeInfo;
  213. PROCEDURE GetRecName( NdxFile: NdxTypes.NdxFileType; Which: CARDINAL;
  214. VAR RecName: ARRAY OF CHAR ): BOOLEAN;
  215. (* returns name of Which'th ndx element, or FALSE if not found *)
  216. VAR
  217. NdxEl : NdxTypes.NdxElement;
  218. TheType: CARDINAL;
  219. BEGIN
  220. NdxTypes.CheckInit( NdxFile);
  221. IF GenLists.ListLength( NdxFile^.Ndx) < Which THEN
  222. RETURN FALSE;
  223. END;
  224. GenLists.GetElmt( NdxFile^.Ndx, Which, NdxEl, TheType);
  225. StrEdit.AssignStr( NdxEl.RecName, RecName);
  226. RETURN M2Strings.Length(RecName) # 0;
  227. (* Return FALSE if index entry is marked as garbage *)
  228. END GetRecName;
  229. PROCEDURE GetRecNames( NdxFile: NdxTypes.NdxFileType;
  230. VAR NameList: GenLists.GenList );
  231. VAR
  232. TmpElmt: NdxTypes.NdxElement;
  233. lngth, cnt, TypeCode : CARDINAL;
  234. BEGIN
  235. GenLists.NewList( NameList );
  236. lngth := GenLists.ListLength( NdxFile^.Ndx );
  237. FOR cnt := 1 TO lngth DO
  238. GenLists.GetElmt( NdxFile^.Ndx, cnt, TmpElmt, TypeCode );
  239. StrEdit.CrunchBlanks( TmpElmt.RecName );
  240. IF (NOT PosUtils.Equal(TmpElmt.RecName, 'qQqQqQqQ'
  241. (*BuildLst.ListStorageRec*)) )
  242. AND (M2Strings.Length( TmpElmt.RecName ) > 0) THEN
  243. GenLists.ListInsert( TmpElmt.RecName, GenLists.StrCode,
  244. NameList, 65535 );
  245. END;
  246. END;
  247. END GetRecNames;
  248. PROCEDURE GarbageSize( VAR TheNdx: GenLists.GenList; VAR GbgSize:
  249. LONGINT; VAR NumDataRecs, NumGbgRecs: CARDINAL);
  250. VAR
  251. cnt, dum: CARDINAL;
  252. TheElmt: NdxTypes.NdxElement;
  253. long1: LONGINT;
  254. BEGIN
  255. GbgSize := NumTypes.L0;
  256. NumGbgRecs := 0;
  257. NumDataRecs := GenLists.ListLength( TheNdx);
  258. FOR cnt := 1 TO NumDataRecs DO
  259. GenLists.GetElmt( TheNdx, cnt, TheElmt, dum);
  260. IF TheElmt.UsedChars < TheElmt.Allocated THEN
  261. (* for every record with any gargbage in it *)
  262. dum := TheElmt.Allocated - TheElmt.UsedChars;
  263. (*We do this in steps because of bugs in Logitech's
  264. v3.03 LONGINTs.*)
  265. long1 := Numbers.Lc(dum);
  266. GbgSize := GbgSize + long1;
  267. IF TheElmt.UsedChars = 0 THEN
  268. INC( NumGbgRecs);
  269. END;
  270. END;
  271. END;
  272. DEC( NumDataRecs, NumGbgRecs);
  273. END GarbageSize;
  274. PROCEDURE ByFilePos(adr1: SYSTEM.ADDRESS; size1: CARDINAL;
  275. adr2: SYSTEM.ADDRESS; size2: CARDINAL): INTEGER;
  276. (*We pass this procedure to the procedure variable parameter
  277. of SortList to make it sort the Ndx according to position
  278. of the record in the file. Used only in CrunchNdxFile,
  279. but we can't make it local to that procedure because of
  280. an implementation restriction in Logitech's handling of
  281. procedure variables.*)
  282. VAR
  283. tmpsize: CARDINAL;
  284. tmp1, tmp2: NdxTypes.NdxElement;
  285. BEGIN
  286. tmpsize := SYSTEM.TSIZE( NdxElement );
  287. LowLevel.Move( adr1, SYSTEM.ADR(tmp1), tmpsize );
  288. LowLevel.Move( adr2, SYSTEM.ADR(tmp2), tmpsize );
  289. IF tmp1.FilePos > tmp2.FilePos THEN
  290. RETURN 1;
  291. ELSIF tmp1.FilePos < tmp2.FilePos THEN
  292. RETURN -1;
  293. ELSE
  294. RETURN 0;
  295. END;
  296. END ByFilePos;
  297. PROCEDURE ByFileName( adr1: SYSTEM.ADDRESS; size1: CARDINAL; adr2:
  298. SYSTEM.ADDRESS; size2: CARDINAL ): INTEGER;
  299. (*We pass this procedure to the procedure variable parameter
  300. of SortList to make it restore the Ndx to reverse
  301. alphabetical order.*)
  302. VAR
  303. tmpsize: CARDINAL;
  304. tmp1, tmp2: NdxTypes.NdxElement;
  305. BEGIN
  306. tmpsize := SYSTEM.TSIZE(NdxElement);
  307. LowLevel.Move( adr1, SYSTEM.ADR(tmp1), tmpsize );
  308. LowLevel.Move( adr2, SYSTEM.ADR(tmp2), tmpsize );
  309. RETURN - M2Strings.CompareStr( tmp1.RecName, tmp2.RecName );
  310. END ByFileName;
  311. PROCEDURE CrunchNdxFile( VAR NdxFile: NdxTypes.NdxFileType );
  312. (*Copies the file over itself.*)
  313. PROCEDURE MoveFileData( TheHandle: CARDINAL;
  314. FromSpot, ToSpot: LONGINT; HowMuch: CARDINAL);
  315. VAR
  316. BytesCopied, BufSize, ChunkSize : CARDINAL;
  317. bufadr: SYSTEM.ADDRESS;
  318. SavedMessage: CARDINAL;
  319. TmpL : LONGINT;
  320. BEGIN
  321. BytesCopied := 0;
  322. IF VStorage.DosAvail( HowMuch ) THEN
  323. BufSize := HowMuch;
  324. ELSE
  325. BufSize := EnvironUtils.MemAvail( 1000 );
  326. (*Guesses the amount of memory available to within 1000
  327. bytes.*)
  328. END;
  329. VStorage.DosAlloc( bufadr, BufSize );
  330. REPEAT
  331. IF (HowMuch - BytesCopied) > BufSize THEN
  332. ChunkSize := BufSize;
  333. ELSE
  334. ChunkSize := HowMuch - BytesCopied;
  335. END;
  336. TmpL := FromSpot + Numbers.Lc( BytesCopied);
  337. (*Necessary because of bugs in Logitech's LONGINT
  338. arithmetic that have persisted into v3.03.*)
  339. HandleIO.SetFilePtr( TheHandle, HandleIO.FromStart, TmpL );
  340. SavedMessage := HandleIO.BlockRead( TheHandle, bufadr, ChunkSize );
  341. CASE SavedMessage OF
  342. StringIO.NoError, StringIO.PartialRead, StringIO.EndOfFile:
  343. (*Do nothing.*);
  344. ELSE
  345. StringIO.PrintMessage( SavedMessage );
  346. (*Explains the error and aborts the program.*)
  347. END;
  348. TmpL := ToSpot + Numbers.Lc(BytesCopied);
  349. HandleIO.SetFilePtr( TheHandle, HandleIO.FromStart, TmpL );
  350. SavedMessage := HandleIO.BlockWrite( TheHandle, bufadr, ChunkSize );
  351. CASE SavedMessage OF
  352. StringIO.NoError, StringIO.PartialRead, StringIO.EndOfFile:
  353. (*Do nothing.*);
  354. ELSE
  355. StringIO.PrintMessage( SavedMessage );
  356. (*Explains the error and aborts the program.*)
  357. END;
  358. INC( BytesCopied, ChunkSize );
  359. UNTIL BytesCopied = HowMuch;
  360. VStorage.DosDealloc( bufadr, BufSize );
  361. END MoveFileData;
  362. VAR
  363. WritingSpot: LONGINT;
  364. RecordCnt, TypeCode : CARDINAL;
  365. TmpElmt: NdxTypes.NdxElement;
  366. ListReplaceNeeded : BOOLEAN;
  367. BEGIN
  368. (*CrunchNdxFile*)
  369. NdxTypes.CheckInit( NdxFile );
  370. IF GenLists.ListLength( NdxFile^.Ndx ) = 0 THEN
  371. (*empty file*)
  372. RETURN;
  373. END;
  374. HandleIO.UpdateDisk( NdxFile^.handle );
  375. GenLists.SortList( NdxFile^.Ndx, ByFilePos );
  376. RecordCnt := 1;
  377. GenLists.GetElmt(NdxFile^.Ndx, RecordCnt, TmpElmt, TypeCode);
  378. WritingSpot := TmpElmt.FilePos;
  379. (*Skip the structure record.*)
  380. ListReplaceNeeded := FALSE;
  381. WHILE RecordCnt <= GenLists.ListLength(NdxFile^.Ndx) DO
  382. (* We look at every element in the Ndx. *)
  383. GenLists.GetElmt(NdxFile^.Ndx, RecordCnt, TmpElmt, TypeCode);
  384. IF (M2Strings.Length(TmpElmt.RecName) > 0) THEN
  385. IF TmpElmt.FilePos > WritingSpot THEN
  386. (* If the record starts farther into the file than we think it's
  387. supposed to start. *)
  388. MoveFileData( NdxFile^.handle, TmpElmt.FilePos,
  389. WritingSpot, TmpElmt.UsedChars );
  390. (* In NdxFile^.handle, move TmpElmt.UsedChars from TmpElmt.FilePos
  391. to WritingSpot. *)
  392. TmpElmt.FilePos := WritingSpot;
  393. NdxFile^.UpdateNeeded := TRUE;
  394. ListReplaceNeeded := TRUE;
  395. ELSIF TmpElmt.Allocated > TmpElmt.UsedChars THEN
  396. (* We know that the amount of space allocated to this record is
  397. going to be reduced in the next loop, so we go ahead and update
  398. its entry in the Ndx. *)
  399. ListReplaceNeeded := TRUE;
  400. END;
  401. IF ListReplaceNeeded THEN
  402. TmpElmt.Allocated := TmpElmt.UsedChars;
  403. GenLists.ListReplace(TmpElmt, TypeCode, NdxFile^.Ndx, RecordCnt);
  404. ListReplaceNeeded := FALSE;
  405. END;
  406. WritingSpot := WritingSpot + Numbers.Lc( TmpElmt.UsedChars );
  407. (* Since we increment WritingSpot by TmpElmt.UsedChars instead of by
  408. TmpElmt.Allocated, when we look at the next record, its FilePos
  409. will be greater than WritingSpot, so we will crunch out small bits
  410. of garbage at the ends of records. *)
  411. INC( RecordCnt );
  412. ELSE
  413. (* The length of the RecName is 0, so we know it's a garbage record.*)
  414. GenLists.ListDelete( NdxFile^.Ndx, RecordCnt, 1 );
  415. NdxFile^.UpdateNeeded := TRUE;
  416. END;
  417. END; (*WHILE*)
  418. NdxFile^.NdxFilePtr := WritingSpot;
  419. (*Sets the position of the NdxMarker.*)
  420. IF GenLists.ListLength( NdxFile^.Ndx ) > 0 THEN
  421. GenLists.SortList( NdxFile^.Ndx, ByFileName );
  422. (*Put the Ndx list back into reverse alphabetical order.*)
  423. END;
  424. IF NdxFile^.UpdateNeeded THEN
  425. NdxFiles.WriteNdx( NdxFile );
  426. END;
  427. END CrunchNdxFile;
  428. PROCEDURE RebuildNdx( VAR NdxFile: NdxTypes.NdxFileType );
  429. (*Scans a file for record separators and builds a new linked
  430. list of record names, record sizes, and file offsets, then
  431. writes the list to the file, beginning at the last byte of
  432. the last record in the file. Used to repair damaged
  433. NdxFiles. *)
  434. CONST
  435. DuplicateNameMessage =
  436. 'Duplicate RecName found. Will try to make it unique';
  437. IrreparableMsg = 'NdxFile may be irreparably damaged';
  438. VAR
  439. RecFound, FileEndReached, NdxMarkFound, Garbage, FirstChar:
  440. BOOLEAN;
  441. bufch: CHAR;
  442. L4, ThisEndSpot, FileSize, BytesToRead, LastSpot, FileSpot: LONGINT;
  443. NdxElemSize, NestingLevel, cnt, BufSize, BufSpot, ZeroRecSize,
  444. ChunkSize, memory: CARDINAL;
  445. buf: LowLevel.Address8086;
  446. OldNdxElmt, NewNdxElmt: NdxTypes.NdxElement;
  447. TmpRecName: NdxTypes.RecNameStr;
  448. TmpStr: ARRAY [0..127] OF CHAR;
  449. PROCEDURE MakeUniqueName( InStr: ARRAY OF CHAR;
  450. VAR OutStr: ARRAY OF CHAR);
  451. VAR
  452. dumc: CARDINAL;
  453. TimeStr: ARRAY [0..30] OF CHAR;
  454. BEGIN
  455. StrEdit.AssignStr( InStr, OutStr );
  456. EnvironUtils.GetTime(dumc,dumc,dumc,dumc, TimeStr);
  457. StrEdit.DeleteChar( ':', TimeStr );
  458. StrEdit.DeleteChar( ' ', TimeStr );
  459. IF M2Strings.Length(OutStr) < (HIGH(OutStr) - 1) THEN
  460. M2Strings.Delete( TimeStr, 1, M2Strings.Length(TimeStr) - 2 );
  461. StrEdit.Append( OutStr, TimeStr );
  462. ELSE
  463. StrEdit.AssignStr( TimeStr, OutStr );
  464. END;
  465. END MakeUniqueName;
  466. PROCEDURE NextChar(): CHAR;
  467. VAR
  468. tmpch: CHAR;
  469. SavedMessage: CARDINAL;
  470. BEGIN
  471. IF FirstChar THEN
  472. (*We only want to allocate our buffer on the first
  473. call.*)
  474. FirstChar := FALSE;
  475. FileSpot := NumTypes.L0;
  476. (*FileSpot tracks our position in the file.*)
  477. BytesToRead := FileSize;
  478. IF BytesToRead > Numbers.Lc( 65000) THEN
  479. (*We make it a little less than 64K because the Storage
  480. module can't really allocate a full 64K block.*)
  481. BufSize := 65000;
  482. ELSE
  483. BufSize := Numbers.C( BytesToRead );
  484. END;
  485. memory := EnvironUtils.MemAvail( 5000 );
  486. (*Guess the amount of memory available, to within 5000
  487. bytes.*)
  488. IF memory < BufSize THEN
  489. BufSize := memory;
  490. END;
  491. VStorage.DosAlloc( buf.a, BufSize );
  492. BufSpot := BufSize;
  493. (*We do this so that we'll begin by doing a BlockRead,
  494. just as we would if we'd reached the end of a buffer.*)
  495. ChunkSize := BufSize;
  496. END;
  497. IF BytesToRead = NumTypes.L0 THEN
  498. FileEndReached := TRUE;
  499. RETURN 0C;
  500. END;
  501. IF (BufSpot >= ChunkSize) THEN
  502. IF BytesToRead < Numbers.Lc( BufSize) THEN
  503. (*If we have fewer bytes to read than will fit in the
  504. buffer.*)
  505. ChunkSize := Numbers.C( BytesToRead );
  506. END;
  507. HandleIO.SetFilePtr( NdxFile^.handle, HandleIO.FromStart, FileSpot );
  508. SavedMessage := HandleIO.BlockRead( NdxFile^.handle, buf.a,
  509. ChunkSize );
  510. (*This is where we actually load the buffer.*)
  511. IF (SavedMessage # StringIO.NoError)
  512. AND
  513. (SavedMessage # StringIO.PartialRead)
  514. AND
  515. (SavedMessage # StringIO.EndOfFile) THEN
  516. StringIO.PrintMessage( SavedMessage );
  517. END;
  518. BufSpot := 0;
  519. END;
  520. tmpch := LowLevel.PeekByte( buf.seg, buf.off + BufSpot );
  521. INC( BufSpot );
  522. INC( FileSpot );
  523. DEC( BytesToRead );
  524. IF BytesToRead = NumTypes.L0 THEN
  525. FileEndReached := TRUE;
  526. END;
  527. RETURN tmpch;
  528. END NextChar;
  529. PROCEDURE NextCard(): CARDINAL;
  530. VAR
  531. conv : RECORD
  532. (*Used in swap and BytesToWord.*)
  533. CASE : BOOLEAN OF
  534. TRUE :
  535. x : CARDINAL;
  536. | FALSE :
  537. a, b : CHAR;
  538. END;
  539. END;
  540. BEGIN
  541. conv.a := NextChar();
  542. conv.b := NextChar();
  543. RETURN conv.x;
  544. END NextCard;
  545. PROCEDURE NdxEntryInsert( TmpElmt: NdxTypes.NdxElement;
  546. VAR NdxFile: NdxTypes.NdxFileType ): BOOLEAN;
  547. VAR
  548. dummylong: LONGINT;
  549. TheElmt, dummycard: CARDINAL;
  550. BEGIN
  551. IF NOT NdxBones.FindRecord( NdxFile, TmpElmt.RecName, dummylong,
  552. dummycard, TheElmt) THEN
  553. (*The FindRecord returns the alphabetically correct
  554. insertion point, TheElmt.*)
  555. GenLists.ListInsert( TmpElmt, NdxTypes.NdxTypeCode, NdxFile^.Ndx,
  556. TheElmt);
  557. RETURN TRUE;
  558. ELSE
  559. RETURN FALSE;
  560. END;
  561. END NdxEntryInsert;
  562. PROCEDURE SetName( RecName: ARRAY OF CHAR;
  563. VAR OldNdxElmt: NdxTypes.NdxElement );
  564. BEGIN
  565. IF OldNdxElmt.FilePos = NumTypes.L0 THEN
  566. (* This makes sure that we don't try to set the name of
  567. the last record while we're processing the first
  568. record in the file.*)
  569. RETURN;
  570. END;
  571. StrEdit.AssignStr( RecName, OldNdxElmt.RecName );
  572. IF M2Strings.Length( RecName ) > 0 THEN
  573. WHILE NOT NdxEntryInsert(OldNdxElmt, NdxFile ) DO
  574. StrEdit.AssignStr( DuplicateNameMessage, TmpStr );
  575. M2Strings.Insert( ': ', TmpStr, 0 );
  576. M2Strings.Insert( RecName, TmpStr, 0 );
  577. ErrorManager.WARN( TmpStr );
  578. MakeUniqueName(OldNdxElmt.RecName, OldNdxElmt.RecName);
  579. StrEdit.AssignStr( OldNdxElmt.RecName, TmpStr );
  580. M2Strings.Insert( 'Trying "', TmpStr, 0 );
  581. StrEdit.Append( TmpStr, '". Please note new name' );
  582. ErrorManager.WARN( TmpStr );
  583. END;
  584. ELSE
  585. GenLists.ListInsert(OldNdxElmt,
  586. NdxTypes.NdxTypeCode, NdxFile^.Ndx, 65535);
  587. (*appends OldNdxElmt to end of Ndx*)
  588. END;
  589. END SetName;
  590. PROCEDURE SkipToNext();
  591. (* When something goes wrong in a record, we call this to try
  592. to skip ahead to the next thing that looks like the start
  593. of a record. It's not foolproof, but it ought to work
  594. most of the time.*)
  595. BEGIN
  596. StringIO.WriteEol( StringIO.outp, 'Skipping over this data:' );
  597. LOOP
  598. bufch := NextChar();
  599. StringIO.WriteStr( StringIO.outp, bufch );
  600. IF (bufch = NdxBones.CodeChar) THEN
  601. bufch := NextChar();
  602. IF bufch = NdxBones.NdxMarker[1] THEN
  603. NdxMarkFound := TRUE;
  604. EXIT;
  605. END;
  606. IF (bufch = NdxBones.StartSep[1]) AND
  607. (NextCard() = GenLists.ListCode) AND
  608. (NextChar() = NdxBones.CodeChar) AND
  609. (NextChar() = NdxBones.StartSep[1]) AND
  610. (NextCard() = GenLists.StrCode) THEN
  611. bufch := NextChar();
  612. IF (bufch = 'O') OR (bufch = 'G') THEN
  613. NestingLevel := 2;
  614. RecFound := TRUE;
  615. EXIT;
  616. END;
  617. END;
  618. END;
  619. END;
  620. END SkipToNext;
  621. PROCEDURE TruncateChosen(): BOOLEAN;
  622. VAR
  623. tmp: ARRAY [0..15] OF CHAR;
  624. BEGIN
  625. StringIO.WriteEol( StringIO.outp, '' );
  626. StringIO.WriteStr( StringIO.outp,
  627. 'Do you want to truncate the file here? ' );
  628. StringIO.ReadStr( StringIO.inp, tmp );
  629. IF CAP(tmp[0]) = 'Y' THEN
  630. NdxMarkFound := TRUE;
  631. RETURN TRUE;
  632. ELSE
  633. RETURN FALSE;
  634. END;
  635. END TruncateChosen;
  636. PROCEDURE MarkBad( VAR RecName: ARRAY OF CHAR );
  637. VAR
  638. TmpStr: ARRAY [0..79] OF CHAR;
  639. BEGIN
  640. StrConv.LongIntegerToStr( FileSpot, 0, TmpStr );
  641. StrEdit.Append( TmpStr,
  642. '= file offset. This record has been corrupted: "' );
  643. StrEdit.Append( TmpStr, RecName );
  644. StrEdit.Append( TmpStr, '"' );
  645. ErrorManager.WARN( TmpStr );
  646. StrEdit.SetLength( RecName, 0 );
  647. END MarkBad;
  648. BEGIN
  649. (*RebuildNdx*)
  650. NdxElemSize := SYSTEM.TSIZE(NdxElement);
  651. (*This gets us around a bug in the beta copy of v3.0.*)
  652. GenLists.NewList( NdxFile^.Ndx );
  653. (*Initialize the list in preparation for rebuilding it; we
  654. assume it hasn't yet been initialized.*)
  655. FileSize := HandleIO.FileLength( NdxFile^.handle );
  656. HandleIO.SetFilePtr( NdxFile^.handle, HandleIO.FromStart, NumTypes.L0 );
  657. LowLevel.Fill( SYSTEM.ADR(OldNdxElmt), NdxElemSize, 0C );
  658. LowLevel.Fill( SYSTEM.ADR(NewNdxElmt), NdxElemSize, 0C );
  659. StrEdit.SetLength( TmpRecName, 0 );
  660. NdxMarkFound := FALSE;
  661. FileEndReached := FALSE;
  662. FirstChar := TRUE;
  663. ZeroRecSize := NextCard();
  664. (*BufSpot should now be 2, because the byte at offset 2--the
  665. 3rd byte--is the next one to be read.*)
  666. IF (FileSize < NumTypes.L65535) AND
  667. (ZeroRecSize > Numbers.C( FileSize)) THEN
  668. ErrorManager.WARN( IrreparableMsg );
  669. END;
  670. FOR cnt := 1 TO ZeroRecSize DO
  671. (*Skip the zero record because it just contains the
  672. structure.*)
  673. bufch := NextChar();
  674. END;
  675. NestingLevel := 1;
  676. RecFound := FALSE;
  677. LastSpot := NumTypes.L0;
  678. ThisEndSpot := NumTypes.L0;
  679. WHILE (NOT FileEndReached) AND (NOT NdxMarkFound) DO
  680. (*Find the next CodeChar.*)
  681. bufch := NextChar();
  682. IF bufch = NdxBones.CodeChar THEN
  683. bufch := NextChar();
  684. (*find out what kind of separator this is*)
  685. IF bufch = NdxBones.StartSep[1] THEN
  686. INC( NestingLevel );
  687. bufch := NextChar();
  688. bufch := NextChar();
  689. (*Skip over the type code.*)
  690. IF NestingLevel = 2 THEN
  691. RecFound := TRUE;
  692. END;
  693. ELSIF bufch = NdxBones.EndSep[1] THEN
  694. DEC( NestingLevel );
  695. IF NestingLevel = 1 THEN
  696. ThisEndSpot := FileSpot;
  697. ELSIF NestingLevel = 0 THEN
  698. (*If NestingLevel gets back to 0 before we find the
  699. NdxMarker, we're done for.*)
  700. ErrorManager.WARN( IrreparableMsg );
  701. MarkBad( TmpRecName );
  702. IF NOT TruncateChosen() THEN
  703. SkipToNext();
  704. END;
  705. ThisEndSpot := FileSpot;
  706. END;
  707. ELSIF bufch = NdxBones.NdxMarker[1] THEN
  708. ThisEndSpot := FileSpot;
  709. NdxMarkFound := TRUE;
  710. ELSIF bufch = NdxBones.CodedStr[1] THEN
  711. (* This means there was a CodeChar embedded in the
  712. user's data. We just skip it. It will be decoded
  713. when it's read.*)
  714. ELSE
  715. StrConv.LongIntegerToStr( FileSpot, 0, TmpStr );
  716. M2Strings.Insert( '" ": illegal character at ', TmpStr, 0 );
  717. TmpStr[1] := bufch;
  718. ErrorManager.WARN( TmpStr );
  719. MarkBad( TmpRecName );
  720. IF NOT TruncateChosen() THEN
  721. SkipToNext();
  722. END;
  723. END;
  724. END;
  725. IF (NOT NdxMarkFound) AND RecFound THEN
  726. RecFound := FALSE;
  727. (*We've just read the next record StartSep.*)
  728. LowLevel.Fill( SYSTEM.ADR(OldNdxElmt), NdxElemSize, 0C );
  729. OldNdxElmt.FilePos := LastSpot;
  730. (* position saved when last start sep was found *)
  731. OldNdxElmt.Allocated := Numbers.C( FileSpot - LastSpot);
  732. DEC( OldNdxElmt.Allocated, 4 );
  733. (* position when this rec startsep found minus
  734. position saved when last rec start sep was found *)
  735. IF M2Strings.Length(TmpRecName) > 0 THEN
  736. OldNdxElmt.UsedChars := Numbers.C( ThisEndSpot - LastSpot );
  737. (*position when this rec endsep found minus
  738. position saved when last rec start sep was found *)
  739. ELSE
  740. OldNdxElmt.UsedChars := 0;
  741. END;
  742. L4 := Numbers.Lc( 4);
  743. (*We do this to avoid a bug in Logitech's v3.0
  744. LONGINTs.*)
  745. LastSpot := FileSpot - L4;
  746. SetName( TmpRecName, OldNdxElmt );
  747. bufch := NextChar(); bufch := NextChar();
  748. (*Skip over the StartSep that starts the record name.*)
  749. bufch := NextChar(); bufch := NextChar();
  750. (*Skip over the record name type code.*)
  751. StrEdit.SetLength(TmpRecName, 0);
  752. bufch := NextChar();
  753. Garbage := bufch = 'G';
  754. LOOP
  755. (*This is where we get the record name, we keep it in
  756. TmpRecName until we get to the start of the next
  757. record or to the NdxMarker. SetName then assigns
  758. TmpRecName to the RecName field of OldNdxElmt, taking
  759. care of any duplication of names.*)
  760. bufch := NextChar();
  761. IF FileEndReached THEN
  762. EXIT;
  763. END;
  764. IF bufch = NdxBones.CodeChar THEN
  765. bufch := NextChar();
  766. IF bufch = NdxBones.EndSep[1] THEN
  767. IF Garbage THEN
  768. StrEdit.SetLength( TmpRecName, 0 );
  769. END;
  770. EXIT;
  771. END;
  772. ELSE
  773. StrEdit.Append( TmpRecName, bufch );
  774. END;
  775. END;
  776. StrEdit.CrunchBlanks( TmpRecName );
  777. ELSIF NdxMarkFound THEN
  778. LowLevel.Fill( SYSTEM.ADR(OldNdxElmt), NdxElemSize, 0C );
  779. OldNdxElmt.FilePos := LastSpot;
  780. (* position saved when last start sep was found *)
  781. OldNdxElmt.Allocated := Numbers.C( FileSpot - LastSpot);
  782. DEC( OldNdxElmt.Allocated, 2 );
  783. (* position when this rec startsep found minus
  784. position saved when last rec start sep was found *)
  785. IF M2Strings.Length( TmpRecName ) > 0 THEN
  786. OldNdxElmt.UsedChars := Numbers.C( ThisEndSpot - LastSpot);
  787. (*position when this rec endsep found minus
  788. position saved when last rec start sep was found *)
  789. ELSE
  790. OldNdxElmt.UsedChars := 0;
  791. END;
  792. NdxFile^.NdxFilePtr := FileSpot - NumTypes.L2;
  793. SetName( TmpRecName, OldNdxElmt );
  794. END;
  795. END;(*WHILE (NOT FileEndReached) AND (NOT NdxMarkFound)*)
  796. IF (NOT NdxMarkFound) THEN
  797. IF LastSpot = NumTypes.L0 THEN
  798. ErrorNames.WarningName('NotNdx');
  799. ELSE
  800. NdxFile^.NdxFilePtr := LastSpot;
  801. END;
  802. END;
  803. VStorage.DosDealloc( buf.a, BufSize );
  804. NdxFile^.BufSizeNow := 0;
  805. NdxFiles.WriteNdx( NdxFile );
  806. END RebuildNdx;
  807. PROCEDURE AllowRebuild( VAR NdxFile: NdxTypes.NdxFileType );
  808. BEGIN
  809. ErrorManager.WARN( 'Damaged file. Will attempt to rebuild it' );
  810. RebuildNdx( NdxFile );
  811. END AllowRebuild;
  812. PROCEDURE Init();
  813. BEGIN
  814. IF Initialized THEN
  815. RETURN;
  816. ELSE
  817. Initialized := TRUE;
  818. END;
  819. (*EntryDiag:
  820. Diagnostics.Init();
  821. :EntryDiag*)
  822. EnvironUtils.Init();
  823. ErrorManager.Init();
  824. ErrorNames.Init();
  825. HandleIO.Init();
  826. GenLists.Init();
  827. ListUtils.Init();
  828. LowLevel.Init();
  829. M2Strings.Init();
  830. NdxBones.Init();
  831. NdxFiles.Init();
  832. NdxTypes.Init();
  833. Numbers.Init();
  834. NumTypes.Init();
  835. PosUtils.Init();
  836. StrConv.Init();
  837. StrEdit.Init();
  838. StringIO.Init();
  839. VStorage.Init();
  840. (*EntryDiag:
  841. Diagnostics.diagS( 'Entering NdxUtils', '' );
  842. :EntryDiag*)
  843. NdxBones.RebuildProc := AllowRebuild;
  844. (*EntryDiag:
  845. Diagnostics.diagS( 'Exiting NdxUtils', '' );
  846. :EntryDiag*)
  847. END Init;
  848. BEGIN
  849. Initialized := FALSE;
  850. Init();
  851. END NdxUtils.