FileIO-3.mod 25 KB

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