FileIO.mod 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  1. IMPLEMENTATION MODULE FileIO;
  2. (* ISO (GPM) version by Pat Terry. Sat 04-25-98 p.terry@ru.ac.za *)
  3. (* This module attempts to provide several potentially non-portable
  4. facilities for Coco/R.
  5. (a) A general file input/output module, with all routines required for
  6. Coco/R itself, as well as several other that would be useful in
  7. Coco-generated applications.
  8. (b) Definition of the "LONGINT" type needed by Coco.
  9. (c) Some conversion functions to handle this long type.
  10. (d) Some "long" and other constant literals that may be problematic
  11. on some implementations.
  12. (e) Some string handling primitives needed to interface to a variety
  13. of known implementations.
  14. The intention is that the rest of the code of Coco and its generated
  15. parsers should be as portable as possible. Provided the definition
  16. module given, and the associated implementation, satisfy the
  17. specification given here, this should be almost 100% possible.
  18. FileIO is based on code by MB 1990/11/25; heavily modified and extended
  19. by PDT and others between 1992/1/6 and the present day. *)
  20. IMPORT (* GNU Modula-2 specific *) Environment,FileSysOp,
  21. SYSTEM, Strings, SysClock, ProgramArgs, TextIO, RawIO, WholeIO,
  22. IOChan, IOResult, RndFile, TermFile, StdChans, ChanConsts;
  23. FROM Storage IMPORT ALLOCATE, DEALLOCATE;
  24. CONST
  25. MaxFiles = BitSetSize;
  26. NameLength = 256;
  27. TYPE
  28. File = POINTER TO FileRec;
  29. FileRec = RECORD
  30. ref: IOChan.ChanId;
  31. self: File;
  32. handle: CARDINAL;
  33. savedCh: CHAR;
  34. textOK, eof, eol, noOutput, noInput, haveCh: BOOLEAN;
  35. name: ARRAY [0 .. NameLength] OF CHAR;
  36. END;
  37. VAR
  38. Handles: BITSET;
  39. Opened: ARRAY [0 .. MaxFiles-1] OF File;
  40. FromKeyboard, ToScreen: BOOLEAN;
  41. res: ChanConsts.OpenResults;
  42. PROCEDURE NotRead (f: File): BOOLEAN;
  43. BEGIN
  44. RETURN (f = NIL) OR (f^.self # f) OR (f^.noInput);
  45. END NotRead;
  46. PROCEDURE NotWrite (f: File): BOOLEAN;
  47. BEGIN
  48. RETURN (f = NIL) OR (f^.self # f) OR (f^.noOutput);
  49. END NotWrite;
  50. PROCEDURE NotFile (f: File): BOOLEAN;
  51. BEGIN
  52. RETURN (f = NIL) OR (f^.self # f) OR (f = con) OR (f = err)
  53. OR (f = StdIn) & FromKeyboard
  54. OR (f = StdOut) & ToScreen
  55. END NotFile;
  56. PROCEDURE CheckRedirection;
  57. BEGIN
  58. FromKeyboard := TRUE; ToScreen := TRUE; (* ISO fail safe *)
  59. (* Ideally we would like
  60. FromKeyboard := NOT (StdIn has been redirected )
  61. ToScreen := NOT (StdOut has been redirected )
  62. *)
  63. END CheckRedirection;
  64. PROCEDURE ASCIIZ (VAR s1, s2: ARRAY OF CHAR);
  65. (* Convert s2 to a nul terminated string in s1 *)
  66. VAR
  67. i: CARDINAL;
  68. BEGIN
  69. i := 0;
  70. WHILE (i <= HIGH(s2)) & (s2[i] # 0C) DO
  71. s1[i] := s2[i]; INC(i)
  72. END;
  73. s1[i] := 0C
  74. END ASCIIZ;
  75. PROCEDURE NextParameter (VAR s: ARRAY OF CHAR);
  76. BEGIN
  77. IF ProgramArgs.IsArgPresent()
  78. THEN
  79. TextIO.ReadToken(ProgramArgs.ArgChan(), s);
  80. ProgramArgs.NextArg()
  81. ELSE s[0] := 0C
  82. END
  83. END NextParameter;
  84. PROCEDURE GetEnv (envVar: ARRAY OF CHAR; VAR s: ARRAY OF CHAR);
  85. (* ++++ GNU Modula-2 specific routine used ++++ *)
  86. VAR
  87. OK : BOOLEAN;
  88. BEGIN
  89. OK := Environment.GetEnvironment(envVar, s);
  90. END GetEnv;
  91. PROCEDURE Open (VAR f: File; fileName: ARRAY OF CHAR; newFile: BOOLEAN);
  92. VAR
  93. i: CARDINAL;
  94. name: ARRAY [0 .. NameLength] OF CHAR;
  95. BEGIN
  96. ExtractFileName(fileName, name);
  97. FOR i := 0 TO NameLength - 1 DO name[i] := CAP(name[i]) END;
  98. IF (name[0] = 0C) OR (Compare(name, "CON") = 0) THEN
  99. (* con already opened, but reset it *)
  100. Okay := TRUE; f := con;
  101. f^.savedCh := 0C; f^.haveCh := FALSE;
  102. f^.eof := FALSE; f^.eol := FALSE; f^.name := "CON";
  103. RETURN
  104. ELSIF Compare(name, "ERR") = 0 THEN
  105. Okay := TRUE; f := err; RETURN
  106. ELSE
  107. ALLOCATE(f, SYSTEM.TSIZE(FileRec));
  108. (* Flags below may have to be altered according to implementation *)
  109. IF newFile
  110. THEN RndFile.OpenClean(f^.ref, fileName,
  111. RndFile.old (* + RndFile.text *) + RndFile.raw, res)
  112. ELSE RndFile.OpenOld(f^.ref, fileName,
  113. RndFile.read (* + RnfDile.text *) +RndFile.raw, res)
  114. END;
  115. Okay := res = RndFile.opened;
  116. IF ~ Okay
  117. THEN
  118. DEALLOCATE(f, SYSTEM.TSIZE(FileRec)); f := NIL
  119. ELSE
  120. (* textOK below may have to be altered according to implementation *)
  121. f^.savedCh := 0C; f^.haveCh := FALSE; f^.textOK := FALSE;
  122. f^.eof := newFile; f^.eol := newFile; f^.self := f;
  123. f^.noInput := newFile; f^.noOutput := ~ newFile;
  124. ASCIIZ(f^.name, fileName);
  125. i := 0 (* find next available filehandle *);
  126. WHILE (i IN Handles) & (i < MaxFiles) DO INC(i) END;
  127. IF i < MaxFiles
  128. THEN f^.handle := i; INCL(Handles, i); Opened[i] := f
  129. ELSE WriteString(err, "Too many files"); Okay := FALSE
  130. END;
  131. END
  132. END
  133. END Open;
  134. PROCEDURE Close (VAR f: File);
  135. BEGIN
  136. Okay := TRUE;
  137. IF NotFile(f) OR (f = StdIn) OR (f = StdOut)
  138. THEN Okay := FALSE
  139. ELSE
  140. EXCL(Handles, f^.handle);
  141. RndFile.Close(f^.ref);
  142. IF Okay THEN DEALLOCATE(f, SYSTEM.TSIZE(FileRec)) END;
  143. f := NIL
  144. END;
  145. (*
  146. EXCEPT (* For ISO compilers *)
  147. Okay := FALSE; f := NIL; RETURN
  148. *)
  149. END Close;
  150. PROCEDURE Delete (VAR f: File);
  151. (* ++++ GNU Modula-2 specific routine used ++++ *)
  152. VAR
  153. fname : ARRAY [0 .. NameLength] OF CHAR;
  154. BEGIN
  155. IF NotFile(f) OR (f = StdIn) OR (f = StdOut)
  156. THEN Okay := FALSE
  157. ELSE
  158. Assign(f^.name, fname);
  159. Close(f);
  160. Okay := FileSysOp.Unlink(fname);
  161. END
  162. END Delete;
  163. PROCEDURE Exists (fileName: ARRAY OF CHAR): BOOLEAN;
  164. VAR f: File;
  165. ok: BOOLEAN;
  166. BEGIN
  167. Open(f, fileName, FALSE);
  168. ok := Okay;
  169. IF ok THEN Close(f) END;
  170. RETURN ok
  171. END Exists;
  172. PROCEDURE SearchFile (VAR f: File; envVar, fileName: ARRAY OF CHAR;
  173. newFile: BOOLEAN);
  174. VAR
  175. i, j: INTEGER;
  176. k: CARDINAL;
  177. c: CHAR;
  178. fname: ARRAY [0 .. NameLength] OF CHAR;
  179. path: ARRAY [0 .. NameLength] OF CHAR;
  180. BEGIN
  181. FOR k := 0 TO HIGH(envVar) DO envVar[k] := CAP(envVar[k]) END;
  182. GetEnv(envVar, path);
  183. i := 0;
  184. REPEAT
  185. j := 0;
  186. REPEAT
  187. c := path[i]; fname[j] := c; INC(i); INC(j)
  188. UNTIL (c = PathSep) OR (c = 0C);
  189. IF (j > 1) & (fname[j-2] = DirSep) THEN DEC(j) ELSE fname[j-1] := DirSep END;
  190. fname[j] := 0C; Concat(fname, fileName, fname);
  191. Open(f, fname, newFile);
  192. UNTIL (c = 0C) OR Okay
  193. END SearchFile;
  194. PROCEDURE ExtractDirectory (fullName: ARRAY OF CHAR;
  195. VAR directory: ARRAY OF CHAR);
  196. VAR
  197. i, start: CARDINAL;
  198. BEGIN
  199. start := 0; i := 0;
  200. WHILE (i <= HIGH(fullName)) & (fullName[i] # 0C) DO
  201. IF i <= HIGH(directory) THEN
  202. directory[i] := fullName[i];
  203. END;
  204. IF (fullName[i] = ":") OR (fullName[i] = DirSep) THEN start := i + 1 END;
  205. INC(i)
  206. END;
  207. IF start <= HIGH(directory) THEN directory[start] := 0C END
  208. END ExtractDirectory;
  209. PROCEDURE ExtractFileName (fullName: ARRAY OF CHAR;
  210. VAR fileName: ARRAY OF CHAR);
  211. VAR
  212. i, l, start: CARDINAL;
  213. BEGIN
  214. start := 0; l := 0;
  215. WHILE (l <= HIGH(fullName)) & (fullName[l] # 0C) DO
  216. IF (fullName[l] = ":") OR (fullName[l] = DirSep) THEN start := l + 1 END;
  217. INC(l)
  218. END;
  219. i := 0;
  220. WHILE (start < l) & (i <= HIGH(fileName)) DO
  221. fileName[i] := fullName[start]; INC(start); INC(i)
  222. END;
  223. IF i <= HIGH(fileName) THEN fileName[i] := 0C END
  224. END ExtractFileName;
  225. PROCEDURE AppendExtension (oldName, ext: ARRAY OF CHAR;
  226. VAR newName: ARRAY OF CHAR);
  227. VAR
  228. i, j: CARDINAL;
  229. fn: ARRAY [0 .. NameLength] OF CHAR;
  230. BEGIN
  231. ExtractDirectory(oldName, newName);
  232. ExtractFileName(oldName, fn);
  233. i := 0; j := 0;
  234. WHILE (i <= NameLength) & (fn[i] # 0C) DO
  235. IF fn[i] = "." THEN j := i + 1 END;
  236. INC(i)
  237. END;
  238. IF (j # i) (* then name did not end with "." *) OR (i = 0) THEN
  239. IF j # 0 THEN i := j - 1 END;
  240. IF (ext[0] # ".") & (ext[0] # 0C) THEN
  241. IF i <= NameLength THEN fn[i] := "."; INC(i) END
  242. END;
  243. j := 0;
  244. WHILE (j <= HIGH(ext)) & (ext[j] # 0C) & (i <= NameLength) DO
  245. fn[i] := ext[j]; INC(i); INC(j)
  246. END
  247. END;
  248. IF i <= NameLength THEN fn[i] := 0C END;
  249. Concat(newName, fn, newName)
  250. END AppendExtension;
  251. PROCEDURE ChangeExtension (oldName, ext: ARRAY OF CHAR;
  252. VAR newName: ARRAY OF CHAR);
  253. VAR
  254. i, j: CARDINAL;
  255. fn: ARRAY [0 .. NameLength] OF CHAR;
  256. BEGIN
  257. ExtractDirectory(oldName, newName);
  258. ExtractFileName(oldName, fn);
  259. i := 0; j := 0;
  260. WHILE (i <= NameLength) & (fn[i] # 0C) DO
  261. IF fn[i] = "." THEN j := i + 1 END;
  262. INC(i)
  263. END;
  264. IF j # 0 THEN i := j - 1 END;
  265. IF (ext[0] # ".") & (ext[0] # 0C) THEN
  266. IF i <= NameLength THEN fn[i] := "."; INC(i) END
  267. END;
  268. j := 0;
  269. WHILE (j <= HIGH(ext)) & (ext[j] # 0C) & (i <= NameLength) DO
  270. fn[i] := ext[j]; INC(i); INC(j)
  271. END;
  272. IF i <= NameLength THEN fn[i] := 0C END;
  273. Concat(newName, fn, newName)
  274. END ChangeExtension;
  275. PROCEDURE Length (f: File): INT32;
  276. (* ++++ implementation specific coercion routine may have to be used ++++ *)
  277. VAR
  278. pos: RndFile.FilePos;
  279. BEGIN
  280. IF NotFile(f)
  281. THEN
  282. Okay := FALSE; RETURN Long0
  283. ELSE
  284. Okay := TRUE;
  285. pos := RndFile.EndPos(f^.ref);
  286. (* ++++ GPM specific routine used ++++ *)
  287. RETURN VAL(INT32, pos)
  288. END;
  289. (*
  290. EXCEPT (* For ISO compilers *)
  291. Okay := FALSE; RETURN Long0
  292. *)
  293. END Length;
  294. PROCEDURE GetPos (f: File): INT32;
  295. (* ++++ implementation specific coercion routine may have to be used ++++ *)
  296. VAR
  297. pos: RndFile.FilePos;
  298. BEGIN
  299. IF NotFile(f)
  300. THEN
  301. Okay := FALSE; RETURN Long0
  302. ELSE
  303. Okay := TRUE;
  304. pos := RndFile.CurrentPos(f^.ref);
  305. (* ++++ GPM specific casting used ++++ *)
  306. RETURN VAL(INT32, pos)
  307. END;
  308. (*
  309. EXCEPT (* For ISO compilers *)
  310. Okay := FALSE; RETURN Long0
  311. *)
  312. END GetPos;
  313. PROCEDURE SetPos (f: File; pos: INT32);
  314. (* ++++ implementation specific coercion routine may have to be used ++++ *)
  315. BEGIN
  316. IF NotFile(f)
  317. THEN
  318. Okay := FALSE
  319. ELSE
  320. Okay := TRUE; f^.haveCh := FALSE;
  321. (* ++++ GPM specific routine used ++++ *)
  322. RndFile.SetPos(f^.ref, VAL(CARDINAL, pos));
  323. END;
  324. (*
  325. EXCEPT (* For ISO compilers *)
  326. Okay := FALSE; f^.haveCh := FALSE; RETURN
  327. *)
  328. END SetPos;
  329. PROCEDURE Reset (f: File);
  330. BEGIN
  331. IF NotFile(f)
  332. THEN
  333. Okay := FALSE
  334. ELSE
  335. SetPos(f, 0);
  336. IF Okay THEN
  337. f^.haveCh := FALSE; f^.eof := f^.noInput; f^.eol := f^.noInput
  338. END
  339. END
  340. END Reset;
  341. PROCEDURE Rewrite (f: File);
  342. BEGIN
  343. IF NotFile(f)
  344. THEN
  345. Okay := FALSE
  346. ELSE
  347. RndFile.Close(f^.ref);
  348. (* Flags below may have to be altered according to implementation *)
  349. RndFile.OpenClean(f^.ref, f^.name,
  350. RndFile.old + (* RndFile.text + *) RndFile.raw, res);
  351. Okay := res = RndFile.opened;
  352. IF ~ Okay
  353. THEN
  354. DEALLOCATE(f, SYSTEM.TSIZE(FileRec)); f := NIL
  355. ELSE
  356. f^.savedCh := 0C; f^.haveCh := FALSE;
  357. f^.eof := TRUE; f^.eol := TRUE;
  358. f^.noInput := TRUE; f^.noOutput := FALSE;
  359. END
  360. END;
  361. (*
  362. EXCEPT (* For ISO compilers *)
  363. Okay := FALSE; RETURN
  364. *)
  365. END Rewrite;
  366. PROCEDURE EndOfLine (f: File): BOOLEAN;
  367. BEGIN
  368. IF NotRead(f)
  369. THEN Okay := FALSE; RETURN TRUE
  370. ELSE Okay := TRUE; RETURN f^.eol OR f^.eof
  371. END
  372. END EndOfLine;
  373. PROCEDURE EndOfFile (f: File): BOOLEAN;
  374. BEGIN
  375. IF NotRead(f)
  376. THEN Okay := FALSE; RETURN TRUE
  377. ELSE Okay := TRUE; RETURN f^.eof
  378. END
  379. END EndOfFile;
  380. PROCEDURE Read (f: File; VAR ch: CHAR);
  381. BEGIN
  382. IF NotRead(f) THEN Okay := FALSE; ch := 0C; RETURN END;
  383. IF f^.haveCh OR f^.eof
  384. THEN
  385. ch := f^.savedCh; Okay := ch # 0C;
  386. ELSE
  387. Okay := TRUE;
  388. IF ~ f^.textOK (* Work around as best one can *)
  389. THEN RawIO.Read(f^.ref, ch)
  390. ELSE TextIO.ReadChar(f^.ref, ch);
  391. END;
  392. IF f^.textOK & (IOResult.ReadResult(f^.ref) = IOResult.endOfLine)
  393. THEN TextIO.SkipLine(f^.ref); ch := EOL
  394. ELSIF ch = LF (* Work around possible bug *) THEN ch := EOL
  395. END;
  396. IF IOResult.ReadResult(f^.ref) = IOResult.endOfInput THEN
  397. Okay := FALSE; ch := 0C;
  398. END;
  399. IF ch = EOFChar THEN Okay := FALSE; ch := 0C END;
  400. END;
  401. IF ~ Okay THEN ch := 0C END;
  402. f^.savedCh := ch; f^.haveCh := ~ Okay;
  403. f^.eof := ch = 0C; f^.eol := f^.eof OR (ch = EOL);
  404. END Read;
  405. PROCEDURE ReadAgain (f: File);
  406. BEGIN
  407. IF NotRead(f)
  408. THEN Okay := FALSE
  409. ELSE f^.haveCh := TRUE
  410. END
  411. END ReadAgain;
  412. PROCEDURE ReadLn (f: File);
  413. VAR
  414. ch: CHAR;
  415. BEGIN
  416. IF NotRead(f) THEN Okay := FALSE; RETURN END;
  417. WHILE ~ f^.eol DO Read(f, ch) END;
  418. f^.haveCh := FALSE; f^.eol := FALSE;
  419. END ReadLn;
  420. PROCEDURE ReadString (f: File; VAR str: ARRAY OF CHAR);
  421. VAR
  422. j: CARDINAL;
  423. ch: CHAR;
  424. BEGIN
  425. str[0] := 0C; j := 0;
  426. IF NotRead(f) THEN Okay := FALSE; RETURN END;
  427. REPEAT Read(f, ch) UNTIL (ch # " ") OR ~ Okay;
  428. IF Okay THEN
  429. WHILE ch >= " " DO
  430. IF j <= HIGH(str) THEN str[j] := ch END; INC(j);
  431. Read(f, ch);
  432. WHILE (ch = BS) OR (ch = DEL) DO
  433. IF j > 0 THEN DEC(j) END; Read(f, ch)
  434. END
  435. END;
  436. IF j <= HIGH(str) THEN str[j] := 0C END;
  437. Okay := j > 0; f^.haveCh := TRUE; f^.savedCh := ch;
  438. END
  439. END ReadString;
  440. PROCEDURE ReadLine (f: File; VAR str: ARRAY OF CHAR);
  441. VAR
  442. j: CARDINAL;
  443. ch: CHAR;
  444. BEGIN
  445. str[0] := 0C; j := 0;
  446. IF NotRead(f) THEN Okay := FALSE; RETURN END;
  447. Read(f, ch);
  448. IF Okay THEN
  449. WHILE ch >= " " DO
  450. IF j <= HIGH(str) THEN str[j] := ch END; INC(j);
  451. Read(f, ch);
  452. WHILE (ch = BS) OR (ch = DEL) DO
  453. IF j > 0 THEN DEC(j) END; Read(f, ch)
  454. END
  455. END;
  456. IF j <= HIGH(str) THEN str[j] := 0C END;
  457. Okay := j > 0; f^.haveCh := TRUE; f^.savedCh := ch;
  458. END
  459. END ReadLine;
  460. PROCEDURE ReadToken (f: File; VAR str: ARRAY OF CHAR);
  461. VAR
  462. j: CARDINAL;
  463. ch: CHAR;
  464. BEGIN
  465. str[0] := 0C; j := 0;
  466. IF NotRead(f) THEN Okay := FALSE; RETURN END;
  467. REPEAT Read(f, ch) UNTIL (ch > " ") OR ~ Okay;
  468. IF Okay THEN
  469. WHILE ch > " " DO
  470. IF j <= HIGH(str) THEN str[j] := ch END; INC(j);
  471. Read(f, ch);
  472. WHILE (ch = BS) OR (ch = DEL) DO
  473. IF j > 0 THEN DEC(j) END; Read(f, ch)
  474. END
  475. END;
  476. IF j <= HIGH(str) THEN str[j] := 0C END;
  477. Okay := j > 0; f^.haveCh := TRUE; f^.savedCh := ch;
  478. END
  479. END ReadToken;
  480. PROCEDURE ReadInt (f: File; VAR i: INTEGER);
  481. VAR
  482. Digit: INTEGER;
  483. j: CARDINAL;
  484. Negative: BOOLEAN;
  485. s: ARRAY [0 .. 80] OF CHAR;
  486. BEGIN
  487. i := 0; j := 0;
  488. IF NotRead(f) THEN Okay := FALSE; RETURN END;
  489. ReadToken(f, s);
  490. IF s[0] = "-" (* deal with sign *)
  491. THEN Negative := TRUE; INC(j)
  492. ELSE Negative := FALSE; IF s[0] = "+" THEN INC(j) END
  493. END;
  494. IF (s[j] < "0") OR (s[j] > "9") THEN Okay := FALSE END;
  495. WHILE (j <= 80) & (s[j] >= "0") & (s[j] <= "9") DO
  496. Digit := VAL(INTEGER, ORD(s[j]) - ORD("0"));
  497. IF i <= (MAX(INTEGER) - Digit) DIV 10
  498. THEN i := 10 * i + Digit
  499. ELSE Okay := FALSE
  500. END;
  501. INC(j)
  502. END;
  503. IF Negative THEN i := -i END;
  504. IF (j > 80) OR (s[j] # 0C) THEN Okay := FALSE END;
  505. IF ~ Okay THEN i := 0 END;
  506. END ReadInt;
  507. PROCEDURE ReadCard (f: File; VAR i: CARDINAL);
  508. VAR
  509. Digit: CARDINAL;
  510. j: CARDINAL;
  511. s: ARRAY [0 .. 80] OF CHAR;
  512. BEGIN
  513. i := 0; j := 0;
  514. IF NotRead(f) THEN Okay := FALSE; RETURN END;
  515. ReadToken(f, s);
  516. WHILE (j <= 80) & (s[j] >= "0") & (s[j] <= "9") DO
  517. Digit := ORD(s[j]) - ORD("0");
  518. IF i <= (MAX(CARDINAL) - Digit) DIV 10
  519. THEN i := 10 * i + Digit
  520. ELSE Okay := FALSE
  521. END;
  522. INC(j)
  523. END;
  524. IF (j > 80) OR (s[j] # 0C) THEN Okay := FALSE END;
  525. IF ~ Okay THEN i := 0 END;
  526. END ReadCard;
  527. PROCEDURE ReadBytes (f: File; VAR buf: ARRAY OF SYSTEM.BYTE; VAR len: CARDINAL);
  528. VAR
  529. TooMany: BOOLEAN;
  530. Wanted: CARDINAL;
  531. BEGIN
  532. IF NotRead(f) OR (f = con)
  533. THEN Okay := FALSE; len := 0;
  534. ELSE
  535. IF len = 0 THEN Okay := TRUE; RETURN END;
  536. TooMany := len - 1 > HIGH(buf);
  537. IF TooMany THEN Wanted := HIGH(buf) + 1 ELSE Wanted := len END;
  538. IOChan.RawRead(f^.ref, SYSTEM.ADR(buf), Wanted, Wanted);
  539. Okay := Wanted # 0;
  540. IF len # Wanted THEN Okay := FALSE END;
  541. len := Wanted;
  542. END;
  543. IF ~ Okay THEN f^.eof := TRUE END;
  544. IF TooMany THEN Okay := FALSE END;
  545. (*
  546. EXCEPT (* For ISO compilers *)
  547. Okay := FALSE; len := 0; RETURN
  548. *)
  549. END ReadBytes;
  550. PROCEDURE Write (f: File; ch: CHAR);
  551. BEGIN
  552. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  553. Okay := TRUE;
  554. IF ch = EOL
  555. THEN (* implementation may not support Text operations on all files *)
  556. IF f^.textOK
  557. THEN TextIO.WriteLn(f^.ref)
  558. ELSE ch := LF; RawIO.Write(f^.ref, ch)
  559. (* but you may have to write CR/LF or CR or LF *)
  560. END
  561. ELSE
  562. IF f^.textOK
  563. THEN TextIO.WriteChar(f^.ref, ch)
  564. ELSE RawIO.Write(f^.ref, ch)
  565. END
  566. END;
  567. (*
  568. EXCEPT (* For ISO compilers *)
  569. Okay := FALSE; RETURN
  570. *)
  571. END Write;
  572. PROCEDURE WriteLn (f: File);
  573. BEGIN
  574. IF NotWrite(f)
  575. THEN Okay := FALSE;
  576. ELSE Write(f, EOL)
  577. END
  578. END WriteLn;
  579. PROCEDURE WriteString (f: File; str: ARRAY OF CHAR);
  580. VAR
  581. pos: CARDINAL;
  582. BEGIN
  583. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  584. pos := 0;
  585. WHILE (pos <= HIGH(str)) & (str[pos] # 0C) DO
  586. Write(f, str[pos]); INC(pos)
  587. END
  588. END WriteString;
  589. PROCEDURE WriteText (f: File; text: ARRAY OF CHAR; len: INTEGER);
  590. VAR
  591. i, slen: INTEGER;
  592. BEGIN
  593. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  594. slen := LENGTH(text);
  595. FOR i := 0 TO len - 1 DO
  596. IF i < slen THEN Write(f, text[i]) ELSE Write(f, " ") END;
  597. END
  598. END WriteText;
  599. PROCEDURE WriteInt (f: File; n: INTEGER; wid: CARDINAL);
  600. VAR
  601. l, d: CARDINAL;
  602. x: INTEGER;
  603. t: ARRAY [1 .. 25] OF CHAR;
  604. sign: CHAR;
  605. BEGIN
  606. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  607. IF n < 0
  608. THEN sign := "-"; x := - n;
  609. ELSE sign := " "; x := n;
  610. END;
  611. l := 0;
  612. REPEAT
  613. d := x MOD 10; x := x DIV 10;
  614. INC(l); t[l] := CHR(ORD("0") + d);
  615. UNTIL x = 0;
  616. IF wid = 0 THEN Write(f, " ") END;
  617. WHILE wid > l + 1 DO Write(f, " "); DEC(wid); END;
  618. IF (sign = "-") OR (wid > l) THEN Write(f, sign); END;
  619. WHILE l > 0 DO Write(f, t[l]); DEC(l); END;
  620. END WriteInt;
  621. PROCEDURE WriteCard (f: File; n, wid: CARDINAL);
  622. VAR
  623. l, d: CARDINAL;
  624. t: ARRAY [1 .. 25] OF CHAR;
  625. BEGIN
  626. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  627. l := 0;
  628. REPEAT
  629. d := n MOD 10; n := n DIV 10;
  630. INC(l); t[l] := CHR(ORD("0") + d);
  631. UNTIL n = 0;
  632. IF wid = 0 THEN Write(f, " ") END;
  633. WHILE wid > l DO Write(f, " "); DEC(wid); END;
  634. WHILE l > 0 DO Write(f, t[l]); DEC(l); END;
  635. END WriteCard;
  636. PROCEDURE WriteBytes (f: File; VAR buf: ARRAY OF SYSTEM.BYTE; len: CARDINAL);
  637. VAR
  638. TooMany: BOOLEAN;
  639. BEGIN
  640. TooMany := (len > 0) & (len - 1 > HIGH(buf));
  641. IF NotWrite(f) OR (f = con) OR (f = err)
  642. THEN
  643. Okay := FALSE
  644. ELSE
  645. Okay := TRUE;
  646. IF TooMany THEN len := HIGH(buf) + 1 END;
  647. IOChan.RawWrite(f^.ref, SYSTEM.ADR(buf), len);
  648. END;
  649. IF TooMany THEN Okay := FALSE END;
  650. (*
  651. EXCEPT (* For ISO compilers *)
  652. Okay := FALSE; RETURN
  653. *)
  654. END WriteBytes;
  655. PROCEDURE GetDate (VAR Year, Month, Day: CARDINAL);
  656. VAR
  657. time: SysClock.DateTime;
  658. BEGIN
  659. SysClock.GetClock(time);
  660. Year := time.year;
  661. Month := time.month;
  662. Day := time.day;
  663. END GetDate;
  664. PROCEDURE GetTime (VAR Hrs, Mins, Secs, Hsecs: CARDINAL);
  665. VAR
  666. time: SysClock.DateTime;
  667. BEGIN
  668. SysClock.GetClock(time);
  669. Hrs := time.hour;
  670. Mins := time.minute;
  671. Secs := time.second;
  672. Hsecs := time.fractions;
  673. END GetTime;
  674. PROCEDURE Write2 (f: File; i: CARDINAL);
  675. BEGIN
  676. Write(f, CHR(i DIV 10 + ORD("0")));
  677. Write(f, CHR(i MOD 10 + ORD("0")));
  678. END Write2;
  679. PROCEDURE WriteDate (f: File);
  680. VAR
  681. Year, Month, Day: CARDINAL;
  682. BEGIN
  683. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  684. GetDate(Year, Month, Day);
  685. Write2(f, Day); Write(f, "/"); Write2(f, Month); Write(f, "/");
  686. WriteCard(f, Year, 1)
  687. END WriteDate;
  688. PROCEDURE WriteTime (f: File);
  689. VAR
  690. Hrs, Mins, Secs, Hsecs: CARDINAL;
  691. BEGIN
  692. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  693. GetTime(Hrs, Mins, Secs, Hsecs);
  694. Write2(f, Hrs); Write(f, ":"); Write2(f, Mins); Write(f, ":");
  695. Write2(f, Secs)
  696. END WriteTime;
  697. VAR
  698. Hrs0, Mins0, Secs0, Hsecs0: CARDINAL;
  699. Hrs1, Mins1, Secs1, Hsecs1: CARDINAL;
  700. PROCEDURE WriteElapsedTime (f: File);
  701. VAR
  702. Hrs, Mins, Secs, Hsecs, s, hs: CARDINAL;
  703. BEGIN
  704. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  705. GetTime(Hrs, Mins, Secs, Hsecs);
  706. WriteString(f, "Elapsed time: ");
  707. IF Hrs >= Hrs1
  708. THEN s := (Hrs - Hrs1) * 3600 + (Mins - Mins1) * 60 + Secs - Secs1
  709. ELSE s := (Hrs + 24 - Hrs1) * 3600 + (Mins - Mins1) * 60 + Secs - Secs1
  710. END;
  711. IF Hsecs >= Hsecs1
  712. THEN hs := Hsecs - Hsecs1
  713. ELSE hs := (Hsecs + 100) - Hsecs1; DEC(s);
  714. END;
  715. WriteCard(f, s, 1); Write(f, ".");
  716. Write2(f, hs); WriteString(f, " s"); WriteLn(f);
  717. Hrs1 := Hrs; Mins1 := Mins; Secs1 := Secs; Hsecs1 := Hsecs;
  718. END WriteElapsedTime;
  719. PROCEDURE WriteExecutionTime (f: File);
  720. VAR
  721. Hrs, Mins, Secs, Hsecs, s, hs: CARDINAL;
  722. BEGIN
  723. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  724. GetTime(Hrs, Mins, Secs, Hsecs);
  725. WriteString(f, "Execution time: ");
  726. IF Hrs >= Hrs0
  727. THEN s := (Hrs - Hrs0) * 3600 + (Mins - Mins0) * 60 + Secs - Secs0
  728. ELSE s := (Hrs + 24 - Hrs0) * 3600 + (Mins - Mins0) * 60 + Secs - Secs0
  729. END;
  730. IF Hsecs >= Hsecs0
  731. THEN hs := Hsecs - Hsecs0
  732. ELSE hs := (Hsecs + 100) - Hsecs0; DEC(s);
  733. END;
  734. WriteCard(f, s, 1); Write(f, "."); Write2(f, hs);
  735. WriteString(f, " s"); WriteLn(f);
  736. END WriteExecutionTime;
  737. (* The code for the next four procedures below may be commented out if your
  738. compiler supports ISO PROCEDURE constant declarations and these declarations
  739. are made in the DEFINITION MODULE *)
  740. PROCEDURE SLENGTH (stringVal: ARRAY OF CHAR): CARDINAL;
  741. BEGIN
  742. RETURN LENGTH(stringVal)
  743. END SLENGTH;
  744. PROCEDURE Assign (source: ARRAY OF CHAR; VAR destination: ARRAY OF CHAR);
  745. BEGIN
  746. (* Be careful - some libraries have the parameters reversed! *)
  747. Strings.Assign(source, destination)
  748. END Assign;
  749. PROCEDURE Extract (source: ARRAY OF CHAR; startIndex: CARDINAL;
  750. numberToExtract: CARDINAL; VAR destination: ARRAY OF CHAR);
  751. BEGIN
  752. Strings.Extract(source, startIndex, numberToExtract, destination)
  753. END Extract;
  754. PROCEDURE Concat (source1, source2: ARRAY OF CHAR; VAR destination: ARRAY OF CHAR);
  755. BEGIN
  756. Strings.Concat(source1, source2, destination);
  757. END Concat;
  758. (* The code for the four procedures above may be commented out if your
  759. compiler supports ISO PROCEDURE constant declarations and these declarations
  760. are made in the DEFINITION MODULE *)
  761. PROCEDURE Compare (stringVal1, stringVal2: ARRAY OF CHAR): INTEGER;
  762. BEGIN
  763. RETURN VAL(INTEGER, Strings.Compare(stringVal1, stringVal2)) - 1;
  764. END Compare;
  765. PROCEDURE ORDL (n: INT32): CARDINAL;
  766. BEGIN RETURN VAL(CARDINAL, n) END ORDL;
  767. PROCEDURE INTL (n: INT32): INTEGER;
  768. BEGIN RETURN VAL(INTEGER, n) END INTL;
  769. PROCEDURE INT (n: CARDINAL): INT32;
  770. BEGIN RETURN VAL(INT32, n) END INT;
  771. PROCEDURE CloseAll;
  772. VAR
  773. handle: CARDINAL;
  774. BEGIN
  775. FOR handle := 0 TO MaxFiles - 1 DO
  776. IF handle IN Handles THEN Close(Opened[handle]) END
  777. END;
  778. END CloseAll;
  779. PROCEDURE QuitExecution;
  780. BEGIN
  781. HALT
  782. END QuitExecution;
  783. BEGIN
  784. CheckRedirection; (* Not apparently available on many systems *)
  785. ProgramArgs.NextArg(); (* Not necessary on some systems *)
  786. GetTime(Hrs0, Mins0, Secs0, Hsecs0);
  787. Hrs1 := Hrs0; Mins1 := Mins0; Secs1 := Secs0; Hsecs1 := Hsecs0;
  788. Handles := BITSET{};
  789. Okay := FALSE; EOFChar := 04C;
  790. ALLOCATE(con, SYSTEM.TSIZE(FileRec));
  791. TermFile.Open(con^.ref, TermFile.read + TermFile.write + TermFile.text
  792. + TermFile.echo, res);
  793. con^.savedCh := 0C; con^.haveCh := FALSE; con^.self := con;
  794. con^.noOutput := FALSE; con^.noInput := FALSE; con^.textOK := TRUE;
  795. con^.eof := FALSE; con^.eol := FALSE;
  796. ALLOCATE(StdIn, SYSTEM.TSIZE(FileRec));
  797. StdIn^.ref := StdChans.StdInChan();
  798. StdIn^.savedCh := 0C; StdIn^.haveCh := FALSE; StdIn^.self := StdIn;
  799. StdIn^.noOutput := TRUE; StdIn^.noInput := FALSE; StdIn^.textOK := TRUE;
  800. StdIn^.eof := FALSE; StdIn^.eol := FALSE;
  801. ALLOCATE(StdOut, SYSTEM.TSIZE(FileRec));
  802. StdOut^.ref := StdChans.StdOutChan();
  803. StdOut^.savedCh := 0C; StdOut^.haveCh := FALSE; StdOut^.self := StdOut;
  804. StdOut^.noOutput := FALSE; StdOut^.noInput := TRUE; StdOut^.textOK := TRUE;
  805. StdOut^.eof := TRUE; StdOut^.eol := TRUE;
  806. ALLOCATE(err, SYSTEM.TSIZE(FileRec));
  807. err^.ref := StdChans.StdErrChan();
  808. err^.savedCh := 0C; err^.haveCh := FALSE; err^.self := err;
  809. err^.noOutput := FALSE; err^.noInput := TRUE; err^.textOK := TRUE;
  810. err^.eof := TRUE; err^.eol := TRUE;
  811. (*
  812. FINALLY (* For ISO compilers *)
  813. (* Preferably find some way to install CloseAll as an at-exit procedure *)
  814. CloseAll;
  815. *)
  816. END FileIO.