StreamUtilities.Mod 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. MODULE StreamUtilities; (** AUTHOR "Patrick Hunziker"; PURPOSE "stream utilities"; *)
  2. (* daisychaining of readers or writers with 'logging side-stream', or with size limitation*)
  3. IMPORT Streams, SYSTEM (*, KernelLog, Commands*);
  4. CONST
  5. ReaderBufSize = Streams.DefaultReaderSize;
  6. WriterBufSize = Streams.DefaultWriterSize;
  7. (* writer that can daisychained with another writer that extracts a copy of the data flow to a monitor stream*)
  8. TYPE WriterMonitor* = OBJECT (Streams.Writer);
  9. VAR out, monitor : Streams.Writer;
  10. PROCEDURE &Init*(out:Streams.Writer; monitor: Streams.Writer);
  11. BEGIN
  12. InitWriter(Sender, WriterBufSize);
  13. SELF.out := out;
  14. SELF.monitor:=monitor;
  15. Reset;
  16. END Init;
  17. PROCEDURE Sender(CONST outBuf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: WORD);
  18. BEGIN
  19. out.Bytes(outBuf, ofs, len);
  20. monitor.Bytes(outBuf, ofs, len);
  21. INC(sent,len);
  22. IF propagate THEN out.Update; monitor.Update END;
  23. res:=out.res;
  24. END Sender;
  25. PROCEDURE CanSetPos*(): BOOLEAN;
  26. BEGIN RETURN out.CanSetPos()
  27. END CanSetPos;
  28. PROCEDURE SetPos*(pos: Streams.Position);
  29. BEGIN Reset; out.SetPos(pos);
  30. END SetPos;
  31. PROCEDURE Pos*(): Streams.Position;
  32. BEGIN RETURN out.Pos()
  33. END Pos;
  34. END WriterMonitor;
  35. TYPE Encryptor*=PROCEDURE{DELEGATE}( VAR buf: ARRAY OF CHAR; pos, len: LONGINT );
  36. (* encrypting writer that can daisychained with another writer *)
  37. TYPE EncryptingWriter* = OBJECT (Streams.Writer);
  38. VAR out : Streams.Writer; encrypt: Encryptor; buf: POINTER TO ARRAY OF CHAR;
  39. PROCEDURE &Init*(out:Streams.Writer; encrypt:Encryptor);
  40. BEGIN
  41. InitWriter(Sender, WriterBufSize);
  42. NEW(buf, WriterBufSize);
  43. SELF.out := out;
  44. SELF.encrypt:=encrypt;
  45. Reset;
  46. END Init;
  47. PROCEDURE Sender(CONST outBuf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: WORD);
  48. VAR i:LONGINT;
  49. BEGIN
  50. FOR i:=0 TO len-1 DO buf[i]:=outBuf[ofs+i] END;
  51. IF encrypt#NIL THEN encrypt(buf^,0,len); END;
  52. out.Bytes(buf^, 0, len);
  53. INC(sent,len);
  54. IF propagate THEN out.Update END;
  55. res:=out.res;
  56. END Sender;
  57. PROCEDURE Pos*(): Streams.Position;
  58. BEGIN RETURN out.Pos()
  59. END Pos;
  60. END EncryptingWriter;
  61. TYPE Decryptor*=PROCEDURE{DELEGATE}( VAR buf: ARRAY OF CHAR; pos, len: LONGINT );
  62. (* reader that can daisychained with another reader that reads and decrypts a stream*)
  63. DecryptingReader* = OBJECT(Streams.Reader)
  64. VAR in: Streams.Reader;
  65. decrypt:Decryptor;
  66. PROCEDURE &Init*(in: Streams.Reader; decrypt: Decryptor);
  67. BEGIN
  68. InitReader(Receiver, ReaderBufSize);
  69. SELF.in := in;
  70. SELF.decrypt:=decrypt;
  71. END Init;
  72. PROCEDURE Receiver(VAR inBuf: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len, res: LONGINT);
  73. BEGIN
  74. ASSERT((size > 0) & (min <= size) & (min >= 0));
  75. in.Bytes(inBuf, ofs, size, len);
  76. IF decrypt#NIL THEN decrypt(inBuf, ofs, len); END;
  77. INC(received,len);
  78. res:=in.res;
  79. END Receiver;
  80. PROCEDURE Pos*(): Streams.Position;
  81. BEGIN RETURN in.Pos()
  82. END Pos;
  83. END DecryptingReader;
  84. TYPE
  85. WriteEntry = POINTER TO RECORD buf: POINTER TO ARRAY OF CHAR; len: LONGINT; propagate: BOOLEAN; next: WriteEntry END;
  86. (* writer that writes asynchronously - updates are delayed until the thread is ready for it
  87. useful to avoid file writing delay problems.
  88. Caution: Pos() and SetPos() enforce synchronisation.
  89. *)
  90. AsynchronousWriter= OBJECT(Streams.Writer)
  91. VAR
  92. first, last: WriteEntry;
  93. free: WriteEntry;
  94. size: LONGINT;
  95. sender: Streams.Sender;
  96. PROCEDURE & InitWriter(sender: Streams.Sender; size: LONGINT);
  97. BEGIN
  98. first := NIL; last := NIL; free := NIL;
  99. SELF.size := size;
  100. SELF.sender := sender;
  101. InitWriter^(Add, size);
  102. Reset;
  103. END InitWriter;
  104. PROCEDURE Add(CONST outBuf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: WORD);
  105. VAR entry: WriteEntry;
  106. BEGIN
  107. IF ~ToLastEntry(outBuf, ofs, len, propagate, res) THEN
  108. entry := GetFreeEntry(MAX(SELF.size, len));
  109. SYSTEM.MOVE(ADDRESS OF outBuf[ofs], ADDRESS OF entry.buf[0], len);
  110. entry.len := len;
  111. entry.propagate := propagate;
  112. PutEntry(entry)
  113. END;
  114. END Add;
  115. (* check last entry for enough space to host data. If available, remove from list and return *)
  116. PROCEDURE ToLastEntry(CONST outBuf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: WORD): BOOLEAN;
  117. BEGIN{EXCLUSIVE}
  118. IF last = NIL THEN RETURN FALSE
  119. ELSIF last.propagate # propagate THEN RETURN FALSE
  120. ELSIF (last.len + len > LEN(last.buf^)) THEN RETURN FALSE
  121. ELSE
  122. SYSTEM.MOVE(ADDRESS OF outBuf[ofs], ADDRESS OF last.buf[last.len], len);
  123. INC(last.len, len);
  124. res := 0;
  125. RETURN TRUE
  126. END;
  127. END ToLastEntry;
  128. PROCEDURE GetFreeEntry(len: LONGINT): WriteEntry;
  129. VAR entry: WriteEntry;
  130. BEGIN{EXCLUSIVE}
  131. IF free = NIL THEN NEW(entry) ELSE entry := free; free := free.next END;
  132. IF (entry.buf = NIL) OR (LEN(entry.buf)< len) THEN NEW(entry.buf, len) END;
  133. entry.len := 0; entry.propagate := FALSE;
  134. RETURN entry
  135. END GetFreeEntry;
  136. PROCEDURE ReturnEntry(entry: WriteEntry);
  137. BEGIN{EXCLUSIVE}
  138. entry.next := free;
  139. free := entry
  140. END ReturnEntry;
  141. PROCEDURE PutEntry(entry: WriteEntry);
  142. BEGIN{EXCLUSIVE}
  143. IF last = NIL THEN first := entry; last := entry
  144. ELSE last.next := entry; last := entry END;
  145. entry.next := NIL;
  146. END PutEntry;
  147. PROCEDURE GetEntry(): WriteEntry;
  148. VAR entry: WriteEntry;
  149. BEGIN{EXCLUSIVE}
  150. AWAIT(first # NIL);
  151. entry := first;
  152. first := first.next;
  153. IF first = NIL THEN last := NIL END;
  154. RETURN entry
  155. END GetEntry;
  156. PROCEDURE ProcessWrites;
  157. VAR entry: WriteEntry;
  158. BEGIN
  159. LOOP
  160. entry := GetEntry();
  161. sender(entry.buf^, 0, entry.len, entry.propagate, res);
  162. ReturnEntry(entry);
  163. END;
  164. END ProcessWrites;
  165. BEGIN{ACTIVE}
  166. ProcessWrites;
  167. END AsynchronousWriter;
  168. AsynchronousForwarder* = OBJECT (AsynchronousWriter);
  169. VAR out: Streams.Writer;
  170. PROCEDURE &Init*(out:Streams.Writer);
  171. BEGIN
  172. SELF.out := out;
  173. InitWriter(Sender, WriterBufSize);
  174. END Init;
  175. PROCEDURE CanSetPos*(): BOOLEAN;
  176. BEGIN RETURN out.CanSetPos()
  177. END CanSetPos;
  178. PROCEDURE SetPos*(pos: Streams.Position);
  179. BEGIN{EXCLUSIVE}
  180. AWAIT(first = NIL);
  181. Reset; out.SetPos(pos);
  182. END SetPos;
  183. PROCEDURE Pos*(): Streams.Position;
  184. BEGIN{EXCLUSIVE}
  185. AWAIT(first = NIL);
  186. RETURN out.Pos()
  187. END Pos;
  188. PROCEDURE Sender(CONST outBuf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: WORD);
  189. BEGIN
  190. out.Bytes(outBuf, ofs, len);
  191. IF propagate THEN out.Update END;
  192. INC(sent,len);
  193. res:=out.res;
  194. END Sender;
  195. END AsynchronousForwarder;
  196. (* reader that can daisychained with another reader that extracts a copy of the data flow to a monitor stream*)
  197. ReaderMonitor* = OBJECT(Streams.Reader)
  198. VAR in: Streams.Reader;
  199. monitor: Streams.Writer;
  200. PROCEDURE &Init*(in: Streams.Reader; monitor: Streams.Writer);
  201. BEGIN
  202. InitReader(Receiver, ReaderBufSize);
  203. SELF.in := in;
  204. SELF.monitor:=monitor;
  205. END Init;
  206. PROCEDURE Receiver(VAR buf: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len, res: LONGINT);
  207. BEGIN
  208. ASSERT((size > 0) & (min <= size) & (min >= 0));
  209. size := MAX(min,MIN(in.Available(),size));
  210. in.Bytes(buf, ofs, size, len);
  211. INC(received,len);
  212. res:=in.res;
  213. monitor.Bytes(buf, ofs, len);
  214. monitor.Update;
  215. END Receiver;
  216. PROCEDURE CanSetPos*(): BOOLEAN;
  217. BEGIN RETURN in.CanSetPos()
  218. END CanSetPos;
  219. PROCEDURE SetPos*(pos: Streams.Position);
  220. BEGIN Reset; in.SetPos(pos)
  221. END SetPos;
  222. PROCEDURE Pos*(): Streams.Position;
  223. BEGIN RETURN in.Pos()
  224. END Pos;
  225. END ReaderMonitor;
  226. LimitedWriter* = OBJECT (Streams.Writer);
  227. VAR out : Streams.Writer;
  228. size, remain-: LONGINT;
  229. PROCEDURE &Init*(out:Streams.Writer; size: LONGINT);
  230. BEGIN
  231. InitWriter(Sender, MIN(size, WriterBufSize));
  232. SELF.out := out;
  233. SELF.size:=size; remain:=size;
  234. END Init;
  235. PROCEDURE Sender(CONST outBuf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: WORD);
  236. VAR num:LONGINT;
  237. BEGIN
  238. num:=MIN(remain,len);
  239. out.Bytes(outBuf, ofs, num);
  240. DEC(remain, num);
  241. IF propagate THEN out.Update END;
  242. IF num<len THEN res:=Streams.EOF ELSE res:=out.res END;
  243. END Sender;
  244. PROCEDURE Reset*;
  245. BEGIN
  246. remain:=size;
  247. END Reset;
  248. END LimitedWriter;
  249. LimitedReader* = OBJECT (Streams.Reader);
  250. VAR in : Streams.Reader;
  251. total, remain-: LONGINT;
  252. PROCEDURE &Init*(in:Streams.Reader; size: LONGINT);
  253. BEGIN
  254. InitReader(Receiver, MIN(size, ReaderBufSize));
  255. SELF.in := in;
  256. total:=size; remain:=size;
  257. END Init;
  258. PROCEDURE Receiver(VAR buf: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len, res: LONGINT);
  259. VAR num:LONGINT;
  260. BEGIN
  261. ASSERT(size >= 0);
  262. IF (remain=0) THEN len:=0; res:=Streams.EOF; RETURN END;
  263. in.Bytes(buf, ofs, MIN(remain,size), len);
  264. DEC(remain,len); INC(received,len);
  265. res:=in.res;
  266. END Receiver;
  267. PROCEDURE Reset*;
  268. BEGIN
  269. remain:=total;
  270. END Reset;
  271. END LimitedReader;
  272. (* convert stream to Base64 encoding on-the-fly *)
  273. TYPE Base64Writer* = OBJECT (Streams.Writer);
  274. VAR out : Streams.Writer; buf: POINTER TO ARRAY OF CHAR;
  275. group, i, ll: LONGINT; done:BOOLEAN;
  276. PROCEDURE &Init*(out:Streams.Writer);
  277. BEGIN
  278. IF ~tablesReady THEN InitTables END;
  279. InitWriter(Sender, WriterBufSize);
  280. NEW(buf, WriterBufSize);
  281. SELF.out := out;
  282. Reset;
  283. END Init;
  284. PROCEDURE Reset*;
  285. BEGIN
  286. Reset^;
  287. group := 0; i := 0; ll := 0; done:=FALSE;
  288. END Reset;
  289. PROCEDURE Sender(CONST outBuf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: WORD);
  290. VAR ix:LONGINT;
  291. BEGIN
  292. IF done THEN res:=Streams.EOF; RETURN END;
  293. ix := ofs; (* encoding snipped from CryptoBase64 *)
  294. WHILE ix < ofs+len DO
  295. group := group*100H + ORD( outBuf[ix] ); INC( ix ); INC( i );
  296. IF i = 3 THEN
  297. out.Char( etab[group DIV 40000H MOD 64] );
  298. out.Char( etab[group DIV 1000H MOD 64] );
  299. out.Char( etab[group DIV 40H MOD 64] );
  300. out.Char( etab[group MOD 64] );
  301. INC(sent,4);
  302. INC( ll, 4 );
  303. IF ll >= 72 THEN out.Ln; ll := 0 END;
  304. group := 0;
  305. i := 0
  306. END;
  307. END;
  308. IF propagate THEN out.Update END;
  309. res:=out.res;
  310. END Sender;
  311. PROCEDURE Close*; (*required termination of a Base64 sequence*)
  312. BEGIN
  313. Update;
  314. IF i > 0 THEN (* encode rest *)
  315. IF i = 1 THEN group := group*100H END;
  316. out.Char( etab[group DIV 400H MOD 64] );
  317. out.Char( etab[group DIV 10H MOD 64] );
  318. IF i = 1 THEN out.Char( '=' ) ELSE out.Char( etab[group*4 MOD 64] ) END;
  319. out.Char( '=' );
  320. END;
  321. out.Update;
  322. res:=out.res;
  323. END Close;
  324. PROCEDURE Pos*(): Streams.Position;
  325. BEGIN RETURN out.Pos()
  326. END Pos;
  327. END Base64Writer;
  328. (* decode Base64 stream to cleartext on-the-fly *)
  329. Base64Reader* = OBJECT(Streams.Reader)
  330. VAR in: Streams.Reader;
  331. i, rest, code, group: LONGINT;
  332. done:BOOLEAN;
  333. PROCEDURE &Init*(in: Streams.Reader);
  334. BEGIN
  335. InitReader(Receiver, ReaderBufSize);
  336. SELF.in := in;
  337. in.SkipSpaces;
  338. IF ~tablesReady THEN InitTables END;
  339. Reset;
  340. END Init;
  341. PROCEDURE Reset*;
  342. BEGIN
  343. Reset^;
  344. group := 0; i := 0; code:=0; done:=FALSE;
  345. END Reset;
  346. PROCEDURE Receiver(VAR inBuf: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len, res: LONGINT);
  347. VAR c:CHAR; size4: LONGINT;
  348. BEGIN
  349. size4:=size DIV 4 * 4; (* a multiple of 4*)
  350. ASSERT((size > 0) & (min <= size4) & (min >= 0));
  351. len:=0;
  352. IF done THEN res:=Streams.EOF; RETURN
  353. ELSE res:=Streams.Ok
  354. END;
  355. (* decoding snipped from CryptoBase64 *)
  356. REPEAT in.Char( c ) UNTIL (c > ' ') OR (c = 0X) OR (in.res#Streams.Ok);
  357. IF (in.res=Streams.Ok) THEN
  358. code := dtab[ORD( c )];
  359. WHILE (code >= 0) & (in.res=Streams.Ok) & (len< size4) DO
  360. group := group*64 + code; INC( i );
  361. IF i = 4 THEN
  362. inBuf[ofs+len] := CHR( group DIV 10000H MOD 100H ); INC( len );
  363. inBuf[ofs+len] := CHR( group DIV 100H MOD 100H ); INC( len );
  364. inBuf[ofs+len] := CHR( group MOD 100H ); INC( len );
  365. group := 0; i := 0
  366. END;
  367. REPEAT in.Char( c ) UNTIL (c > ' ') OR (c = 0X) OR (in.res#Streams.Ok);
  368. code := dtab[ORD( c )];
  369. END;
  370. IF c = '=' THEN (* decode rest *)
  371. IF i < 2 THEN res:=Streams.FormatError;
  372. ELSE
  373. group := group*64; rest := 2; in.Char( c );
  374. IF in.res=Streams.Ok THEN
  375. IF c = '=' THEN group := group*64; rest := 1 END;
  376. inBuf[ofs+len] := CHR( group DIV 10000H ); INC( len );
  377. IF rest = 2 THEN inBuf[ofs+len] := CHR( group DIV 100H MOD 100H ); INC( len ) END;
  378. done:=TRUE;
  379. ELSE res:=in.res
  380. END;
  381. END;
  382. END;
  383. ELSE
  384. res:=in.res;
  385. END;
  386. INC(received,len);
  387. END Receiver;
  388. PROCEDURE Pos*(): Streams.Position;
  389. BEGIN RETURN in.Pos()
  390. END Pos;
  391. END Base64Reader;
  392. TYPE TYPE DumpWriter*= OBJECT(Streams.Writer);
  393. PROCEDURE Send* ( CONST buf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: WORD );
  394. BEGIN
  395. res:=Streams.Ok;
  396. END Send;
  397. END DumpWriter;
  398. VAR (*tables for Base64*)
  399. etab: ARRAY 64 OF CHAR;
  400. dtab: ARRAY 128 OF INTEGER;
  401. tablesReady:BOOLEAN;
  402. PROCEDURE InitTables;
  403. VAR i, max: INTEGER;
  404. BEGIN
  405. max := ORD("Z") - ORD("A");
  406. FOR i := 0 TO max DO
  407. etab[i] := CHR( i + ORD("A") )
  408. END;
  409. INC(max);
  410. FOR i := max TO max + ORD("z") - ORD("a") DO
  411. etab[i] := CHR( i - max + ORD("a") )
  412. END;
  413. max := max + ORD("z") - ORD("a") + 1;
  414. FOR i := max TO max + ORD("9") - ORD("0") DO
  415. etab[i] := CHR( i - max + ORD("0") )
  416. END;
  417. etab[62] := "+";
  418. etab[63] := "/";
  419. FOR i := 0 TO 127 DO dtab[i] := -1 END;
  420. FOR i := 0 TO 63 DO dtab[ORD( etab[i] )] := i END;
  421. tablesReady:=TRUE;
  422. END InitTables;
  423. (*open a monitoring writer on the out stream*)
  424. PROCEDURE OpenWriterMonitor*(VAR w: Streams.Writer; out:Streams.Writer; monitor: Streams.Writer);
  425. VAR wm: WriterMonitor;
  426. BEGIN
  427. NEW(wm, out, monitor); w:=wm;
  428. END OpenWriterMonitor;
  429. PROCEDURE OpenAsynchronousForwarder*(out: Streams.Writer): Streams.Writer;
  430. VAR a: AsynchronousForwarder;
  431. BEGIN
  432. NEW(a, out); RETURN a
  433. END OpenAsynchronousForwarder;
  434. (*open a monitoring reader on the in stream*)
  435. PROCEDURE OpenReaderMonitor*(VAR r: Streams.Reader; in:Streams.Reader; monitor: Streams.Writer);
  436. VAR rm: ReaderMonitor;
  437. BEGIN
  438. NEW(rm, in, monitor); r:=rm;
  439. END OpenReaderMonitor;
  440. (*open a size limited writer r on the out stream*)
  441. PROCEDURE OpenLimitedWriter*(VAR w: Streams.Writer; out: Streams.Writer; size:LONGINT);
  442. VAR lw: LimitedWriter;
  443. BEGIN
  444. NEW(lw, out, size); w:=lw;
  445. END OpenLimitedWriter;
  446. (*open a size limited reader r on the in stream*)
  447. PROCEDURE OpenLimitedReader*(VAR r: Streams.Reader; in: Streams.Reader; size:LONGINT);
  448. VAR lr: LimitedReader;
  449. BEGIN
  450. NEW(lr, in, size); r:=lr;
  451. END OpenLimitedReader;
  452. (*
  453. (* application example: reader/writer monitors *)
  454. PROCEDURE Test*(context:Commands.Context);
  455. VAR w, log: Streams.Writer;
  456. r:Streams.Reader;
  457. s: ARRAY 64 OF CHAR;
  458. res:BOOLEAN;
  459. BEGIN
  460. NEW(log, KernelLog.Send, WriterBufSize);
  461. OpenReaderMonitor(r, context.arg, log); (*monitor the context.arg reader and send monitored input to log *)
  462. res:=r.GetString(s);
  463. OpenWriterMonitor(w, context.out, log);(* monitor the context.out writer and send monitored data to log*)
  464. w.String("holla"); w.Ln;
  465. w.Update;
  466. END Test;
  467. (* application example: size limited streams *)
  468. PROCEDURE Test2*(context:Commands.Context);
  469. VAR w, log: Streams.Writer;
  470. r:Streams.Reader;
  471. s: ARRAY 64 OF CHAR;
  472. res:BOOLEAN;
  473. BEGIN
  474. NEW(log, KernelLog.Send, WriterBufSize);
  475. OpenLimitedReader(r, context.arg, 7); (*monitor the context.arg reader and send monitored input to log *)
  476. res:=r.GetString(s);
  477. log.String(s); log.Ln;
  478. res:=r.GetString(s);
  479. log.String(s); log.Ln;
  480. log.Update;
  481. OpenLimitedWriter(w, log, 6);(* monitor the context.out writer and send monitored data to log*)
  482. w.String("123456789"); w.Ln; w.Update;
  483. END Test2;
  484. *)
  485. (*
  486. PROCEDURE TestAsync*;
  487. VAR log: Streams.Writer; i: LONGINT;
  488. BEGIN
  489. NEW(log, KernelLog.Send,128);
  490. log := OpenAsynchronousForwarder(log);
  491. FOR i := 0 TO 200 DO
  492. log.String(" Hallo from asynch "); log.Ln; log.Update;
  493. END;
  494. KernelLog.String(" D O N E "); KernelLog.Ln;
  495. END TestAsync;
  496. PROCEDURE TestAsync2*;
  497. VAR log: AsynchronousWriter; i: LONGINT;
  498. BEGIN
  499. NEW(log, KernelLog.Send,128);
  500. FOR i := 0 TO 200 DO
  501. log.String(" Hallo from asynch2 "); log.Ln; log.Update;
  502. END;
  503. KernelLog.String(" D O N E "); KernelLog.Ln;
  504. END TestAsync2;
  505. PROCEDURE TestEncode(VAR buf: ARRAY OF CHAR; pos,len:LONGINT);
  506. VAR i:LONGINT;
  507. BEGIN
  508. FOR i:=pos TO pos+len-1 DO
  509. IF (buf[i]>="!") & (buf[i]<"~") THEN buf[i]:=CHR(ORD(buf[i])+1) END;
  510. END;
  511. END TestEncode;
  512. PROCEDURE TestDecode(VAR buf: ARRAY OF CHAR; pos,len:LONGINT);
  513. VAR i:LONGINT;
  514. BEGIN
  515. FOR i:=pos TO pos+len-1 DO
  516. IF (buf[i]>"!") & (buf[i]<="~") THEN buf[i]:=CHR(ORD(buf[i])-1) END;
  517. END;
  518. END TestDecode;
  519. PROCEDURE TestCrypt*(context:Commands.Context);
  520. VAR log: Streams.Writer; secretwriter: EncryptingWriter; secretreader:DecryptingReader;
  521. i: LONGINT;
  522. string:ARRAY 64 OF CHAR; res:BOOLEAN;
  523. BEGIN
  524. NEW(log, KernelLog.Send,128);
  525. NEW(secretreader, context.arg, TestDecode);
  526. res:=secretreader.GetString(string);
  527. log.String("decoded secret: "); log.String(string); log.Ln; log.String("encoded secret: "); log.Update;
  528. NEW(secretwriter, log, TestEncode);
  529. secretwriter.String(string); secretwriter.Update;
  530. END TestCrypt;
  531. *)
  532. (*
  533. PROCEDURE TestBase64*(context:Commands.Context);
  534. VAR secretwriter: Base64Writer;
  535. secretreader:Base64Reader;
  536. string:ARRAY 64 OF CHAR;
  537. BEGIN
  538. NEW(secretwriter, context.out);
  539. secretwriter.String("admin:1234"); (* expect "YWRtaW46MTIzNA==" *)
  540. secretwriter.Close;
  541. context.out.Ln; context.out.Update;
  542. NEW(secretreader, context.arg);
  543. secretreader.String(string);
  544. context.out.String(string); (* given the argument "YWRtaW46MTIzNA==", expect "admin:1234" *)
  545. context.out.Ln; context.out.Update;
  546. END TestBase64;
  547. *)
  548. END StreamUtilities.
  549. System.FreeDownTo StreamUtilities ~
  550. StreamUtilities.Test hello ~
  551. StreamUtilities.Test2 abcd efghijk ~
  552. StreamUtilities.TestAsync abcd efghijk ~
  553. StreamUtilities.TestCrypt IfmmpXpsme ~
  554. StreamUtilities.TestBase64 YWRtaW46MTIzNA== ~
  555. StreamUtilities.TestAsync
  556. StreamUtilities.TestAsync2