Streams.mod 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. #
  2. (*
  3. (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands.
  4. See the copyright notice in the ACK home directory, in the file "Copyright".
  5. *)
  6. (*$R-*)
  7. IMPLEMENTATION MODULE Streams;
  8. (*
  9. Module: Stream Input/Output
  10. Author: Ceriel J.H. Jacobs
  11. Version: $Id$
  12. Implementation for Unix
  13. *)
  14. FROM SYSTEM IMPORT BYTE, ADR;
  15. FROM Epilogue IMPORT CallAtEnd;
  16. FROM Storage IMPORT Allocate, Available;
  17. IMPORT StripUnix;
  18. CONST BUFSIZ = 1024; (* tunable *)
  19. TYPE IOB = RECORD
  20. kind: StreamKind;
  21. mode: StreamMode;
  22. eof: BOOLEAN;
  23. buffering: StreamBuffering;
  24. next : Stream;
  25. fildes: INTEGER;
  26. cnt, maxcnt: INTEGER;
  27. bufferedcnt: INTEGER;
  28. buf: ARRAY[1..BUFSIZ] OF BYTE;
  29. END;
  30. Stream = POINTER TO IOB;
  31. VAR
  32. ibuf, obuf, ebuf: IOB;
  33. head: Stream;
  34. PROCEDURE getstruct(VAR stream: Stream);
  35. BEGIN
  36. stream := head;
  37. WHILE (stream # NIL) AND (stream^.kind # none) DO
  38. stream := stream^.next;
  39. END;
  40. IF stream = NIL THEN
  41. IF NOT Available(SIZE(IOB)) THEN
  42. RETURN;
  43. END;
  44. Allocate(stream,SIZE(IOB));
  45. stream^.next := head;
  46. head := stream;
  47. END;
  48. END getstruct;
  49. PROCEDURE freestruct(stream: Stream);
  50. BEGIN
  51. stream^.kind := none;
  52. END freestruct;
  53. PROCEDURE OpenStream(VAR stream: Stream;
  54. filename: ARRAY OF CHAR;
  55. kind: StreamKind;
  56. mode: StreamMode;
  57. VAR result: StreamResult);
  58. VAR fd: INTEGER;
  59. i: CARDINAL;
  60. BEGIN
  61. IF kind = none THEN
  62. result := illegaloperation;
  63. RETURN;
  64. END;
  65. getstruct(stream);
  66. IF stream = NIL THEN
  67. result := nomemory;
  68. RETURN;
  69. END;
  70. WITH stream^ DO
  71. FOR i := 0 TO HIGH(filename) DO
  72. buf[i+1] := BYTE(filename[i]);
  73. END;
  74. buf[HIGH(filename)+2] := BYTE(0C);
  75. END;
  76. IF (mode = reading) THEN
  77. fd := StripUnix.open(ADR(stream^.buf), 0);
  78. ELSE
  79. fd := -1;
  80. IF (mode = appending) THEN
  81. fd := StripUnix.open(ADR(stream^.buf), 1);
  82. IF fd >= 0 THEN
  83. IF (StripUnix.lseek(fd, 0D , 2) < 0D) THEN ; END;
  84. END;
  85. END;
  86. IF fd < 0 THEN
  87. fd := StripUnix.creat(ADR(stream^.buf), 666B);
  88. END;
  89. END;
  90. IF fd < 0 THEN
  91. result := openfailed;
  92. freestruct(stream);
  93. stream := NIL;
  94. RETURN;
  95. END;
  96. result := succeeded;
  97. stream^.fildes := fd;
  98. stream^.kind := kind;
  99. stream^.mode := mode;
  100. stream^.buffering := blockbuffered;
  101. stream^.bufferedcnt := BUFSIZ;
  102. stream^.maxcnt := 0;
  103. stream^.eof := FALSE;
  104. IF mode = reading THEN
  105. stream^.cnt := 1;
  106. ELSE
  107. stream^.cnt := 0;
  108. END;
  109. END OpenStream;
  110. PROCEDURE SetStreamBuffering( stream: Stream;
  111. b: StreamBuffering;
  112. VAR result: StreamResult);
  113. BEGIN
  114. result := succeeded;
  115. IF (stream = NIL) OR (stream^.kind = none) THEN
  116. result := nostream;
  117. RETURN;
  118. END;
  119. IF (stream^.mode = reading) OR
  120. ((b = linebuffered) AND (stream^.kind = binary)) THEN
  121. result := illegaloperation;
  122. RETURN;
  123. END;
  124. FlushStream(stream, result);
  125. IF b = unbuffered THEN
  126. stream^.bufferedcnt := 1;
  127. END;
  128. stream^.buffering := b;
  129. END SetStreamBuffering;
  130. PROCEDURE FlushStream(stream: Stream; VAR result: StreamResult);
  131. VAR cnt1: INTEGER;
  132. BEGIN
  133. result := succeeded;
  134. IF (stream = NIL) OR (stream^.kind = none) THEN
  135. result := nostream;
  136. RETURN;
  137. END;
  138. WITH stream^ DO
  139. IF mode = reading THEN
  140. result := illegaloperation;
  141. RETURN;
  142. END;
  143. IF (cnt > 0) THEN
  144. cnt1 := cnt;
  145. cnt := 0;
  146. IF StripUnix.write(fildes, ADR(buf), cnt1) < 0 THEN END;
  147. END;
  148. END;
  149. END FlushStream;
  150. PROCEDURE CloseStream(VAR stream: Stream; VAR result: StreamResult);
  151. BEGIN
  152. IF (stream # NIL) AND (stream^.kind # none) THEN
  153. result := succeeded;
  154. IF stream^.mode # reading THEN
  155. FlushStream(stream, result);
  156. END;
  157. IF StripUnix.close(stream^.fildes) < 0 THEN ; END;
  158. freestruct(stream);
  159. ELSE
  160. result := nostream;
  161. END;
  162. stream := NIL;
  163. END CloseStream;
  164. PROCEDURE EndOfStream(stream: Stream; VAR result: StreamResult): BOOLEAN;
  165. BEGIN
  166. result := succeeded;
  167. IF (stream = NIL) OR (stream^.kind = none) THEN
  168. result := nostream;
  169. RETURN FALSE;
  170. END;
  171. IF stream^.mode # reading THEN
  172. result := illegaloperation;
  173. RETURN FALSE;
  174. END;
  175. IF stream^.eof THEN RETURN TRUE; END;
  176. RETURN (CHAR(NextByte(stream)) = 0C) AND stream^.eof;
  177. END EndOfStream;
  178. PROCEDURE FlushLineBuffers();
  179. VAR s: Stream;
  180. result: StreamResult;
  181. BEGIN
  182. s := head;
  183. WHILE s # NIL DO
  184. IF (s^.kind # none) AND (s^.buffering = linebuffered) THEN
  185. FlushStream(s, result);
  186. END;
  187. s := s^.next;
  188. END;
  189. END FlushLineBuffers;
  190. PROCEDURE NextByte(stream: Stream): BYTE;
  191. VAR c: BYTE;
  192. BEGIN
  193. WITH stream^ DO
  194. IF cnt <= maxcnt THEN
  195. c := buf[cnt];
  196. ELSE
  197. IF eof THEN RETURN BYTE(0C); END;
  198. IF stream = InputStream THEN
  199. FlushLineBuffers();
  200. END;
  201. maxcnt := StripUnix.read(fildes, ADR(buf), bufferedcnt);
  202. cnt := 1;
  203. IF maxcnt <= 0 THEN
  204. eof := TRUE;
  205. c := BYTE(0C);
  206. ELSE
  207. c := buf[1];
  208. END;
  209. END;
  210. END;
  211. RETURN c;
  212. END NextByte;
  213. PROCEDURE Read(stream: Stream; VAR ch: CHAR; VAR result: StreamResult);
  214. VAR EoF: BOOLEAN;
  215. BEGIN
  216. ch := 0C;
  217. EoF := EndOfStream(stream, result);
  218. IF result # succeeded THEN RETURN; END;
  219. IF EoF THEN
  220. result := endoffile;
  221. RETURN;
  222. END;
  223. WITH stream^ DO
  224. ch := CHAR(buf[cnt]);
  225. INC(cnt);
  226. END;
  227. END Read;
  228. PROCEDURE ReadByte(stream: Stream; VAR byte: BYTE; VAR result: StreamResult);
  229. VAR EoF: BOOLEAN;
  230. BEGIN
  231. byte := BYTE(0C);
  232. EoF := EndOfStream(stream, result);
  233. IF result # succeeded THEN RETURN; END;
  234. IF EoF THEN
  235. result := endoffile;
  236. RETURN;
  237. END;
  238. WITH stream^ DO
  239. byte := buf[cnt];
  240. INC(cnt);
  241. END;
  242. END ReadByte;
  243. PROCEDURE ReadBytes(stream: Stream;
  244. VAR bytes: ARRAY OF BYTE;
  245. VAR result: StreamResult);
  246. VAR i: CARDINAL;
  247. BEGIN
  248. FOR i := 0 TO HIGH(bytes) DO
  249. ReadByte(stream, bytes[i], result);
  250. END;
  251. END ReadBytes;
  252. PROCEDURE Write(stream: Stream; ch: CHAR; VAR result: StreamResult);
  253. BEGIN
  254. IF (stream = NIL) OR (stream^.kind = none) THEN
  255. result := nostream;
  256. RETURN;
  257. END;
  258. IF (stream^.kind # text) OR (stream^.mode = reading) THEN
  259. result := illegaloperation;
  260. RETURN;
  261. END;
  262. WITH stream^ DO
  263. INC(cnt);
  264. buf[cnt] := BYTE(ch);
  265. IF (cnt >= bufferedcnt) OR
  266. ((ch = 12C) AND (buffering = linebuffered))
  267. THEN
  268. FlushStream(stream, result);
  269. END;
  270. END;
  271. END Write;
  272. PROCEDURE WriteByte(stream: Stream; byte: BYTE; VAR result: StreamResult);
  273. BEGIN
  274. IF (stream = NIL) OR (stream^.kind = none) THEN
  275. result := nostream;
  276. RETURN;
  277. END;
  278. IF (stream^.kind # binary) OR (stream^.mode = reading) THEN
  279. result := illegaloperation;
  280. RETURN;
  281. END;
  282. WITH stream^ DO
  283. INC(cnt);
  284. buf[cnt] := byte;
  285. IF cnt >= bufferedcnt THEN
  286. FlushStream(stream, result);
  287. END;
  288. END;
  289. END WriteByte;
  290. PROCEDURE WriteBytes(stream: Stream; bytes: ARRAY OF BYTE; VAR result: StreamResult);
  291. VAR i: CARDINAL;
  292. BEGIN
  293. FOR i := 0 TO HIGH(bytes) DO
  294. WriteByte(stream, bytes[i], result);
  295. END;
  296. END WriteBytes;
  297. PROCEDURE EndIt;
  298. VAR h, h1 : Stream;
  299. result: StreamResult;
  300. BEGIN
  301. h := head;
  302. WHILE h # NIL DO
  303. h1 := h;
  304. CloseStream(h1, result);
  305. h := h^.next;
  306. END;
  307. END EndIt;
  308. PROCEDURE GetPosition(s: Stream; VAR position: LONGINT;
  309. VAR result: StreamResult);
  310. BEGIN
  311. IF (s = NIL) OR (s^.kind = none) THEN
  312. result := illegaloperation;
  313. RETURN;
  314. END;
  315. IF (s^.mode # reading) THEN FlushStream(s, result); END;
  316. position := StripUnix.lseek(s^.fildes, 0D, 1);
  317. IF position < 0D THEN
  318. result := illegaloperation;
  319. RETURN;
  320. END;
  321. IF s^.mode = reading THEN
  322. position := position - LONG(s^.maxcnt - s^.cnt + 1);
  323. END;
  324. END GetPosition;
  325. PROCEDURE SetPosition(s: Stream; position: LONGINT; VAR result: StreamResult);
  326. VAR currpos: LONGINT;
  327. BEGIN
  328. currpos := 0D;
  329. IF (s = NIL) OR (s^.kind = none) THEN
  330. result := nostream;
  331. RETURN;
  332. END;
  333. IF (s^.mode # reading) THEN
  334. FlushStream(s, result);
  335. ELSE
  336. s^.maxcnt := 0;
  337. s^.eof := FALSE;
  338. END;
  339. IF s^.mode = appending THEN
  340. currpos := StripUnix.lseek(s^.fildes, 0D, 1);
  341. IF currpos < 0D THEN
  342. result := illegaloperation;
  343. RETURN;
  344. END;
  345. END;
  346. IF position < currpos THEN
  347. result := illegaloperation;
  348. RETURN;
  349. END;
  350. currpos := StripUnix.lseek(s^.fildes, position, 0);
  351. IF currpos < 0D THEN
  352. result := illegaloperation;
  353. RETURN;
  354. END;
  355. result := succeeded;
  356. END SetPosition;
  357. PROCEDURE isatty(stream: Stream; VAR result: StreamResult): BOOLEAN;
  358. BEGIN
  359. IF (stream = NIL) OR (stream^.kind = none) THEN
  360. result := nostream;
  361. RETURN FALSE;
  362. END;
  363. IF (StripUnix.isatty(stream^.fildes) = 0) THEN
  364. RETURN FALSE;
  365. END;
  366. RETURN TRUE;
  367. END isatty;
  368. PROCEDURE InitStreams;
  369. VAR result: StreamResult;
  370. BEGIN
  371. InputStream := ADR(ibuf);
  372. OutputStream := ADR(obuf);
  373. ErrorStream := ADR(ebuf);
  374. WITH ibuf DO
  375. kind := text;
  376. mode := reading;
  377. eof := FALSE;
  378. next := ADR(obuf);
  379. fildes := 0;
  380. maxcnt := 0;
  381. cnt := 1;
  382. bufferedcnt := BUFSIZ;
  383. END;
  384. WITH obuf DO
  385. kind := text;
  386. mode := writing;
  387. eof := TRUE;
  388. next := ADR(ebuf);
  389. fildes := 1;
  390. maxcnt := 0;
  391. cnt := 0;
  392. bufferedcnt := BUFSIZ;
  393. IF isatty(OutputStream, result) THEN
  394. buffering := linebuffered;
  395. ELSE
  396. buffering := blockbuffered;
  397. END;
  398. END;
  399. WITH ebuf DO
  400. kind := text;
  401. mode := writing;
  402. eof := TRUE;
  403. next := NIL;
  404. fildes := 2;
  405. maxcnt := 0;
  406. cnt := 0;
  407. bufferedcnt := BUFSIZ;
  408. IF isatty(ErrorStream, result) THEN
  409. buffering := linebuffered;
  410. ELSE
  411. buffering := blockbuffered;
  412. END;
  413. END;
  414. head := InputStream;
  415. IF CallAtEnd(EndIt) THEN ; END;
  416. END InitStreams;
  417. BEGIN
  418. InitStreams
  419. END Streams.