POSUTILS.MOD 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. IMPLEMENTATION MODULE PosUtils;
  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/posutils.mov 1.6 10 Mar 1991 15:31:30 coleb $
  12. *
  13. *)
  14. IMPORT LowLevel;
  15. IMPORT M2Strings;
  16. IMPORT Numbers;
  17. IMPORT StrEdit;
  18. IMPORT SYSTEM;
  19. VAR
  20. Initialized : BOOLEAN;
  21. PROCEDURE Init();
  22. BEGIN
  23. IF Initialized THEN
  24. RETURN;
  25. ELSE
  26. Initialized := TRUE;
  27. END;
  28. LowLevel.Init();
  29. M2Strings.Init();
  30. Numbers.Init();
  31. StrEdit.Init();
  32. CaseSensitive := FALSE;
  33. END Init;
  34. CONST
  35. space = ' ';
  36. tab = CHAR(9);
  37. PROCEDURE BreakPoint(VAR thestr : ARRAY OF CHAR; maxlength : CARDINAL;
  38. breakchars : ARRAY OF CHAR) : CARDINAL;
  39. VAR
  40. cnt : CARDINAL;
  41. BEGIN
  42. cnt := maxlength-1;
  43. WHILE (cnt>0) AND (Pos(thestr[cnt],breakchars)>HIGH(breakchars)) DO
  44. DEC(cnt);
  45. END;
  46. IF (cnt=0) AND (Pos(thestr[0],breakchars)>HIGH(breakchars)) THEN
  47. RETURN (maxlength);
  48. ELSE
  49. RETURN (cnt);
  50. END;
  51. END BreakPoint;
  52. PROCEDURE BreakPtAdr( TheAdr: SYSTEM.ADDRESS; TheSize: CARDINAL;
  53. maxlength : CARDINAL; breakchars : ARRAY OF CHAR) :
  54. CARDINAL;
  55. VAR
  56. cnt : CARDINAL;
  57. tmp : LowLevel.Address8086;
  58. BEGIN
  59. cnt := maxlength-1;
  60. tmp.a := TheAdr;
  61. INC( tmp.off, cnt );
  62. WHILE (cnt>0) AND (NOT Present(tmp.b^, breakchars)) DO
  63. DEC( tmp.off );
  64. DEC(cnt);
  65. END;
  66. IF (cnt=0) AND (NOT Present(tmp.b^, breakchars)) THEN
  67. RETURN maxlength;
  68. ELSE
  69. RETURN cnt;
  70. END;
  71. END BreakPtAdr;
  72. PROCEDURE ByteCount( size: CARDINAL; lookfor: CHAR; start:
  73. SYSTEM.ADDRESS): CARDINAL;
  74. VAR
  75. cnt, offset, skipped: CARDINAL;
  76. BEGIN
  77. cnt := 0;
  78. offset := 0;
  79. WHILE offset < size DO
  80. skipped := LowLevel.ScanEQ( size - offset, lookfor,
  81. LowLevel.AddAddr(start, offset) );
  82. IF skipped < (size - offset) THEN
  83. INC( cnt );
  84. INC( offset, skipped + 1 );
  85. ELSE
  86. RETURN cnt;
  87. END;
  88. END;
  89. RETURN cnt;
  90. END ByteCount;
  91. PROCEDURE Equal( str1, str2: ARRAY OF CHAR ): BOOLEAN;
  92. BEGIN
  93. RETURN M2Strings.CompareStr(str1, str2) = 0;
  94. END Equal;
  95. PROCEDURE FindNthPattern(pattern: ARRAY OF CHAR; which: CARDINAL;
  96. searchIn: ARRAY OF CHAR): CARDINAL;
  97. VAR
  98. cnt, lastSpot, inLngth: CARDINAL;
  99. found : BOOLEAN;
  100. BEGIN
  101. found := FALSE;
  102. lastSpot := 0;
  103. cnt := 0;
  104. inLngth := M2Strings.Length(searchIn);
  105. REPEAT
  106. lastSpot := Positn(pattern, searchIn, lastSpot);
  107. IF lastSpot >= inLngth THEN
  108. cnt := which + 1;
  109. ELSE
  110. INC(cnt);
  111. END;
  112. IF cnt = which THEN
  113. found := TRUE;
  114. END;
  115. UNTIL found OR (cnt > which);
  116. IF cnt > which THEN
  117. RETURN(65535);
  118. ELSE
  119. RETURN(lastSpot);
  120. END;
  121. END FindNthPattern;
  122. PROCEDURE FirstNonMatching(TheChar : CHAR; VAR TheStr : ARRAY OF CHAR) :
  123. CHAR;
  124. (* Returns the first character in TheStr that does not match
  125. TheChar. If none found, returns TheChar. *)
  126. VAR
  127. spot, leng : CARDINAL;
  128. BEGIN
  129. leng := M2Strings.Length(TheStr);
  130. IF leng = 0 THEN
  131. RETURN TheChar;
  132. END;
  133. spot := LowLevel.ScanNE( leng, TheChar, SYSTEM.ADR(TheStr) );
  134. IF spot < leng THEN
  135. RETURN TheStr[spot];
  136. ELSE
  137. RETURN TheChar;
  138. END;
  139. END FirstNonMatching;
  140. PROCEDURE IsAlphaChar( TheChar: CHAR ): BOOLEAN;
  141. BEGIN
  142. RETURN ((TheChar >= 'a') AND (TheChar <= 'z')) OR ((TheChar
  143. >= 'A') AND (TheChar <= 'Z'));
  144. END IsAlphaChar;
  145. PROCEDURE IsBlank(TheStr : ARRAY OF CHAR) : BOOLEAN;
  146. (*TRUE if thestr is all blanks *)
  147. VAR
  148. answer, leng : CARDINAL;
  149. BEGIN
  150. leng := M2Strings.Length(TheStr);
  151. IF leng=0 THEN
  152. RETURN TRUE;
  153. END;
  154. answer := LowLevel.ScanNE(leng,space,SYSTEM.ADR(TheStr));
  155. RETURN (answer>=leng);
  156. END IsBlank;
  157. PROCEDURE IsNextPattern( Pattern: ARRAY OF CHAR; VAR TheStr:
  158. ARRAY OF CHAR; StartingSpot: CARDINAL ): BOOLEAN;
  159. VAR
  160. tmp: CARDINAL;
  161. lngth: INTEGER;
  162. tmpadr: LowLevel.Address8086;
  163. BEGIN
  164. lngth := INTEGER(M2Strings.Length(TheStr)) - INTEGER(StartingSpot);
  165. (* Don't bother looking if the StartingSpot is greater than the
  166. length of the string. *)
  167. IF lngth < 0 THEN
  168. RETURN FALSE;
  169. END;
  170. tmp := PosAdr( Pattern, LowLevel.AddAddr( SYSTEM.ADR(TheStr),
  171. StartingSpot), CARDINAL(lngth) );
  172. (* Figure out where Pattern appears, if at all. *)
  173. IF tmp = CARDINAL(lngth) THEN
  174. (* If it doesn't appear at all, return FALSE. *)
  175. RETURN FALSE;
  176. END;
  177. tmpadr.a := SYSTEM.ADR(TheStr);
  178. INC( tmpadr.off, StartingSpot );
  179. (* tmp now represents the offset beyond StartingSpot at which
  180. Pattern appears. We want to know if that's the same offset
  181. as the first nonblank character beyond StartingSpot. *)
  182. RETURN tmp = CARDINAL(LowLevel.ScanNE( lngth, space, tmpadr.a ));
  183. END IsNextPattern;
  184. PROCEDURE IsNumber( TheStr: ARRAY OF CHAR; StartingSpot:
  185. CARDINAL ): BOOLEAN;
  186. VAR
  187. AllNumbers, PointFound, AtLeastOneFound : BOOLEAN;
  188. EndStr, index : CARDINAL;
  189. bufch : CHAR;
  190. BEGIN
  191. M2Strings.Delete( TheStr, 0, StartingSpot );
  192. StrEdit.CrunchBlanks( TheStr );
  193. EndStr := M2Strings.Length(TheStr);
  194. IF (EndStr = 0) OR Present( space, TheStr ) THEN
  195. RETURN FALSE;
  196. END;
  197. DEC( EndStr );
  198. index := 0;
  199. AtLeastOneFound := FALSE;
  200. PointFound := FALSE;
  201. AllNumbers := TRUE;
  202. REPEAT
  203. bufch := TheStr[index];
  204. IF IsNumericChar( bufch ) THEN
  205. AtLeastOneFound := TRUE;
  206. ELSIF (bufch = '-') OR (bufch = '+') THEN
  207. IF NOT (index = 0) THEN
  208. AllNumbers := FALSE;
  209. END;
  210. ELSIF bufch = '.' THEN
  211. IF PointFound THEN
  212. AllNumbers := FALSE;
  213. ELSE
  214. PointFound := TRUE;
  215. END;
  216. ELSE
  217. AllNumbers := FALSE;
  218. END;
  219. INC( index );
  220. UNTIL (NOT AllNumbers) OR (index > EndStr);
  221. RETURN AtLeastOneFound AND AllNumbers;
  222. END IsNumber;
  223. PROCEDURE IsNumericChar( TheChar: CHAR ): BOOLEAN;
  224. BEGIN
  225. RETURN (TheChar >= '0') AND (TheChar <= '9');
  226. END IsNumericChar;
  227. PROCEDURE IsWhiteSpace( TheChar: CHAR ): BOOLEAN;
  228. BEGIN
  229. RETURN (TheChar = space) OR (TheChar = tab);
  230. END IsWhiteSpace;
  231. PROCEDURE LastNonMatching(TheChar : CHAR; VAR TheStr
  232. : ARRAY OF CHAR) : CHAR;
  233. (* Starts at the end of TheStr and searches backward for a
  234. character that does not match TheChar. If none found,
  235. returns TheChar. *)
  236. VAR
  237. spot: INTEGER;
  238. BEGIN
  239. spot := M2Strings.Length(TheStr);
  240. IF spot = 0 THEN
  241. RETURN TheChar;
  242. ELSE
  243. DEC( spot );
  244. END;
  245. WHILE (TheStr[spot] = TheChar) AND (spot > 0) DO
  246. DEC( spot );
  247. END;
  248. IF spot > 0 THEN
  249. RETURN TheStr[spot];
  250. ELSE
  251. RETURN TheChar;
  252. END;
  253. END LastNonMatching;
  254. PROCEDURE MatchingPos( spot: CARDINAL; TheStr: ARRAY
  255. OF CHAR ): CARDINAL;
  256. (*Returns the position of the ending character that matches
  257. the beginning character at position spot within TheStr. If
  258. TheStr does not have a valid beginning character at spot,
  259. or if spot is not inside the present length of TheStr, or
  260. if no matching ending character is found, MatchingPos
  261. returns spot.*)
  262. VAR
  263. TmpSpot, StackCount, lngth : CARDINAL;
  264. Opener, Closer, bufch: CHAR;
  265. Openers, Closers: ARRAY [0..3] OF CHAR;
  266. BEGIN
  267. StrEdit.AssignStr( '[{(<', Openers );
  268. StrEdit.AssignStr( ']})>', Closers );
  269. lngth := M2Strings.Length( TheStr );
  270. IF spot >= lngth THEN
  271. RETURN spot;
  272. END;
  273. bufch := TheStr[spot];
  274. TmpSpot := Pos( bufch, Openers );
  275. IF TmpSpot <= HIGH( Openers ) THEN
  276. Opener := bufch;
  277. Closer := Closers[ TmpSpot ];
  278. TmpSpot := spot + 1;
  279. StackCount := 0;
  280. WHILE TmpSpot < lngth DO
  281. IF TheStr[ TmpSpot ] = Opener THEN
  282. INC( StackCount );
  283. ELSIF TheStr[ TmpSpot ] = Closer THEN
  284. IF StackCount = 0 THEN
  285. RETURN TmpSpot;
  286. ELSE
  287. DEC( StackCount );
  288. END;
  289. END;
  290. INC( TmpSpot );
  291. END;
  292. END;
  293. RETURN spot;
  294. END MatchingPos;
  295. PROCEDURE PatternScan( PatternAdr: SYSTEM.ADDRESS;
  296. PatternSize: CARDINAL; ScanAreaAdr: SYSTEM.ADDRESS;
  297. ScanAreaSize: CARDINAL ): CARDINAL;
  298. VAR
  299. BytesSkipped, ThisScan, cnt, PossibleStartSpots : CARDINAL;
  300. MaxScan: INTEGER;
  301. PatAdr, AreaAdr: LowLevel.Address8086;
  302. BEGIN
  303. PatAdr.a := PatternAdr;
  304. AreaAdr.a := ScanAreaAdr;
  305. IF (ScanAreaSize >= PatternSize) AND (PatternSize#0) THEN
  306. PossibleStartSpots := ScanAreaSize - PatternSize + 1;
  307. IF PossibleStartSpots > 32766 THEN
  308. MaxScan := 32766;
  309. ELSE
  310. MaxScan := PossibleStartSpots;
  311. END;
  312. BytesSkipped := LowLevel.ScanEQ( MaxScan, PatAdr.b^, ScanAreaAdr );
  313. ThisScan := BytesSkipped;
  314. INC( AreaAdr.off, ThisScan );
  315. WHILE (BytesSkipped < PossibleStartSpots) DO
  316. IF ThisScan < CARDINAL(MaxScan) THEN
  317. cnt := 0;
  318. LOOP
  319. (* as long as following elements equal do:*)
  320. INC( cnt );
  321. IF cnt >= PatternSize THEN
  322. (*if at end of pattern, we've found a match*)
  323. RETURN BytesSkipped;
  324. END;
  325. INC( PatAdr.off );
  326. INC( AreaAdr.off );
  327. IF AreaAdr.b^ # PatAdr.b^ THEN
  328. (*if next element # object, stop comparing*)
  329. EXIT;
  330. END;
  331. END;
  332. END;
  333. INC( BytesSkipped );
  334. IF BytesSkipped > PossibleStartSpots THEN
  335. RETURN ScanAreaSize;
  336. (* Pattern not found. *)
  337. ELSIF (PossibleStartSpots - BytesSkipped) > 32766 THEN
  338. MaxScan := 32766;
  339. ELSE
  340. MaxScan := PossibleStartSpots - BytesSkipped;
  341. END;
  342. PatAdr.a := PatternAdr;
  343. AreaAdr.a := ScanAreaAdr;
  344. INC( AreaAdr.off, BytesSkipped );
  345. ThisScan := LowLevel.ScanEQ( MaxScan,
  346. PatAdr.b^, AreaAdr.a );
  347. INC( BytesSkipped, ThisScan );
  348. INC( AreaAdr.off, ThisScan );
  349. END;
  350. END;
  351. RETURN ScanAreaSize;
  352. (* Pattern not found. *)
  353. END PatternScan;
  354. PROCEDURE PosAdr(VAR s2 : ARRAY OF CHAR; s : SYSTEM.ADDRESS;
  355. size: CARDINAL) : CARDINAL;
  356. BEGIN
  357. RETURN PatternScan( SYSTEM.ADR(s2), M2Strings.Length(s2), s, size );
  358. END PosAdr;
  359. PROCEDURE Pos(s2 : ARRAY OF CHAR; s : ARRAY OF CHAR) : CARDINAL;
  360. (* Return the index in s of the first occurence of the substring s2,
  361. or High(s)+1 if there are no occurences of s2.*)
  362. VAR
  363. i, j, n, n2, len : CARDINAL;
  364. BEGIN
  365. n := M2Strings.Length(s);
  366. (*length of string to search in*)
  367. n2 := M2Strings.Length(s2);
  368. (*length of string to search for*)
  369. IF (n>=n2) AND (n2#0) THEN
  370. (*only search if target bigger than object; object non-null*)
  371. len := n-n2+1;
  372. (* number of possible starting positions *)
  373. i := LowLevel.ScanEQ(len,s2[0],SYSTEM.ADR(s[0]));
  374. (*changed; start at first occurence of s2[0]*)
  375. WHILE (i<len) DO
  376. (*only for possible starting positions*)
  377. j := 1;
  378. LOOP
  379. (* as long as following elements equal object do:*)
  380. IF j>=n2 THEN
  381. (*if at end of object, finished*)
  382. RETURN i;
  383. END;
  384. IF s[i+j]<>s2[0+j] THEN
  385. (*if next element <> to object, stop comparing*)
  386. EXIT;
  387. END;
  388. INC(j);
  389. END;
  390. i := i+1+CARDINAL(LowLevel.ScanEQ(len-1-i,s2[0],SYSTEM.ADR(s[i+1])));
  391. (*changed: try next possible starting position *)
  392. END;
  393. END;
  394. RETURN (HIGH(s)+1);
  395. (* String not found. *)
  396. END Pos;
  397. PROCEDURE PosDelimited(VAR pattern, obj, delimiters : ARRAY OF
  398. CHAR) : CARDINAL;
  399. (*These are VAR parameters just for purposes of efficiency.
  400. Neither is actually changed.*)
  401. VAR
  402. spot1, spot2 : CARDINAL;
  403. BEGIN
  404. (*PosDelimited*)
  405. IF PresentPos( pattern, obj, spot1 ) THEN
  406. spot2 := spot1 + M2Strings.Length(pattern);
  407. (*spot2 should now point to the character after the
  408. pattern*)
  409. IF (spot1=0)
  410. OR
  411. Present(obj[spot1-1],delimiters) THEN
  412. (*Now we know everything to the left of the pattern
  413. is okay.*)
  414. IF (spot2>=M2Strings.Length(obj)) OR Present(obj[spot2],
  415. delimiters) THEN
  416. (*And now we know everything is okay to the right.*)
  417. RETURN (spot1);
  418. END;
  419. END;
  420. END;
  421. RETURN (HIGH(obj)+1);
  422. END PosDelimited;
  423. PROCEDURE Pos1by1(s2 : ARRAY OF CHAR; s : ARRAY OF CHAR) : CARDINAL;
  424. (* Return the index in s of the first occurence of the substring s2,
  425. or High(s)+1 if there are no occurences of s2.*)
  426. VAR
  427. found : BOOLEAN;
  428. i, j, n, n2, len : CARDINAL;
  429. BEGIN
  430. n := M2Strings.Length(s);
  431. (*length of string to search in*)
  432. n2 := M2Strings.Length(s2);
  433. (*length of string to search for*)
  434. IF (n >= n2) AND (n2 > 0) THEN
  435. (*only search if the target is bigger then the object*)
  436. len := n-n2;
  437. (* highest possible starting index *)
  438. i := 0;
  439. WHILE (i<=len) DO
  440. (*for every possible starting position*)
  441. IF (s[i]=s2[0]) THEN
  442. (*if equals first char of object*)
  443. j := 1;
  444. LOOP
  445. (* as long as target elements equal object do:*)
  446. IF j>n2-1 THEN
  447. (*if at end of object, finsihed*)
  448. RETURN i;
  449. END;
  450. IF s[i+j]<>s2[0+j] THEN
  451. (*if next element <> to object, stop comparing*)
  452. EXIT;
  453. END;
  454. INC(j);
  455. END;
  456. END;
  457. INC(i);
  458. (* try next possible starting position *)
  459. END;
  460. END;
  461. RETURN (HIGH(s)+1);
  462. (* String not found.*)
  463. END Pos1by1;
  464. PROCEDURE Positn(s2 : ARRAY OF CHAR; s : ARRAY OF CHAR; i : CARDINAL) :
  465. CARDINAL;
  466. (* Return the index in s of the first occurence of the substring s2,
  467. at or after index i, or HIGH(s)+1 if there are no occurences of s2.*)
  468. VAR
  469. len, PosAdrResult: CARDINAL;
  470. BEGIN
  471. len := M2Strings.Length( s );
  472. IF len > i THEN
  473. PosAdrResult := PosAdr( s2, SYSTEM.ADR(s[i]), len - i );
  474. IF PosAdrResult < (len - i) THEN
  475. RETURN i + PosAdrResult;
  476. END;
  477. END;
  478. RETURN (HIGH(s)+1);
  479. (* String not found.*)
  480. END Positn;
  481. PROCEDURE PosInsensitive(str1, str2 : ARRAY OF CHAR) : CARDINAL;
  482. BEGIN
  483. StrEdit.CAPstr(str1);
  484. StrEdit.CAPstr(str2);
  485. RETURN Pos(str1,str2);
  486. END PosInsensitive;
  487. PROCEDURE Present(str1, str2 : ARRAY OF CHAR) : BOOLEAN;
  488. BEGIN
  489. IF NOT CaseSensitive THEN
  490. StrEdit.CAPstr(str1);
  491. StrEdit.CAPstr(str2);
  492. END;
  493. IF Pos(str1,str2)<=HIGH(str2) THEN
  494. RETURN (TRUE);
  495. ELSE
  496. RETURN (FALSE);
  497. END;
  498. END Present;
  499. PROCEDURE PresentPos(str1, str2 : ARRAY OF CHAR; VAR spot:
  500. CARDINAL) : BOOLEAN;
  501. VAR
  502. size: CARDINAL;
  503. BEGIN
  504. IF NOT CaseSensitive THEN
  505. StrEdit.CAPstr(str1);
  506. StrEdit.CAPstr(str2);
  507. END;
  508. size := M2Strings.Length(str2);
  509. spot := PosAdr( str1, SYSTEM.ADR(str2), size );
  510. IF spot < size THEN
  511. RETURN (TRUE);
  512. ELSE
  513. spot := HIGH(str2) + 1;
  514. RETURN (FALSE);
  515. END;
  516. END PresentPos;
  517. PROCEDURE SubStrMatch( str1: ARRAY OF CHAR; StartingAt1,
  518. EndingAt1: CARDINAL; VAR str2: ARRAY OF CHAR; StartingAt2,
  519. EndingAt2: CARDINAL ): BOOLEAN;
  520. VAR
  521. size1, size2, tmpc: CARDINAL;
  522. BEGIN
  523. size1 := M2Strings.Length(str1);
  524. size2 := M2Strings.Length(str2);
  525. IF (size1 <= StartingAt1) OR (size2 <= StartingAt2) OR
  526. (StartingAt1 > EndingAt1) OR (StartingAt2 > EndingAt2) THEN
  527. RETURN FALSE;
  528. END;
  529. size1 := Numbers.Min( EndingAt1 + 1, size1) - StartingAt1;
  530. size2 := Numbers.Min( EndingAt2 + 1, size2) - StartingAt2;
  531. tmpc := PatternScan( SYSTEM.ADR(str1[StartingAt1]), size1,
  532. SYSTEM.ADR(str2[StartingAt2]), size2 );
  533. RETURN tmpc = 0;
  534. END SubStrMatch;
  535. BEGIN
  536. Initialized := FALSE;
  537. Init();
  538. END PosUtils.