Streams.mod 9.5 KB

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