FileIO.mod 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  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 SearchFile (VAR f: File; envVar, fileName: ARRAY OF CHAR;
  164. newFile: BOOLEAN);
  165. VAR
  166. i, j: INTEGER;
  167. k: CARDINAL;
  168. c: CHAR;
  169. fname: ARRAY [0 .. NameLength] OF CHAR;
  170. path: ARRAY [0 .. NameLength] OF CHAR;
  171. BEGIN
  172. FOR k := 0 TO HIGH(envVar) DO envVar[k] := CAP(envVar[k]) END;
  173. GetEnv(envVar, path);
  174. i := 0;
  175. REPEAT
  176. j := 0;
  177. REPEAT
  178. c := path[i]; fname[j] := c; INC(i); INC(j)
  179. UNTIL (c = PathSep) OR (c = 0C);
  180. IF (j > 1) & (fname[j-2] = DirSep) THEN DEC(j) ELSE fname[j-1] := DirSep END;
  181. fname[j] := 0C; Concat(fname, fileName, fname);
  182. Open(f, fname, newFile);
  183. UNTIL (c = 0C) OR Okay
  184. END SearchFile;
  185. PROCEDURE ExtractDirectory (fullName: ARRAY OF CHAR;
  186. VAR directory: ARRAY OF CHAR);
  187. VAR
  188. i, start: CARDINAL;
  189. BEGIN
  190. start := 0; i := 0;
  191. WHILE (i <= HIGH(fullName)) & (fullName[i] # 0C) DO
  192. IF i <= HIGH(directory) THEN
  193. directory[i] := fullName[i];
  194. END;
  195. IF (fullName[i] = ":") OR (fullName[i] = DirSep) THEN start := i + 1 END;
  196. INC(i)
  197. END;
  198. IF start <= HIGH(directory) THEN directory[start] := 0C END
  199. END ExtractDirectory;
  200. PROCEDURE ExtractFileName (fullName: ARRAY OF CHAR;
  201. VAR fileName: ARRAY OF CHAR);
  202. VAR
  203. i, l, start: CARDINAL;
  204. BEGIN
  205. start := 0; l := 0;
  206. WHILE (l <= HIGH(fullName)) & (fullName[l] # 0C) DO
  207. IF (fullName[l] = ":") OR (fullName[l] = DirSep) THEN start := l + 1 END;
  208. INC(l)
  209. END;
  210. i := 0;
  211. WHILE (start < l) & (i <= HIGH(fileName)) DO
  212. fileName[i] := fullName[start]; INC(start); INC(i)
  213. END;
  214. IF i <= HIGH(fileName) THEN fileName[i] := 0C END
  215. END ExtractFileName;
  216. PROCEDURE AppendExtension (oldName, ext: ARRAY OF CHAR;
  217. VAR newName: ARRAY OF CHAR);
  218. VAR
  219. i, j: CARDINAL;
  220. fn: ARRAY [0 .. NameLength] OF CHAR;
  221. BEGIN
  222. ExtractDirectory(oldName, newName);
  223. ExtractFileName(oldName, fn);
  224. i := 0; j := 0;
  225. WHILE (i <= NameLength) & (fn[i] # 0C) DO
  226. IF fn[i] = "." THEN j := i + 1 END;
  227. INC(i)
  228. END;
  229. IF (j # i) (* then name did not end with "." *) OR (i = 0) THEN
  230. IF j # 0 THEN i := j - 1 END;
  231. IF (ext[0] # ".") & (ext[0] # 0C) THEN
  232. IF i <= NameLength THEN fn[i] := "."; INC(i) END
  233. END;
  234. j := 0;
  235. WHILE (j <= HIGH(ext)) & (ext[j] # 0C) & (i <= NameLength) DO
  236. fn[i] := ext[j]; INC(i); INC(j)
  237. END
  238. END;
  239. IF i <= NameLength THEN fn[i] := 0C END;
  240. Concat(newName, fn, newName)
  241. END AppendExtension;
  242. PROCEDURE ChangeExtension (oldName, ext: ARRAY OF CHAR;
  243. VAR newName: ARRAY OF CHAR);
  244. VAR
  245. i, j: CARDINAL;
  246. fn: ARRAY [0 .. NameLength] OF CHAR;
  247. BEGIN
  248. ExtractDirectory(oldName, newName);
  249. ExtractFileName(oldName, fn);
  250. i := 0; j := 0;
  251. WHILE (i <= NameLength) & (fn[i] # 0C) DO
  252. IF fn[i] = "." THEN j := i + 1 END;
  253. INC(i)
  254. END;
  255. IF j # 0 THEN i := j - 1 END;
  256. IF (ext[0] # ".") & (ext[0] # 0C) THEN
  257. IF i <= NameLength THEN fn[i] := "."; INC(i) END
  258. END;
  259. j := 0;
  260. WHILE (j <= HIGH(ext)) & (ext[j] # 0C) & (i <= NameLength) DO
  261. fn[i] := ext[j]; INC(i); INC(j)
  262. END;
  263. IF i <= NameLength THEN fn[i] := 0C END;
  264. Concat(newName, fn, newName)
  265. END ChangeExtension;
  266. PROCEDURE Length (f: File): INT32;
  267. (* ++++ implementation specific coercion routine may have to be used ++++ *)
  268. VAR
  269. pos: RndFile.FilePos;
  270. BEGIN
  271. IF NotFile(f)
  272. THEN
  273. Okay := FALSE; RETURN Long0
  274. ELSE
  275. Okay := TRUE;
  276. pos := RndFile.EndPos(f^.ref);
  277. (* ++++ GPM specific routine used ++++ *)
  278. RETURN VAL(INT32, pos)
  279. END;
  280. (*
  281. EXCEPT (* For ISO compilers *)
  282. Okay := FALSE; RETURN Long0
  283. *)
  284. END Length;
  285. PROCEDURE GetPos (f: File): INT32;
  286. (* ++++ implementation specific coercion routine may have to be used ++++ *)
  287. VAR
  288. pos: RndFile.FilePos;
  289. BEGIN
  290. IF NotFile(f)
  291. THEN
  292. Okay := FALSE; RETURN Long0
  293. ELSE
  294. Okay := TRUE;
  295. pos := RndFile.CurrentPos(f^.ref);
  296. (* ++++ GPM specific casting used ++++ *)
  297. RETURN VAL(INT32, pos)
  298. END;
  299. (*
  300. EXCEPT (* For ISO compilers *)
  301. Okay := FALSE; RETURN Long0
  302. *)
  303. END GetPos;
  304. PROCEDURE SetPos (f: File; pos: INT32);
  305. (* ++++ implementation specific coercion routine may have to be used ++++ *)
  306. BEGIN
  307. IF NotFile(f)
  308. THEN
  309. Okay := FALSE
  310. ELSE
  311. Okay := TRUE; f^.haveCh := FALSE;
  312. (* ++++ GPM specific routine used ++++ *)
  313. RndFile.SetPos(f^.ref, VAL(CARDINAL, pos));
  314. END;
  315. (*
  316. EXCEPT (* For ISO compilers *)
  317. Okay := FALSE; f^.haveCh := FALSE; RETURN
  318. *)
  319. END SetPos;
  320. PROCEDURE Reset (f: File);
  321. BEGIN
  322. IF NotFile(f)
  323. THEN
  324. Okay := FALSE
  325. ELSE
  326. SetPos(f, 0);
  327. IF Okay THEN
  328. f^.haveCh := FALSE; f^.eof := f^.noInput; f^.eol := f^.noInput
  329. END
  330. END
  331. END Reset;
  332. PROCEDURE Rewrite (f: File);
  333. BEGIN
  334. IF NotFile(f)
  335. THEN
  336. Okay := FALSE
  337. ELSE
  338. RndFile.Close(f^.ref);
  339. (* Flags below may have to be altered according to implementation *)
  340. RndFile.OpenClean(f^.ref, f^.name,
  341. RndFile.old + (* RndFile.text + *) RndFile.raw, res);
  342. Okay := res = RndFile.opened;
  343. IF ~ Okay
  344. THEN
  345. DEALLOCATE(f, SYSTEM.TSIZE(FileRec)); f := NIL
  346. ELSE
  347. f^.savedCh := 0C; f^.haveCh := FALSE;
  348. f^.eof := TRUE; f^.eol := TRUE;
  349. f^.noInput := TRUE; f^.noOutput := FALSE;
  350. END
  351. END;
  352. (*
  353. EXCEPT (* For ISO compilers *)
  354. Okay := FALSE; RETURN
  355. *)
  356. END Rewrite;
  357. PROCEDURE EndOfLine (f: File): BOOLEAN;
  358. BEGIN
  359. IF NotRead(f)
  360. THEN Okay := FALSE; RETURN TRUE
  361. ELSE Okay := TRUE; RETURN f^.eol OR f^.eof
  362. END
  363. END EndOfLine;
  364. PROCEDURE EndOfFile (f: File): BOOLEAN;
  365. BEGIN
  366. IF NotRead(f)
  367. THEN Okay := FALSE; RETURN TRUE
  368. ELSE Okay := TRUE; RETURN f^.eof
  369. END
  370. END EndOfFile;
  371. PROCEDURE Read (f: File; VAR ch: CHAR);
  372. BEGIN
  373. IF NotRead(f) THEN Okay := FALSE; ch := 0C; RETURN END;
  374. IF f^.haveCh OR f^.eof
  375. THEN
  376. ch := f^.savedCh; Okay := ch # 0C;
  377. ELSE
  378. Okay := TRUE;
  379. IF ~ f^.textOK (* Work around as best one can *)
  380. THEN RawIO.Read(f^.ref, ch)
  381. ELSE TextIO.ReadChar(f^.ref, ch);
  382. END;
  383. IF f^.textOK & (IOResult.ReadResult(f^.ref) = IOResult.endOfLine)
  384. THEN TextIO.SkipLine(f^.ref); ch := EOL
  385. ELSIF ch = LF (* Work around possible bug *) THEN ch := EOL
  386. END;
  387. IF IOResult.ReadResult(f^.ref) = IOResult.endOfInput THEN
  388. Okay := FALSE; ch := 0C;
  389. END;
  390. IF ch = EOFChar THEN Okay := FALSE; ch := 0C END;
  391. END;
  392. IF ~ Okay THEN ch := 0C END;
  393. f^.savedCh := ch; f^.haveCh := ~ Okay;
  394. f^.eof := ch = 0C; f^.eol := f^.eof OR (ch = EOL);
  395. END Read;
  396. PROCEDURE ReadAgain (f: File);
  397. BEGIN
  398. IF NotRead(f)
  399. THEN Okay := FALSE
  400. ELSE f^.haveCh := TRUE
  401. END
  402. END ReadAgain;
  403. PROCEDURE ReadLn (f: File);
  404. VAR
  405. ch: CHAR;
  406. BEGIN
  407. IF NotRead(f) THEN Okay := FALSE; RETURN END;
  408. WHILE ~ f^.eol DO Read(f, ch) END;
  409. f^.haveCh := FALSE; f^.eol := FALSE;
  410. END ReadLn;
  411. PROCEDURE ReadString (f: File; VAR str: ARRAY OF CHAR);
  412. VAR
  413. j: CARDINAL;
  414. ch: CHAR;
  415. BEGIN
  416. str[0] := 0C; j := 0;
  417. IF NotRead(f) THEN Okay := FALSE; RETURN END;
  418. REPEAT Read(f, ch) UNTIL (ch # " ") OR ~ Okay;
  419. IF Okay THEN
  420. WHILE ch >= " " DO
  421. IF j <= HIGH(str) THEN str[j] := ch END; INC(j);
  422. Read(f, ch);
  423. WHILE (ch = BS) OR (ch = DEL) DO
  424. IF j > 0 THEN DEC(j) END; Read(f, ch)
  425. END
  426. END;
  427. IF j <= HIGH(str) THEN str[j] := 0C END;
  428. Okay := j > 0; f^.haveCh := TRUE; f^.savedCh := ch;
  429. END
  430. END ReadString;
  431. PROCEDURE ReadLine (f: File; VAR str: ARRAY OF CHAR);
  432. VAR
  433. j: CARDINAL;
  434. ch: CHAR;
  435. BEGIN
  436. str[0] := 0C; j := 0;
  437. IF NotRead(f) THEN Okay := FALSE; RETURN END;
  438. Read(f, ch);
  439. IF Okay THEN
  440. WHILE ch >= " " DO
  441. IF j <= HIGH(str) THEN str[j] := ch END; INC(j);
  442. Read(f, ch);
  443. WHILE (ch = BS) OR (ch = DEL) DO
  444. IF j > 0 THEN DEC(j) END; Read(f, ch)
  445. END
  446. END;
  447. IF j <= HIGH(str) THEN str[j] := 0C END;
  448. Okay := j > 0; f^.haveCh := TRUE; f^.savedCh := ch;
  449. END
  450. END ReadLine;
  451. PROCEDURE ReadToken (f: File; VAR str: ARRAY OF CHAR);
  452. VAR
  453. j: CARDINAL;
  454. ch: CHAR;
  455. BEGIN
  456. str[0] := 0C; j := 0;
  457. IF NotRead(f) THEN Okay := FALSE; RETURN END;
  458. REPEAT Read(f, ch) UNTIL (ch > " ") OR ~ Okay;
  459. IF Okay THEN
  460. WHILE ch > " " DO
  461. IF j <= HIGH(str) THEN str[j] := ch END; INC(j);
  462. Read(f, ch);
  463. WHILE (ch = BS) OR (ch = DEL) DO
  464. IF j > 0 THEN DEC(j) END; Read(f, ch)
  465. END
  466. END;
  467. IF j <= HIGH(str) THEN str[j] := 0C END;
  468. Okay := j > 0; f^.haveCh := TRUE; f^.savedCh := ch;
  469. END
  470. END ReadToken;
  471. PROCEDURE ReadInt (f: File; VAR i: INTEGER);
  472. VAR
  473. Digit: INTEGER;
  474. j: CARDINAL;
  475. Negative: BOOLEAN;
  476. s: ARRAY [0 .. 80] OF CHAR;
  477. BEGIN
  478. i := 0; j := 0;
  479. IF NotRead(f) THEN Okay := FALSE; RETURN END;
  480. ReadToken(f, s);
  481. IF s[0] = "-" (* deal with sign *)
  482. THEN Negative := TRUE; INC(j)
  483. ELSE Negative := FALSE; IF s[0] = "+" THEN INC(j) END
  484. END;
  485. IF (s[j] < "0") OR (s[j] > "9") THEN Okay := FALSE END;
  486. WHILE (j <= 80) & (s[j] >= "0") & (s[j] <= "9") DO
  487. Digit := VAL(INTEGER, ORD(s[j]) - ORD("0"));
  488. IF i <= (MAX(INTEGER) - Digit) DIV 10
  489. THEN i := 10 * i + Digit
  490. ELSE Okay := FALSE
  491. END;
  492. INC(j)
  493. END;
  494. IF Negative THEN i := -i END;
  495. IF (j > 80) OR (s[j] # 0C) THEN Okay := FALSE END;
  496. IF ~ Okay THEN i := 0 END;
  497. END ReadInt;
  498. PROCEDURE ReadCard (f: File; VAR i: CARDINAL);
  499. VAR
  500. Digit: CARDINAL;
  501. j: CARDINAL;
  502. s: ARRAY [0 .. 80] OF CHAR;
  503. BEGIN
  504. i := 0; j := 0;
  505. IF NotRead(f) THEN Okay := FALSE; RETURN END;
  506. ReadToken(f, s);
  507. WHILE (j <= 80) & (s[j] >= "0") & (s[j] <= "9") DO
  508. Digit := ORD(s[j]) - ORD("0");
  509. IF i <= (MAX(CARDINAL) - Digit) DIV 10
  510. THEN i := 10 * i + Digit
  511. ELSE Okay := FALSE
  512. END;
  513. INC(j)
  514. END;
  515. IF (j > 80) OR (s[j] # 0C) THEN Okay := FALSE END;
  516. IF ~ Okay THEN i := 0 END;
  517. END ReadCard;
  518. PROCEDURE ReadBytes (f: File; VAR buf: ARRAY OF SYSTEM.BYTE; VAR len: CARDINAL);
  519. VAR
  520. TooMany: BOOLEAN;
  521. Wanted: CARDINAL;
  522. BEGIN
  523. IF NotRead(f) OR (f = con)
  524. THEN Okay := FALSE; len := 0;
  525. ELSE
  526. IF len = 0 THEN Okay := TRUE; RETURN END;
  527. TooMany := len - 1 > HIGH(buf);
  528. IF TooMany THEN Wanted := HIGH(buf) + 1 ELSE Wanted := len END;
  529. IOChan.RawRead(f^.ref, SYSTEM.ADR(buf), Wanted, Wanted);
  530. Okay := Wanted # 0;
  531. IF len # Wanted THEN Okay := FALSE END;
  532. len := Wanted;
  533. END;
  534. IF ~ Okay THEN f^.eof := TRUE END;
  535. IF TooMany THEN Okay := FALSE END;
  536. (*
  537. EXCEPT (* For ISO compilers *)
  538. Okay := FALSE; len := 0; RETURN
  539. *)
  540. END ReadBytes;
  541. PROCEDURE Write (f: File; ch: CHAR);
  542. BEGIN
  543. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  544. Okay := TRUE;
  545. IF ch = EOL
  546. THEN (* implementation may not support Text operations on all files *)
  547. IF f^.textOK
  548. THEN TextIO.WriteLn(f^.ref)
  549. ELSE ch := LF; RawIO.Write(f^.ref, ch)
  550. (* but you may have to write CR/LF or CR or LF *)
  551. END
  552. ELSE
  553. IF f^.textOK
  554. THEN TextIO.WriteChar(f^.ref, ch)
  555. ELSE RawIO.Write(f^.ref, ch)
  556. END
  557. END;
  558. (*
  559. EXCEPT (* For ISO compilers *)
  560. Okay := FALSE; RETURN
  561. *)
  562. END Write;
  563. PROCEDURE WriteLn (f: File);
  564. BEGIN
  565. IF NotWrite(f)
  566. THEN Okay := FALSE;
  567. ELSE Write(f, EOL)
  568. END
  569. END WriteLn;
  570. PROCEDURE WriteString (f: File; str: ARRAY OF CHAR);
  571. VAR
  572. pos: CARDINAL;
  573. BEGIN
  574. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  575. pos := 0;
  576. WHILE (pos <= HIGH(str)) & (str[pos] # 0C) DO
  577. Write(f, str[pos]); INC(pos)
  578. END
  579. END WriteString;
  580. PROCEDURE WriteText (f: File; text: ARRAY OF CHAR; len: INTEGER);
  581. VAR
  582. i, slen: INTEGER;
  583. BEGIN
  584. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  585. slen := LENGTH(text);
  586. FOR i := 0 TO len - 1 DO
  587. IF i < slen THEN Write(f, text[i]) ELSE Write(f, " ") END;
  588. END
  589. END WriteText;
  590. PROCEDURE WriteInt (f: File; n: INTEGER; wid: CARDINAL);
  591. VAR
  592. l, d: CARDINAL;
  593. x: INTEGER;
  594. t: ARRAY [1 .. 25] OF CHAR;
  595. sign: CHAR;
  596. BEGIN
  597. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  598. IF n < 0
  599. THEN sign := "-"; x := - n;
  600. ELSE sign := " "; x := n;
  601. END;
  602. l := 0;
  603. REPEAT
  604. d := x MOD 10; x := x DIV 10;
  605. INC(l); t[l] := CHR(ORD("0") + d);
  606. UNTIL x = 0;
  607. IF wid = 0 THEN Write(f, " ") END;
  608. WHILE wid > l + 1 DO Write(f, " "); DEC(wid); END;
  609. IF (sign = "-") OR (wid > l) THEN Write(f, sign); END;
  610. WHILE l > 0 DO Write(f, t[l]); DEC(l); END;
  611. END WriteInt;
  612. PROCEDURE WriteCard (f: File; n, wid: CARDINAL);
  613. VAR
  614. l, d: CARDINAL;
  615. t: ARRAY [1 .. 25] OF CHAR;
  616. BEGIN
  617. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  618. l := 0;
  619. REPEAT
  620. d := n MOD 10; n := n DIV 10;
  621. INC(l); t[l] := CHR(ORD("0") + d);
  622. UNTIL n = 0;
  623. IF wid = 0 THEN Write(f, " ") END;
  624. WHILE wid > l DO Write(f, " "); DEC(wid); END;
  625. WHILE l > 0 DO Write(f, t[l]); DEC(l); END;
  626. END WriteCard;
  627. PROCEDURE WriteBytes (f: File; VAR buf: ARRAY OF SYSTEM.BYTE; len: CARDINAL);
  628. VAR
  629. TooMany: BOOLEAN;
  630. BEGIN
  631. TooMany := (len > 0) & (len - 1 > HIGH(buf));
  632. IF NotWrite(f) OR (f = con) OR (f = err)
  633. THEN
  634. Okay := FALSE
  635. ELSE
  636. Okay := TRUE;
  637. IF TooMany THEN len := HIGH(buf) + 1 END;
  638. IOChan.RawWrite(f^.ref, SYSTEM.ADR(buf), len);
  639. END;
  640. IF TooMany THEN Okay := FALSE END;
  641. (*
  642. EXCEPT (* For ISO compilers *)
  643. Okay := FALSE; RETURN
  644. *)
  645. END WriteBytes;
  646. PROCEDURE GetDate (VAR Year, Month, Day: CARDINAL);
  647. VAR
  648. time: SysClock.DateTime;
  649. BEGIN
  650. SysClock.GetClock(time);
  651. Year := time.year;
  652. Month := time.month;
  653. Day := time.day;
  654. END GetDate;
  655. PROCEDURE GetTime (VAR Hrs, Mins, Secs, Hsecs: CARDINAL);
  656. VAR
  657. time: SysClock.DateTime;
  658. BEGIN
  659. SysClock.GetClock(time);
  660. Hrs := time.hour;
  661. Mins := time.minute;
  662. Secs := time.second;
  663. Hsecs := time.fractions;
  664. END GetTime;
  665. PROCEDURE Write2 (f: File; i: CARDINAL);
  666. BEGIN
  667. Write(f, CHR(i DIV 10 + ORD("0")));
  668. Write(f, CHR(i MOD 10 + ORD("0")));
  669. END Write2;
  670. PROCEDURE WriteDate (f: File);
  671. VAR
  672. Year, Month, Day: CARDINAL;
  673. BEGIN
  674. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  675. GetDate(Year, Month, Day);
  676. Write2(f, Day); Write(f, "/"); Write2(f, Month); Write(f, "/");
  677. WriteCard(f, Year, 1)
  678. END WriteDate;
  679. PROCEDURE WriteTime (f: File);
  680. VAR
  681. Hrs, Mins, Secs, Hsecs: CARDINAL;
  682. BEGIN
  683. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  684. GetTime(Hrs, Mins, Secs, Hsecs);
  685. Write2(f, Hrs); Write(f, ":"); Write2(f, Mins); Write(f, ":");
  686. Write2(f, Secs)
  687. END WriteTime;
  688. VAR
  689. Hrs0, Mins0, Secs0, Hsecs0: CARDINAL;
  690. Hrs1, Mins1, Secs1, Hsecs1: CARDINAL;
  691. PROCEDURE WriteElapsedTime (f: File);
  692. VAR
  693. Hrs, Mins, Secs, Hsecs, s, hs: CARDINAL;
  694. BEGIN
  695. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  696. GetTime(Hrs, Mins, Secs, Hsecs);
  697. WriteString(f, "Elapsed time: ");
  698. IF Hrs >= Hrs1
  699. THEN s := (Hrs - Hrs1) * 3600 + (Mins - Mins1) * 60 + Secs - Secs1
  700. ELSE s := (Hrs + 24 - Hrs1) * 3600 + (Mins - Mins1) * 60 + Secs - Secs1
  701. END;
  702. IF Hsecs >= Hsecs1
  703. THEN hs := Hsecs - Hsecs1
  704. ELSE hs := (Hsecs + 100) - Hsecs1; DEC(s);
  705. END;
  706. WriteCard(f, s, 1); Write(f, ".");
  707. Write2(f, hs); WriteString(f, " s"); WriteLn(f);
  708. Hrs1 := Hrs; Mins1 := Mins; Secs1 := Secs; Hsecs1 := Hsecs;
  709. END WriteElapsedTime;
  710. PROCEDURE WriteExecutionTime (f: File);
  711. VAR
  712. Hrs, Mins, Secs, Hsecs, s, hs: CARDINAL;
  713. BEGIN
  714. IF NotWrite(f) THEN Okay := FALSE; RETURN END;
  715. GetTime(Hrs, Mins, Secs, Hsecs);
  716. WriteString(f, "Execution time: ");
  717. IF Hrs >= Hrs0
  718. THEN s := (Hrs - Hrs0) * 3600 + (Mins - Mins0) * 60 + Secs - Secs0
  719. ELSE s := (Hrs + 24 - Hrs0) * 3600 + (Mins - Mins0) * 60 + Secs - Secs0
  720. END;
  721. IF Hsecs >= Hsecs0
  722. THEN hs := Hsecs - Hsecs0
  723. ELSE hs := (Hsecs + 100) - Hsecs0; DEC(s);
  724. END;
  725. WriteCard(f, s, 1); Write(f, "."); Write2(f, hs);
  726. WriteString(f, " s"); WriteLn(f);
  727. END WriteExecutionTime;
  728. (* The code for the next four procedures below may be commented out if your
  729. compiler supports ISO PROCEDURE constant declarations and these declarations
  730. are made in the DEFINITION MODULE *)
  731. PROCEDURE SLENGTH (stringVal: ARRAY OF CHAR): CARDINAL;
  732. BEGIN
  733. RETURN LENGTH(stringVal)
  734. END SLENGTH;
  735. PROCEDURE Assign (source: ARRAY OF CHAR; VAR destination: ARRAY OF CHAR);
  736. BEGIN
  737. (* Be careful - some libraries have the parameters reversed! *)
  738. Strings.Assign(source, destination)
  739. END Assign;
  740. PROCEDURE Extract (source: ARRAY OF CHAR; startIndex: CARDINAL;
  741. numberToExtract: CARDINAL; VAR destination: ARRAY OF CHAR);
  742. BEGIN
  743. Strings.Extract(source, startIndex, numberToExtract, destination)
  744. END Extract;
  745. PROCEDURE Concat (source1, source2: ARRAY OF CHAR; VAR destination: ARRAY OF CHAR);
  746. BEGIN
  747. Strings.Concat(source1, source2, destination);
  748. END Concat;
  749. (* The code for the four procedures above may be commented out if your
  750. compiler supports ISO PROCEDURE constant declarations and these declarations
  751. are made in the DEFINITION MODULE *)
  752. PROCEDURE Compare (stringVal1, stringVal2: ARRAY OF CHAR): INTEGER;
  753. BEGIN
  754. RETURN VAL(INTEGER, Strings.Compare(stringVal1, stringVal2)) - 1;
  755. END Compare;
  756. PROCEDURE ORDL (n: INT32): CARDINAL;
  757. BEGIN RETURN VAL(CARDINAL, n) END ORDL;
  758. PROCEDURE INTL (n: INT32): INTEGER;
  759. BEGIN RETURN VAL(INTEGER, n) END INTL;
  760. PROCEDURE INT (n: CARDINAL): INT32;
  761. BEGIN RETURN VAL(INT32, n) END INT;
  762. PROCEDURE CloseAll;
  763. VAR
  764. handle: CARDINAL;
  765. BEGIN
  766. FOR handle := 0 TO MaxFiles - 1 DO
  767. IF handle IN Handles THEN Close(Opened[handle]) END
  768. END;
  769. END CloseAll;
  770. PROCEDURE QuitExecution;
  771. BEGIN
  772. HALT
  773. END QuitExecution;
  774. BEGIN
  775. CheckRedirection; (* Not apparently available on many systems *)
  776. ProgramArgs.NextArg(); (* Not necessary on some systems *)
  777. GetTime(Hrs0, Mins0, Secs0, Hsecs0);
  778. Hrs1 := Hrs0; Mins1 := Mins0; Secs1 := Secs0; Hsecs1 := Hsecs0;
  779. Handles := BITSET{};
  780. Okay := FALSE; EOFChar := 04C;
  781. ALLOCATE(con, SYSTEM.TSIZE(FileRec));
  782. TermFile.Open(con^.ref, TermFile.read + TermFile.write + TermFile.text
  783. + TermFile.echo, res);
  784. con^.savedCh := 0C; con^.haveCh := FALSE; con^.self := con;
  785. con^.noOutput := FALSE; con^.noInput := FALSE; con^.textOK := TRUE;
  786. con^.eof := FALSE; con^.eol := FALSE;
  787. ALLOCATE(StdIn, SYSTEM.TSIZE(FileRec));
  788. StdIn^.ref := StdChans.StdInChan();
  789. StdIn^.savedCh := 0C; StdIn^.haveCh := FALSE; StdIn^.self := StdIn;
  790. StdIn^.noOutput := TRUE; StdIn^.noInput := FALSE; StdIn^.textOK := TRUE;
  791. StdIn^.eof := FALSE; StdIn^.eol := FALSE;
  792. ALLOCATE(StdOut, SYSTEM.TSIZE(FileRec));
  793. StdOut^.ref := StdChans.StdOutChan();
  794. StdOut^.savedCh := 0C; StdOut^.haveCh := FALSE; StdOut^.self := StdOut;
  795. StdOut^.noOutput := FALSE; StdOut^.noInput := TRUE; StdOut^.textOK := TRUE;
  796. StdOut^.eof := TRUE; StdOut^.eol := TRUE;
  797. ALLOCATE(err, SYSTEM.TSIZE(FileRec));
  798. err^.ref := StdChans.StdErrChan();
  799. err^.savedCh := 0C; err^.haveCh := FALSE; err^.self := err;
  800. err^.noOutput := FALSE; err^.noInput := TRUE; err^.textOK := TRUE;
  801. err^.eof := TRUE; err^.eol := TRUE;
  802. (*
  803. FINALLY (* For ISO compilers *)
  804. (* Preferably find some way to install CloseAll as an at-exit procedure *)
  805. CloseAll;
  806. *)
  807. END FileIO.