home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / D / CLISP / CLISPSRC.TAR / clisp-1995-01-01 / src / stream.d < prev    next >
Encoding:
Text File  |  1994-12-30  |  523.9 KB  |  13,434 lines

  1. # Streams fⁿr CLISP
  2. # Bruno Haible 30.12.1994
  3. # Generic Streams: Marcus Daniels 8.4.1994
  4.  
  5. #include "lispbibl.c"
  6. #include "arilev0.c" # fⁿr R_sign
  7.  
  8. #ifdef GNU_READLINE
  9.   #define READLINE_LIBRARY # Hinweis, wo die Include-Files gesucht werden mⁿssen
  10.   #include "readline.h"
  11.   #include "history.h"
  12.   #undef READLINE_LIBRARY
  13.   #ifdef STDC_HEADERS
  14.     #include <string.h>  # deklariert strcpy()
  15.   #endif
  16. #endif
  17.  
  18.  
  19. #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS)
  20.   # Betriebssystem-Funktion read sichtbar machen:
  21.     #undef read
  22. #endif
  23.  
  24.  
  25. # Nochmals zum Aufbau von Streams:
  26. # strmflags = Flags
  27.   # Bits in den Flags:
  28.   #define strmflags_ia_bit_B     0  # gesetzt, falls Integer-Stream der Art a
  29.   #define strmflags_ib_bit_B     1  # gesetzt, falls Integer-Stream der Art b
  30.   #define strmflags_ic_bit_B     2  # gesetzt, falls Integer-Stream der Art c
  31.   #define strmflags_immut_bit_B  3  # gesetzt, falls gelesene Objekte immutabel sind
  32.   #define strmflags_rd_by_bit_B  4  # gesetzt, falls READ-BYTE m÷glich ist
  33.   #define strmflags_wr_by_bit_B  5  # gesetzt, falls WRITE-BYTE m÷glich ist
  34.   # define strmflags_rd_ch_bit_B 6  # gesetzt, falls READ-CHAR m÷glich ist
  35.   #define strmflags_wr_ch_bit_B  7  # gesetzt, falls WRITE-CHAR m÷glich ist
  36.   # Bitmasken in den Flags:
  37.   #define strmflags_ia_B     bit(strmflags_ia_bit_B)
  38.   #define strmflags_ib_B     bit(strmflags_ib_bit_B)
  39.   #define strmflags_ic_B     bit(strmflags_ic_bit_B)
  40.   # define strmflags_immut_B bit(strmflags_immut_bit_B)
  41.   #define strmflags_rd_by_B  bit(strmflags_rd_by_bit_B)
  42.   #define strmflags_wr_by_B  bit(strmflags_wr_by_bit_B)
  43.   #define strmflags_rd_ch_B  bit(strmflags_rd_ch_bit_B)
  44.   #define strmflags_wr_ch_B  bit(strmflags_wr_ch_bit_B)
  45.   #define strmflags_i_B   (strmflags_ia_B | strmflags_ib_B | strmflags_ic_B)
  46.   #define strmflags_rd_B  (strmflags_rd_by_B | strmflags_rd_ch_B)
  47.   #define strmflags_wr_B  (strmflags_wr_by_B | strmflags_wr_ch_B)
  48.   #define strmflags_by_B  (strmflags_rd_by_B | strmflags_wr_by_B)
  49.   #define strmflags_ch_B  (strmflags_rd_ch_B | strmflags_wr_ch_B)
  50.   # strmflags_open_B: die 4 oberen Bits
  51. # strmtype = NΣhere Typinfo. Siehe LISPBIBL.D.
  52.  
  53. # einzelne Komponenten:
  54.   # strm_rd_by       Pseudofunktion fⁿr READ-BYTE
  55.   # strm_wr_by       Pseudofunktion fⁿr WRITE-BYTE
  56.   # strm_rd_ch       Pseudofunktion fⁿr READ-CHAR
  57.   # strm_rd_ch_last  letztes von READ-CHAR gelesenes Zeichen
  58.   #                  (bzw. als Fixnum nach UNREAD-CHAR bzw. NIL sonst)
  59.   # strm_wr_ch       Pseudofunktion fⁿr WRITE-CHAR
  60.   # strm_wr_ch_lpos  Line-Position in der Zeile nach dem letzten WRITE-CHAR
  61.   #ifdef STRM_WR_SS
  62.   # strm_wr_ss       Pseudofunktion fⁿr WRITE-SIMPLE-STRING
  63.   #endif
  64. # weitere (typspezifische) Komponenten:
  65.   # siehe in LISPBIBL.D und bei den einzelnen Stream-Typen.
  66.  
  67.  
  68. # ==============================================================================
  69. #                           S T R E A M S
  70.  
  71. # Da MAKE-TWO-WAY-STREAM eventuell einen Stream liefern kann, der z.B.
  72. # Character-Input und Byte-Output ist, und damit insbesondere alle
  73. # READ-/WRITE-Operationen effizient laufen, werden Streams folgenderma▀en
  74. # aufgebaut:
  75. #    - Typ des Streams,
  76. #    - Komponenten fⁿr READ-BYTE,
  77. #    - Komponenten fⁿr WRITE-BYTE,
  78. #    - Komponenten fⁿr READ-CHAR,
  79. #    - Komponenten fⁿr WRITE-CHAR,
  80. #    - vom Typ des Streams abhΣngige Komponenten.
  81.  
  82. # Feste Komponenten
  83. # -----------------
  84. # fⁿr READ-BYTE:
  85. #     RD_BY          Pseudofunktion zum Lesen eines Bytes
  86. # fⁿr WRITE-BYTE:
  87. #     WR_BY          Pseudofunktion zum Schreiben eines Bytes
  88. # fⁿr READ-CHAR:
  89. #     RD_CH          Pseudofunktion zum Lesen eines Characters
  90. #     RD_CH_LAST     letztes gelesenes Zeichen und Flag
  91. #                    (NIL zu Beginn, eof_value nach End-of-File,
  92. #                    sonst: letztes gelesenes Zeichen, als Fixnum,
  93. #                    falls es mit UNREAD zurⁿckgeschoben wurde)
  94. # fⁿr WRITE-CHAR:
  95. #     WR_CH          Pseudofunktion zum Schreiben eines Characters
  96. #     WR_CH_LPOS     Position in der Zeile (Fixnum >=0) (fⁿr FORMAT ~T)
  97.  
  98. # Pseudofunktionen sind Adressen von C-Funktionen, die direkt angesprungen
  99. # werden k÷nnen, mit dem Stream als erstem Argument, und ein Objekt
  100. # als Ergebnis liefern.
  101.   #define P(fun)  (type_constpointer_object(machine_type,(Pseudofun)&(fun)))
  102.  
  103. # Spezifikation der vier Typen von Pseudofunktionen:
  104.   #
  105.   # Spezifikation fⁿr READ-BYTE - Pseudofunktion:
  106.   # fun(stream)
  107.   # > stream: Stream
  108.   # < ergebnis: gelesener Integer (eof_value bei EOF)
  109.   # kann GC ausl÷sen
  110.     typedef object (* rd_by_Pseudofun) (object stream);
  111.   #
  112.   # Spezifikation fⁿr WRITE-BYTE - Pseudofunktion:
  113.   # fun(stream,obj)
  114.   # > stream: Stream
  115.   # > obj: auszugebender Integer
  116.   # kann GC ausl÷sen
  117.     typedef void (* wr_by_Pseudofun) (object stream, object obj);
  118.   #
  119.   # Spezifikation fⁿr READ-CHAR - Pseudofunktion:
  120.   # fun(&stream)
  121.   # > stream: Stream
  122.   # < stream: Stream
  123.   # < ergebnis: gelesenes Character (eof_value bei EOF)
  124.   # kann GC ausl÷sen
  125.     typedef object (* rd_ch_Pseudofun) (object* stream_);
  126.   #
  127.   # Spezifikation fⁿr WRITE-CHAR - Pseudofunktion:
  128.   # fun(&stream,obj)
  129.   # > stream: Stream
  130.   # < stream: Stream
  131.   # > obj: auszugebendes Character
  132.   # kann GC ausl÷sen
  133.     typedef void (* wr_ch_Pseudofun) (object* stream_, object obj);
  134.   #
  135.   #ifdef STRM_WR_SS
  136.   # Spezifikation fⁿr WRITE-SIMPLE-STRING - Pseudofunktion:
  137.   # fun(&stream,string,start,len)
  138.   # > string: Simple-String
  139.   # > start: Startindex
  140.   # > len: Anzahl der auszugebenden Zeichen
  141.   # > stream: Stream
  142.   # < stream: Stream
  143.   # kann GC ausl÷sen
  144.     typedef void (* wr_ss_Pseudofun) (object* stream_, object string, uintL start, uintL len);
  145.   #endif
  146.  
  147. # Pseudofunktionen aus einem Stream herausgreifen:
  148.   #define rd_by(strm)  (*(rd_by_Pseudofun)(ThePseudofun(TheStream(strm)->strm_rd_by)))
  149.   #define wr_by(strm)  (*(wr_by_Pseudofun)(ThePseudofun(TheStream(strm)->strm_wr_by)))
  150.   #define rd_ch(strm)  (*(rd_ch_Pseudofun)(ThePseudofun(TheStream(strm)->strm_rd_ch)))
  151.   #define wr_ch(strm)  (*(wr_ch_Pseudofun)(ThePseudofun(TheStream(strm)->strm_wr_ch)))
  152.   #ifdef STRM_WR_SS
  153.   #define wr_ss(strm)  (*(wr_ss_Pseudofun)(ThePseudofun(TheStream(strm)->strm_wr_ss)))
  154.   #endif
  155.  
  156. #  M÷gliche Typen von Streams                 Zusatzkomponenten
  157. #  --------------------------                 -----------------
  158. #  0. Keyboard-Stream
  159. #  1. Interaktiver Terminalstream             Eingabebuffer, ZeichenzΣhler
  160. #  2. File-Stream fⁿr String-Chars            Handle, Pathname, File-Position,
  161. #     (Input, Output, I/O, Closed=Probe)      Buffer
  162. #  3. File-Stream fⁿr Characters              Handle, Pathname, File-Position,
  163. #     (Input, Output, I/O, Closed=Probe)      Buffer
  164. #  4. File-Stream fⁿr Unsigned-Bytes          Handle, Pathname, File-Position,
  165. #     (Input, Output, I/O, Closed=Probe)      Buffer, Bit-Buffer
  166. #  5. File-Stream fⁿr Signed-Bytes            Handle, Pathname, File-Position,
  167. #     (Input, Output, I/O, Closed=Probe)      Buffer, Bit-Buffer
  168. #  6. Synonym-Stream                          Symbol
  169. #  7. Broadcast-(Output-)Stream               Liste von Streams
  170. #  8. Concatenated-(Input-)Stream             Liste von Streams
  171. #  9. Two-Way-Stream                          Stream fⁿr Input, Stream fⁿr Output
  172. # 10. Echo-Stream                             Stream fⁿr Input, Stream fⁿr Output
  173. # 11. String-Input-Stream                     Gesamtstring, ZeichenzΣhler
  174. # 12. String-Output-Stream                    Buffer (Semi-Simple-String)
  175. # 13. String-Push-Stream                      String mit Fill-Pointer
  176. # 14. Pretty-Printer-Hilfs-Stream             Liste von Buffers, Modus
  177. # 15. Buffered-Input-Stream                   fun, mode, String, ZeichenzΣhler
  178. # 16. Buffered-Output-Stream                  fun, Buffer (Semi-Simple-String)
  179. # 17. Window-Stream                           ---
  180. #ifdef PRINTER
  181. # 18. Printer-Stream                          Atari: 2 Gemdos-Funktionsnummern
  182. #endif
  183. #ifdef HANDLES
  184. # 19. File-Handle-Stream                      Handle, Pathname
  185. #endif
  186. #ifdef PIPES
  187. # 20. Pipe-Input-Stream                       Pid, Handle
  188. # 21. Pipe-Output-Stream                      Pid, Handle
  189. #endif
  190. #ifdef SOCKETS
  191. # 22. Socket-Stream                           Info, Handle
  192. #endif
  193. #ifdef GENERIC_STREAMS
  194. # 23. Generic-Stream                          Private Controller Object
  195. #endif
  196.  
  197. # ZusΣtzlich wird (sicherheitshalber) eine Liste aller offenen File-Streams
  198. # gefⁿhrt.
  199.  
  200. # Fehlermeldung, wenn eine Stream-Operation auf einem Stream nicht erlaubt ist.
  201. # fehler_illegal_streamop(caller,stream);
  202. # > caller: Aufrufer (ein Symbol)
  203. # > stream: Stream
  204.   nonreturning_function(global, fehler_illegal_streamop, (object caller, object stream));
  205.   global void fehler_illegal_streamop(caller,stream)
  206.     var reg1 object caller;
  207.     var reg2 object stream;
  208.     { pushSTACK(stream); # Wert fⁿr Slot STREAM von STREAM-ERROR
  209.       pushSTACK(stream);
  210.       pushSTACK(caller);
  211.       fehler(stream_error,
  212.              DEUTSCH ? "~ auf ~ ist unzulΣssig." :
  213.              ENGLISH ? "~ on ~ is illegal" :
  214.              FRANCAIS ? "~ de/sur ~ est impossible." :
  215.              ""
  216.             );
  217.     }
  218.  
  219. # Dummy-Pseudo-Funktionen, die Errors liefern:
  220.   local object rd_by_dummy (object stream);
  221.   local object rd_by_dummy(stream)
  222.     var reg1 object stream;
  223.     { fehler_illegal_streamop(S(read_byte),stream); }
  224.   local void wr_by_dummy (object stream, object obj);
  225.   local void wr_by_dummy(stream,obj)
  226.     var reg1 object stream;
  227.     var reg2 object obj;
  228.     { fehler_illegal_streamop(S(write_byte),stream); }
  229.   local object rd_ch_dummy (object* stream_);
  230.   local object rd_ch_dummy(stream_)
  231.     var reg1 object* stream_;
  232.     { fehler_illegal_streamop(S(read_char),*stream_); }
  233.   local void wr_ch_dummy (object* stream_, object obj);
  234.   local void wr_ch_dummy(stream_,obj)
  235.     var reg1 object* stream_;
  236.     var reg2 object obj;
  237.     { fehler_illegal_streamop(S(write_char),*stream_); }
  238.   #ifdef STRM_WR_SS
  239.   local void wr_ss_dummy (object* stream_, object string, uintL start, uintL len);
  240.   local void wr_ss_dummy(stream_,string,start,len)
  241.     var reg3 object* stream_;
  242.     var reg5 object string;
  243.     var reg4 uintL start;
  244.     var reg2 uintL len;
  245.     { if (len==0) return;
  246.      {var reg1 uintL index = start;
  247.       pushSTACK(string); # Simple-String retten
  248.       dotimespL(len,len,
  249.         { write_schar(stream_,TheSstring(STACK_0)->data[index]);
  250.           index++;
  251.         });
  252.       skipSTACK(1);
  253.     }}
  254.   # Dasselbe, wenn write_char auf diesem Stream keine GC ausl÷sen kann:
  255.   local void wr_ss_dummy_nogc (object* stream_, object string, uintL start, uintL len);
  256.   local void wr_ss_dummy_nogc(stream_,string,start,len)
  257.     var reg3 object* stream_;
  258.     var reg5 object string;
  259.     var reg4 uintL start;
  260.     var reg2 uintL len;
  261.     { if (len==0) return;
  262.      {var reg1 uintB* ptr = &TheSstring(string)->data[start];
  263.       dotimespL(len,len, { write_schar(stream_,*ptr++); } );
  264.     }}
  265.   #endif
  266.   # Am Ende eines wr_ss die Line-Position aktualisieren:
  267.   # wr_ss_lpos(stream,ptr,len);
  268.   # > stream: Stream, nicht der Terminal-Stream
  269.   # > ptr: Pointer ans Ende(!) der bereits auf den Stream ausgegebenen Zeichen
  270.   # > len: Anzahl der Zeichen, >0
  271.   # < ergebnis: TRUE, falls ein NL unter den Zeichen ist, FALSE sonst
  272.   local boolean wr_ss_lpos (object stream, uintB* ptr, uintL len);
  273.   local boolean wr_ss_lpos(stream,ptr,len)
  274.     var reg4 object stream;
  275.     var reg1 uintB* ptr;
  276.     var reg5 uintL len;
  277.     { var reg3 uintL pos = 0; # zΣhle die Zahl der Zeichen seit dem letzten NL
  278.       var reg2 uintL count;
  279.       dotimespL(count,len, { if (*--ptr == NL) goto found_NL; pos++; } );
  280.       if (FALSE)
  281.         found_NL: # pos Zeichen seit dem letzten NL
  282.         { TheStream(stream)->strm_wr_ch_lpos = fixnum(pos);
  283.           return TRUE;
  284.         }
  285.         else
  286.         # kein NL
  287.         { TheStream(stream)->strm_wr_ch_lpos = fixnum_inc(TheStream(stream)->strm_wr_ch_lpos,len);
  288.           return FALSE;
  289.     }   }
  290.  
  291. # Liest ein Byte von einem Stream.
  292. # read_byte(stream)
  293. # > stream: Stream
  294. # < ergebnis: gelesener Integer (eof_value bei EOF)
  295. # kann GC ausl÷sen
  296.   global object read_byte (object stream);
  297.   global object read_byte(stream)
  298.     var reg1 object stream;
  299.     { return rd_by(stream)(stream); }
  300.  
  301. # Schreibt ein Byte auf einen Stream.
  302. # write_byte(stream,byte);
  303. # > stream: Stream
  304. # > byte: auszugebender Integer
  305. # kann GC ausl÷sen
  306.   global void write_byte(object stream, object byte);
  307.   global void write_byte(stream,byte)
  308.     var reg1 object stream;
  309.     var reg2 object byte;
  310.     { wr_by(stream)(stream,byte); }
  311.  
  312. # Liest ein Character von einem Stream.
  313. # read_char(&stream)
  314. # > stream: Stream
  315. # < stream: Stream
  316. # < ergebnis: gelesenes Character (eof_value bei EOF)
  317. # kann GC ausl÷sen
  318.   global object read_char (object* stream_);
  319.   global object read_char(stream_)
  320.     var reg1 object* stream_;
  321.     { var reg2 object stream = *stream_;
  322.       if (!mposfixnump(TheStream(stream)->strm_rd_ch_last)) # Char nach UNREAD ?
  323.         # nein -> neues Zeichen holen:
  324.         { var reg3 object newch = rd_ch(stream)(stream_);
  325.           TheStream(*stream_)->strm_rd_ch_last = newch; # und abspeichern
  326.           return newch;
  327.         }
  328.         else
  329.         # ja -> Flagbit l÷schen und letztes Zeichen holen:
  330.         { return TheStream(stream)->strm_rd_ch_last =
  331.                    fixnum_to_char(TheStream(stream)->strm_rd_ch_last);
  332.     }   }
  333.  
  334. # Schiebt das letzte gelesene Character auf einen Stream zurⁿck.
  335. # unread_char(&stream,ch);
  336. # > ch: letztes gelesenes Character
  337. # > stream: Stream
  338. # < stream: Stream
  339.   global void unread_char (object* stream_, object ch);
  340.   global void unread_char(stream_,ch)
  341.     var reg1 object* stream_;
  342.     var reg3 object ch;
  343.     { var reg2 object stream = *stream_;
  344.       if (eq(TheStream(stream)->strm_rd_ch_last,ch))
  345.         { TheStream(stream)->strm_rd_ch_last =
  346.             char_to_fixnum(TheStream(stream)->strm_rd_ch_last); # Flagbit setzen
  347.         }
  348.         else
  349.         { if (mcharp(TheStream(stream)->strm_rd_ch_last))
  350.             { pushSTACK(stream); # Wert fⁿr Slot STREAM von STREAM-ERROR
  351.               pushSTACK(ch);
  352.               pushSTACK(stream);
  353.               pushSTACK(S(unread_char));
  354.               fehler(stream_error,
  355.                      DEUTSCH ? "~: Das letzte von ~ gelesene Zeichen war nicht ~." :
  356.                      ENGLISH ? "~: the last character read from ~ was not ~" :
  357.                      FRANCAIS ? "~ : Le dernier caractΦre lu dans ~ n'Θtait pas ~." :
  358.                      ""
  359.                     );
  360.             }
  361.             else
  362.             { pushSTACK(stream); # Wert fⁿr Slot STREAM von STREAM-ERROR
  363.               pushSTACK(S(read_char));
  364.               pushSTACK(stream);
  365.               pushSTACK(S(unread_char));
  366.               fehler(stream_error,
  367.                      DEUTSCH ? "~ von ~ ohne vorheriges ~." :
  368.                      ENGLISH ? "~ from ~ without ~ before it" :
  369.                      FRANCAIS ? "~ de ~ sans prΘcΘdent ~." :
  370.                      ""
  371.                     );
  372.             }
  373.     }   }
  374.  
  375. # Liest ein Character von einem Stream, ohne es zu verbrauchen.
  376. # peek_char(&stream)
  377. # > stream: Stream
  378. # < stream: Stream
  379. # < ergebnis: gelesenes Character (eof_value bei EOF)
  380. # kann GC ausl÷sen
  381.   global object peek_char (object* stream_);
  382.   global object peek_char(stream_)
  383.     var reg1 object* stream_;
  384.     { var reg2 object stream = *stream_;
  385.       if (!mposfixnump(TheStream(stream)->strm_rd_ch_last)) # Char nach UNREAD ?
  386.         # nein -> neues Zeichen holen:
  387.         { var reg3 object newch = rd_ch(stream)(stream_);
  388.           # und abspeichern:
  389.           TheStream(*stream_)->strm_rd_ch_last =
  390.             (eq(newch,eof_value) ? newch : char_to_fixnum(newch));
  391.           return newch;
  392.         }
  393.         else
  394.         # ja -> letztes Zeichen holen:
  395.         { return fixnum_to_char(TheStream(stream)->strm_rd_ch_last); }
  396.     }
  397.  
  398. # Schreibt ein Character auf einen Stream.
  399. # write_char(&stream,ch);
  400. # > ch: auszugebendes Character
  401. # > stream: Stream
  402. # < stream: Stream
  403. # kann GC ausl÷sen
  404.   global void write_char (object* stream_, object ch);
  405.   global void write_char(stream_,ch)
  406.     var reg1 object* stream_;
  407.     var reg4 object ch;
  408.     { var reg3 cint c = char_int(ch);
  409.       # Char schreiben:
  410.       wr_ch(*stream_)(stream_,ch);
  411.       # Line Position aktualisieren:
  412.      {var reg2 object stream = *stream_;
  413.       if (!(TheStream(stream)->strmtype == strmtype_terminal))
  414.         # nicht der Terminal-Stream
  415.         { if (c == NL)
  416.             # Nach Newline: Line Position := 0
  417.             { TheStream(stream)->strm_wr_ch_lpos = Fixnum_0; }
  418.             else
  419.             # Line Position incrementieren:
  420.             { TheStream(stream)->strm_wr_ch_lpos =
  421.                 fixnum_inc(TheStream(stream)->strm_wr_ch_lpos,1);
  422.         }   }
  423.         else
  424.         # es ist der Terminal-Stream
  425.         #ifdef TERMINAL_USES_KEYBOARD
  426.         { ; } # Auf dem Atari macht dies wr_ch_terminal() selbst.
  427.         #else
  428.         # Wie wirken sich die Steuerzeichen in der Position aus?
  429.         { if (graphic_char_p(c))
  430.             # normales druckendes Zeichen -> Line Position incrementieren:
  431.             { TheStream(stream)->strm_wr_ch_lpos =
  432.                 fixnum_inc(TheStream(stream)->strm_wr_ch_lpos,1);
  433.             }
  434.           elif (c == NL)
  435.             # Newline -> Line Position := 0
  436.             { TheStream(stream)->strm_wr_ch_lpos = Fixnum_0; }
  437.           elif (c == BS)
  438.             # Backspace -> Line Position, wenn m÷glich, decrementieren:
  439.             { if (!eq(TheStream(stream)->strm_wr_ch_lpos,Fixnum_0))
  440.                 { TheStream(stream)->strm_wr_ch_lpos =
  441.                     fixnum_inc(TheStream(stream)->strm_wr_ch_lpos,-1);
  442.             }   }
  443.         }
  444.         #endif
  445.     }}
  446.  
  447. # UP: Fⁿllt beim Schlie▀en eines Streams die Dummy-Pseudofunktionen ein.
  448. # close_dummys(stream);
  449. # > stream: Stream
  450.   local void close_dummys (object stream);
  451.   local void close_dummys(stream)
  452.     var reg1 object stream;
  453.     { TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  454.       TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  455.       TheStream(stream)->strm_rd_ch = P(rd_ch_dummy);
  456.       TheStream(stream)->strm_rd_ch_last = NIL; # Lastchar := NIL
  457.       TheStream(stream)->strm_wr_ch = P(wr_ch_dummy);
  458.       #ifdef STRM_WR_SS
  459.       TheStream(stream)->strm_wr_ss = P(wr_ss_dummy);
  460.       #endif
  461.       TheStream(stream)->strmflags &= ~strmflags_open_B; # FΣhigkeiten-Flags l÷schen
  462.     }
  463.  
  464. # Liefert Fehlermeldung, wenn der Wert des Symbols sym kein Stream ist.
  465.   nonreturning_function(local, fehler_value_stream, (object sym));
  466.   # siehe unten
  467.  
  468. # UP: Liefert den Stream, der der Wert einer Variablen ist.
  469. # var_stream(sym)
  470. # > sym: Variable (Symbol)
  471. # < ergebnis: Stream
  472.   global object var_stream (object sym);
  473.   global object var_stream(sym)
  474.     var reg1 object sym;
  475.     { if (!mstreamp(Symbol_value(sym))) { fehler_value_stream(sym); }
  476.       return Symbol_value(sym);
  477.     }
  478.  
  479. #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(AMIGAOS) || defined(RISCOS)
  480. # Fehler, wenn aus einem obskuren Grunde ein WRITE nicht gehen sollte:
  481.   nonreturning_function(local, fehler_unwritable, (object caller, object stream));
  482.   local void fehler_unwritable(caller,stream)
  483.     var reg1 object caller;
  484.     var reg2 object stream;
  485.     { pushSTACK(stream); # Wert fⁿr Slot PATHNAME von FILE-ERROR
  486.       pushSTACK(stream);
  487.       pushSTACK(caller);
  488.       fehler(file_error,
  489.              DEUTSCH ? "~: Kann nichts auf ~ ausgeben." :
  490.              ENGLISH ? "~: cannot output to ~" :
  491.              FRANCAIS ? "~ : Ne peux rien Θcrire sur ~." :
  492.              ""
  493.             );
  494.     }
  495. #endif
  496.  
  497. # Fehler, wenn ein Objekt kein Character ist:
  498. # fehler_wr_char(stream,obj);
  499.   nonreturning_function(local, fehler_wr_char, (object stream, object obj));
  500.   local void fehler_wr_char(stream,obj)
  501.     var reg1 object stream;
  502.     var reg2 object obj;
  503.     { pushSTACK(obj); # Wert fⁿr Slot DATUM von TYPE-ERROR
  504.       pushSTACK(S(character)); # Wert fⁿr Slot EXPECTED-TYPE von TYPE-ERROR
  505.       pushSTACK(stream);
  506.       pushSTACK(obj);
  507.       fehler(type_error,
  508.              DEUTSCH ? "~ ist kein Character und kann daher nicht auf ~ ausgegeben werden." :
  509.              ENGLISH ? "~ is not a character, cannot be output onto ~" :
  510.              FRANCAIS ? "~, n'Θtant pas de type CHARACTER, ne peut pas Ωtre Θcrit dans ~." :
  511.              ""
  512.             );
  513.     }
  514.  
  515. # Fehler, wenn ein Character kein String-Char ist:
  516. # fehler_wr_string_char(stream,ch);
  517.   nonreturning_function(local, fehler_wr_string_char, (object stream, object ch));
  518.   local void fehler_wr_string_char(stream,ch)
  519.     var reg1 object stream;
  520.     var reg2 object ch;
  521.     { pushSTACK(ch); # Wert fⁿr Slot DATUM von TYPE-ERROR
  522.       pushSTACK(S(string_char)); # Wert fⁿr Slot EXPECTED-TYPE von TYPE-ERROR
  523.       pushSTACK(stream);
  524.       pushSTACK(ch);
  525.       fehler(type_error,
  526.              DEUTSCH ? "Character ~ ist kein String-Char und kann daher nicht auf ~ ausgegeben werden." :
  527.              ENGLISH ? "character ~ is not a string-char, cannot be output onto ~" :
  528.              FRANCAIS ? "Le caractΦre ~, n'Θtant pas de type STRING-CHAR, ne peut pas Ωtre Θcrit dans ~." :
  529.              ""
  530.             );
  531.     }
  532.  
  533. # Fehler, wenn ein Objekt kein Integer ist:
  534. # fehler_wr_integer(stream,obj);
  535.   nonreturning_function(local, fehler_wr_integer, (object stream, object obj));
  536.   local void fehler_wr_integer(stream,obj)
  537.     var reg1 object stream;
  538.     var reg2 object obj;
  539.     { pushSTACK(obj); # Wert fⁿr Slot DATUM von TYPE-ERROR
  540.       pushSTACK(S(integer)); # Wert fⁿr Slot EXPECTED-TYPE von TYPE-ERROR
  541.       pushSTACK(stream);
  542.       pushSTACK(obj);
  543.       fehler(type_error,
  544.              DEUTSCH ? "~ ist kein Integer und kann daher nicht auf ~ ausgegeben werden." :
  545.              ENGLISH ? "~ is not an integer, cannot be output onto ~" :
  546.              FRANCAIS ? "~, n'Θtant pas un entier, ne peut pas Ωtre Θcrit dans ~." :
  547.              ""
  548.             );
  549.     }
  550.  
  551. # Fehler, wenn ein Integer nicht im passenden Bereich ist:
  552. # fehler_bad_integer(stream,obj);
  553.   nonreturning_function(local, fehler_bad_integer, (object stream, object obj));
  554.   local void fehler_bad_integer(stream,obj)
  555.     var reg1 object stream;
  556.     var reg2 object obj;
  557.     { pushSTACK(stream);
  558.       pushSTACK(obj);
  559.       fehler(error,
  560.              DEUTSCH ? "Integer ~ ist zu gro▀ oder zu klein und kann daher nicht auf ~ ausgegeben werden." :
  561.              ENGLISH ? "integer ~ is out of range, cannot be output onto ~" :
  562.              FRANCAIS ? "L'entier ~, n'Θtant pas dans l'intervalle souhaitΘ, ne peut pas Ωtre Θcrit dans ~." :
  563.              ""
  564.             );
  565.     }
  566.  
  567. # Fehler, wenn ein Argument kein Fixnum >=0 ist:
  568. # fehler_bad_lpos();
  569. # > STACK_0: lpos
  570.   nonreturning_function(local, fehler_bad_lpos, (void));
  571.   local void fehler_bad_lpos()
  572.     { # line-position in STACK_0
  573.       pushSTACK(STACK_0); # Wert fⁿr Slot DATUM von TYPE-ERROR
  574.       pushSTACK(O(type_posfixnum)); # Wert fⁿr Slot EXPECTED-TYPE von TYPE-ERROR
  575.       pushSTACK(STACK_(0+2));
  576.       pushSTACK(TheSubr(subr_self)->name);
  577.       fehler(type_error,
  578.              DEUTSCH ? "~: Argument mu▀ ein Fixnum >=0 sein, nicht ~" :
  579.              ENGLISH ? "~: argument ~ should be a nonnegative fixnum" :
  580.              FRANCAIS ? "~ : L'argument doit Ωtre de type FIXNUM positif ou zΘro et non pas ~.":
  581.              ""
  582.             );
  583.     }
  584.  
  585. # UP: ▄berprⁿft Argumente, ob sie Streams sind.
  586. # test_stream_args(args_pointer,argcount);
  587. # > args_pointer: Pointer ⁿber die Argumente
  588. # > argcount: Anzahl der Argumente
  589. # > subr_self: Aufrufer (ein SUBR)
  590.   local void test_stream_args (object* args_pointer, uintC argcount);
  591.   local void test_stream_args(args_pointer, argcount)
  592.     var reg1 object* args_pointer;
  593.     var reg2 uintC argcount;
  594.     { dotimesC(argcount,argcount,
  595.         { var reg3 object next_arg = NEXT(args_pointer);
  596.           if (!streamp(next_arg)) { fehler_stream(next_arg); }
  597.         });
  598.     }
  599.  
  600.  
  601. #if defined(UNIX) || defined(EMUNIX) || defined(DJUNIX) || defined(WATCOM) || defined(RISCOS)
  602.  
  603. # UP: L÷scht bereits eingegebenen interaktiven Input von einem Handle.
  604.   local void clear_tty_input (Handle handle);
  605.   #if !(defined(DJUNIX) || defined(RISCOS))
  606.   local void clear_tty_input(handle)
  607.     var reg1 Handle handle;
  608.     { # Methode 1: tcflush TCIFLUSH, siehe TERMIOS(3V)
  609.       # Methode 2: ioctl TCFLSH TCIFLUSH, siehe TERMIO(4)
  610.       # Methode 3: ioctl TIOCFLUSH FREAD, siehe TTCOMPAT(4)
  611.       begin_system_call();
  612.       #ifdef UNIX_TERM_TERMIOS
  613.       if (!( TCFLUSH(handle,TCIFLUSH) ==0))
  614.         { if (!((errno==ENOTTY)||(errno==EINVAL))) { OS_error(); } } # kein TTY: OK, sonstigen Error melden
  615.       #endif
  616.       #ifdef UNIX_TERM_TERMIO
  617.       #ifdef TCIFLUSH # !RISCOS
  618.       if (!( ioctl(handle,TCFLSH,(CADDR_T)TCIFLUSH) ==0))
  619.         { if (!(errno==ENOTTY)) { OS_error(); } } # kein TTY: OK, sonstigen Error melden
  620.       #endif
  621.       #endif
  622.       #ifdef UNIX_TERM_SGTTY
  623.       #ifdef FREAD # !UNIX_MINT
  624.       {var int arg = FREAD;
  625.        if (!( ioctl(handle,TIOCFLUSH,&arg) ==0))
  626.          { if (!(errno==ENOTTY)) { OS_error(); } } # kein TTY: OK, sonstigen Error melden
  627.       }
  628.       #endif
  629.       #endif
  630.       #ifdef EMUNIX
  631.       # Eberhard Mattes sagt, das funktioniert nur, wenn IDEFAULT nicht
  632.       # gesetzt ist. ??
  633.       if (!( ioctl(handle,TCFLSH,0) ==0))
  634.         { if (!(errno==ENOTTY)) { OS_error(); } } # kein TTY: OK, sonstigen Erro
  635.       #endif
  636.       end_system_call();
  637.     }
  638.   #else
  639.     #define clear_tty_input(handle)
  640.   #endif
  641.  
  642. # UP: Bringt den wartenden Output eines Handles ans Ziel.
  643.   local void finish_tty_output (Handle handle);
  644.   #if !(defined(DJUNIX) || defined(WATCOM) || defined(RISCOS))
  645.   local void finish_tty_output(handle)
  646.     var reg1 Handle handle;
  647.     { # Methode 1: fsync, siehe FSYNC(2)
  648.       # Methode 2: tcdrain, siehe TERMIOS(3V)
  649.       # Methode 3: ioctl TCSBRK 1, siehe TERMIO(4)
  650.       # evtl. Methode 3: ioctl TCGETS/TCSETSW, siehe TERMIO(4)
  651.       # oder (fast Σquivalent) ioctl TIOCGETP/TIOCSETP, siehe TTCOMPAT(4)
  652.       begin_system_call();
  653.       #if !(defined(UNIX) && !defined(HAVE_FSYNC))
  654.       if (!( fsync(handle) ==0))
  655.         #ifdef EMUNIX_NEW_8e
  656.         #ifdef EMUNIX_NEW_9a
  657.         if (!(errno==ENOSYS))
  658.         #else
  659.         if (!(errno==EMSDOS))
  660.         #endif
  661.         #endif
  662.         if (!(errno==EINVAL)) { OS_error(); }
  663.       #endif
  664.       #ifdef UNIX_TERM_TERMIOS
  665.       if (!( TCDRAIN(handle) ==0))
  666.         { if (!((errno==ENOTTY)||(errno==EINVAL))) { OS_error(); } } # kein TTY: OK, sonstigen Error melden
  667.       #endif
  668.       #ifdef UNIX_TERM_TERMIO
  669.       if (!( ioctl(handle,TCSBRK,(CADDR_T)1) ==0))
  670.         { if (!(errno==ENOTTY)) { OS_error(); } }
  671.       #endif
  672.       #if defined(UNIX_TERM_TERMIOS) && defined(TCGETS) && defined(TCSETSW)
  673.       {var struct termios term_parameters;
  674.        if (!(   ( ioctl(handle,TCGETS,&term_parameters) ==0)
  675.              && ( ioctl(handle,TCSETSW,&term_parameters) ==0)
  676.           ) )
  677.          { if (!((errno==ENOTTY)||(errno==EINVAL))) { OS_error(); } } # kein TTY: OK, sonstigen Error melden
  678.       }
  679.       #endif
  680.       #ifdef EMUNIX
  681.       {var struct termio term_parameters;
  682.        if (!(   ( ioctl(handle,TCGETA,&term_parameters) ==0)
  683.              && ( ioctl(handle,TCSETAW,&term_parameters) ==0)
  684.           ) )
  685.          { if (!(errno==ENOTTY)) { OS_error(); } }
  686.       }
  687.       #endif
  688.       #if 0 # Vorsicht: das mⁿ▀te FINISH-OUTPUT und CLEAR-INPUT bewirken!
  689.       {var struct sgttyb tty_parameters;
  690.        if (!(   ( ioctl(handle,TIOCGETP,&tty_parameters) ==0)
  691.              && ( ioctl(handle,TIOCSETP,&tty_parameters) ==0)
  692.           ) )
  693.          { if (!(errno==ENOTTY)) { OS_error(); } }
  694.       }
  695.       #endif
  696.       end_system_call();
  697.     }
  698.   #else
  699.     #define finish_tty_output(handle)
  700.   #endif
  701.  
  702. # UP: Bringt den wartenden Output eines Handles ans Ziel.
  703.   local void force_tty_output (Handle handle);
  704.   #if !(defined(DJUNIX) || defined(WATCOM) || (defined(UNIX) && !defined(HAVE_FSYNC)) || defined(RISCOS))
  705.   local void force_tty_output(handle)
  706.     var reg1 Handle handle;
  707.     { # Methode: fsync, siehe FSYNC(2)
  708.       begin_system_call();
  709.       if (!( fsync(handle) ==0))
  710.         #ifdef EMUNIX_NEW_8e
  711.         #ifdef EMUNIX_NEW_9a
  712.         if (!(errno==ENOSYS))
  713.         #else
  714.         if (!(errno==EMSDOS))
  715.         #endif
  716.         #endif
  717.         if (!(errno==EINVAL)) { OS_error(); }
  718.       end_system_call();
  719.     }
  720.   #else
  721.     #define force_tty_output(handle)
  722.   #endif
  723.  
  724. # UP: L÷scht den wartenden Output eines Handles.
  725.   local void clear_tty_output (Handle handle);
  726.   #if !(defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS))
  727.   local void clear_tty_output(handle)
  728.     var reg1 Handle handle;
  729.     { # Methode 1: tcflush TCOFLUSH, siehe TERMIOS(3V)
  730.       # Methode 2: ioctl TCFLSH TCOFLUSH, siehe TERMIO(4)
  731.       # Methode 3: ioctl TIOCFLUSH FWRITE, siehe TTCOMPAT(4)
  732.       begin_system_call();
  733.       #ifdef UNIX_TERM_TERMIOS
  734.       if (!( TCFLUSH(handle,TCOFLUSH) ==0))
  735.         { if (!((errno==ENOTTY)||(errno==EINVAL))) { OS_error(); } } # kein TTY: OK, sonstigen Error melden
  736.       #endif
  737.       #ifdef UNIX_TERM_TERMIO
  738.       #ifdef TCOFLUSH # !RISCOS
  739.       if (!( ioctl(handle,TCFLSH,(CADDR_T)TCOFLUSH) ==0))
  740.         { if (!(errno==ENOTTY)) { OS_error(); } } # kein TTY: OK, sonstigen Error melden
  741.       #endif
  742.       #endif
  743.       #ifdef UNIX_TERM_SGTTY
  744.       #ifdef FWRITE # !UNIX_MINT
  745.       {var int arg = FWRITE;
  746.        if (!( ioctl(handle,TIOCFLUSH,&arg) ==0))
  747.          { if (!(errno==ENOTTY)) { OS_error(); } } # kein TTY: OK, sonstigen Error melden
  748.       }
  749.       #endif
  750.       #endif
  751.       end_system_call();
  752.     }
  753.   #else
  754.     #define clear_tty_output(handle)
  755.   #endif
  756.  
  757. #endif
  758.  
  759. #if defined(AMIGAOS)
  760.  
  761. # UP: Bringt den wartenden Output eines Handles ans Ziel.
  762.   local void finish_tty_output (Handle handle);
  763.   # Wir k÷nnen nichts tun, da wir handle nicht schlie▀en dⁿrfen und
  764.   # kein fsync() haben.
  765.   #define finish_tty_output(handle)
  766.  
  767. # UP: Bringt den wartenden Output eines Handles ans Ziel.
  768.   local void force_tty_output (Handle handle);
  769.   #define force_tty_output(handle)  finish_tty_output(handle)
  770.  
  771. # UP: L÷scht den wartenden Output eines Handles.
  772.   local void clear_tty_output (Handle handle);
  773.   # Nichts zu tun.
  774.   #define clear_tty_output(handle)
  775.  
  776. #endif
  777.  
  778.  
  779. #if (defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(AMIGAOS) || defined(RISCOS)) && (!defined(TERMINAL_USES_KEYBOARD) || defined(HANDLES) || defined(PIPES) || defined(SOCKETS))
  780. #define XHANDLES
  781.  
  782. # Handle-Streams
  783. # ==============
  784.  
  785. # sind ein gemeinsamer Rahmen fⁿr Streams, deren Input/Output ungebuffert
  786. # ⁿber ein Handle des Betriebssystems abgewickelt wird. Umfa▀t:
  787. # Input: Terminal-Stream, File-Handle-Stream, Pipe-Input-Stream, Socket-Stream.
  788. # Output: File-Handle-Stream, Pipe-Output-Stream, Socket-Stream.
  789.  
  790. #define strm_isatty   strm_other[0]  # Flag, ob das Input-Handle ein TTY ist
  791. #define strm_ihandle  strm_other[1]  # Input-Handle immer als zweite Komponente
  792. #define strm_ohandle  strm_other[2]  # Output-Handle immer als dritte Komponente
  793.  
  794. # Da▀ beim Input EOF erreicht ist, erkennt man an
  795. # TheStream(stream)->strm_rd_ch_last = eof_value.
  796.  
  797. # READ-CHAR - Pseudofunktion fⁿr Handle-Streams:
  798.   local object rd_ch_handle (object* stream_);
  799.   local object rd_ch_handle(stream_)
  800.     var reg4 object* stream_;
  801.     {   restart_it:
  802.      {  var reg2 object stream = *stream_;
  803.         if (eq(TheStream(stream)->strm_rd_ch_last,eof_value)) # schon EOF?
  804.           { return eof_value; }
  805.       { var reg3 Handle handle = TheHandle(TheStream(stream)->strm_ihandle);
  806.         var uintB c;
  807.         #if defined(AMIGAOS)
  808.         interruptp({ pushSTACK(S(read_char)); tast_break(); # Ctrl-C -> Break-Schleife aufrufen
  809.                      return read_char(stream_);
  810.                    });
  811.         #endif
  812.         run_time_stop(); # Run-Time-Stoppuhr anhalten
  813.         #ifdef GRAPHICS_SWITCH
  814.         if (handle == stdin_handle) switch_text_mode();
  815.         #endif
  816.         begin_system_call();
  817.        {
  818.         #if !defined(AMIGAOS)
  819.         var reg1 int ergebnis = read(handle,&c,1); # Zeichen lesen versuchen
  820.         #else # defined(AMIGAOS)
  821.         var reg1 long ergebnis = Read(handle,&c,1L); # Zeichen lesen versuchen
  822.         #endif
  823.         end_system_call();
  824.         run_time_restart(); # Run-Time-Stoppuhr weiterlaufen lassen
  825.         if (ergebnis<0)
  826.           {
  827.             #if !defined(AMIGAOS)
  828.             if (errno==EINTR) # Unterbrechung (evtl. durch Ctrl-C) ?
  829.               { interruptp({ pushSTACK(S(read_char)); tast_break(); }); # Break-Schleife aufrufen
  830.                 goto restart_it;
  831.               }
  832.             #endif
  833.             OS_error();
  834.           }
  835.         if (ergebnis==0)
  836.           # kein Zeichen verfⁿgbar -> mu▀ EOF sein
  837.           { return eof_value; }
  838.           else
  839.           {
  840.             #if defined(AMIGAOS)
  841.             # Ctrl-C wird meist wΣhrend des Read()-Aufrufs festgestellt, und
  842.             # Read() liefert dann "unschuldig" ein Zeichen ab. Wir behandeln
  843.             # das Ctrl-C jetzt. Damit das Zeichen nicht verlorengeht, wird
  844.             # es wie durch unread_char() zurⁿckgelegt.
  845.             interruptp(
  846.               { TheStream(stream)->strm_rd_ch_last = fixnum(c);
  847.                 pushSTACK(S(read_char)); tast_break(); # Break-Schleife aufrufen
  848.                 return read_char(stream_);
  849.               });
  850.             #endif
  851.             return code_char(c);
  852.           }
  853.     }}}}
  854.  
  855. # Stellt fest, ob ein Handle-Stream ein Zeichen verfⁿgbar hat.
  856. # listen_handle(stream)
  857. # > stream: Handle-Stream
  858. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  859. #             -1 falls bei EOF angelangt,
  860. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  861.   local signean listen_handle (object stream);
  862.   local signean listen_handle(stream)
  863.     var reg2 object stream;
  864.     { if (eq(TheStream(stream)->strm_rd_ch_last,eof_value)) # schon EOF ?
  865.         { return signean_minus; }
  866.       # Methode 1: select, siehe SELECT(2)
  867.       # Methode 2: ioctl FIONREAD, siehe FILIO(4)
  868.       # Methode 3: kurzzeitig auf non-blocking I/O schalten und read versuchen,
  869.       #            siehe READ(2V) und FILIO(4), bzw.
  870.       #            siehe READ(2V), FCNTL(2V) und FCNTL(5)
  871.      {var reg3 Handle handle = TheHandle(TheStream(stream)->strm_ihandle);
  872.       #if defined(MSDOS) && !defined(EMUNIX_PORTABEL)
  873.       { var reg2 uintB status;
  874.         begin_system_call();
  875.         get_handle_input_status(handle,status);
  876.         end_system_call();
  877.         if (status) { return signean_null; } # Zeichen verfⁿgbar
  878.       }
  879.       if (!nullp(TheStream(stream)->strm_isatty))
  880.         # Terminal
  881.         { return signean_plus; } # kein Zeichen verfⁿgbar
  882.         else
  883.         # File
  884.         # kein Zeichen verfⁿgbar -> EOF erkennen
  885.         { TheStream(stream)->strm_rd_ch_last = eof_value;
  886.           return signean_minus;
  887.         }
  888.       #elif defined(EMUNIX_PORTABEL)
  889.       { var struct termio oldtermio;
  890.         var struct termio newtermio;
  891.         begin_system_call();
  892.         if (!( ioctl(handle,TCGETA,&oldtermio) ==0))
  893.           { if (!((errno==ENOTTY)||(errno==EINVAL))) { OS_error(); } }
  894.         newtermio = oldtermio;
  895.         newtermio.c_lflag &= ~IDEFAULT & ~ICANON;
  896.         if (!( ioctl(handle,TCSETA,&newtermio) ==0))
  897.           { if (!((errno==ENOTTY)||(errno==EINVAL))) { OS_error(); } }
  898.        {var uintL chars_ready = 0;
  899.         var int result = ioctl(handle,FIONREAD,&chars_ready); # abfragen
  900.         # (Bei EMUNIX_NEW_8f k÷nnte man das auch mit select() machen.)
  901.         if (!( ioctl(handle,TCSETA,&oldtermio) ==0))
  902.           { if (!((errno==ENOTTY)||(errno==EINVAL))) { OS_error(); } }
  903.         end_system_call();
  904.         if (result == 0)
  905.           # Abfrage gelungen
  906.           { if (chars_ready > 0) { return signean_null; } } # welche verfⁿgbar?
  907.         #ifdef EMUNIX_NEW_8e
  908.         begin_system_call();
  909.         if (!isatty(handle))
  910.           { result = eof(handle);
  911.             if (result<0)
  912.               { if (!(errno==ESPIPE)) { OS_error(); } } # "Illegal seek error" ist OK
  913.               else
  914.               { end_system_call();
  915.                 if (result>0) # EOF erreicht?
  916.                   { return signean_minus; }
  917.                   else
  918.                   { return signean_null; }
  919.           }   }
  920.         end_system_call();
  921.         #endif
  922.         return signean_plus; # offenbar kein Zeichen verfⁿgbar
  923.       }}
  924.       #elif !defined(AMIGAOS)
  925.       #ifdef HAVE_SELECT
  926.       { # Verwende select mit readfds = einelementige Menge {handle}
  927.         # und timeout = Null-Zeitintervall.
  928.         var fd_set handle_menge; # Menge von Handles := {handle}
  929.         var struct timeval zero_time; # Zeitintervall := 0
  930.         FD_ZERO(&handle_menge); FD_SET(handle,&handle_menge);
  931.         restart_select:
  932.         zero_time.tv_sec = 0; zero_time.tv_usec = 0;
  933.         begin_system_call();
  934.        {var reg1 int ergebnis;
  935.         ergebnis = select(FD_SETSIZE,&handle_menge,NULL,NULL,&zero_time);
  936.         end_system_call();
  937.         if (ergebnis<0)
  938.           { if (errno==EINTR) goto restart_select;
  939.             if (!(errno==EBADF)) { OS_error(); } # UNIX_LINUX liefert bei Files EBADF !
  940.           }
  941.           else
  942.           { # ergebnis = Anzahl der Handles in handle_menge, bei denen read
  943.             # sofort ein Ergebnis liefern wⁿrde.
  944.             if (ergebnis==0)
  945.               { return signean_plus; } # kein Zeichen verfⁿgbar
  946.             # ergebnis=1
  947.             # Wenn read() sofort ein Ergebnis liefern wⁿrde, kann das auch EOF
  948.             # sein! (Beispiel: Linux und Pipes.) Wir hⁿten uns daher vor
  949.             # einem  { return signean_null; }  und versuchen stattdessen
  950.             # erst noch Methoden 2 und 3.
  951.       }}  }
  952.       #endif
  953.       #ifdef HAVE_FIONREAD
  954.       # versuche die Zahl der verfⁿgbaren Zeichen abzufragen:
  955.       begin_system_call();
  956.       {var uintL chars_ready;
  957.        if ( ioctl(handle,FIONREAD,&chars_ready) ==0) # abfragen
  958.          # Abfrage gelungen, also war's ein File
  959.          { end_system_call();
  960.            if (chars_ready > 0) { return signean_null; } # welche verfⁿgbar?
  961.            # sonst EOF des File erkennen:
  962.            TheStream(stream)->strm_rd_ch_last = eof_value;
  963.            return signean_minus;
  964.          }
  965.        if (!((errno == ENOTTY)||(errno == EINVAL))) { OS_error(); }
  966.        end_system_call();
  967.       }# Abfrage mi▀lungen, war wohl kein File
  968.       #endif
  969.       #ifdef GRAPHICS_SWITCH
  970.       if (handle == stdin_handle) switch_text_mode();
  971.       #endif
  972.       #ifndef HAVE_SELECT
  973.       if (!nullp(TheStream(stream)->strm_isatty))
  974.         # Terminal
  975.         { # in Non-blocking-Modus umschalten, dann read() versuchen:
  976.           var uintB c;
  977.           var int ergebnis;
  978.           begin_system_call();
  979.           restart_read_tty:
  980.           #ifdef FIONBIO # non-blocking I/O α la BSD 4.2
  981.           { var int non_blocking_io;
  982.             non_blocking_io = 1;
  983.             if (!( ioctl(handle,FIONBIO,&non_blocking_io) ==0))
  984.               { OS_error(); }
  985.             ergebnis = read(handle,&c,1); # Zeichen lesen versuchen
  986.             non_blocking_io = 0;
  987.             if (!( ioctl(handle,FIONBIO,&non_blocking_io) ==0))
  988.               { OS_error(); }
  989.           }
  990.           #else # non-blocking I/O α la SYSV
  991.           { var reg2 int fcntl_flags;
  992.             if (( fcntl_flags = fcntl(handle,F_GETFL,0) )<0) { OS_error(); }
  993.             if ( fcntl(handle,F_SETFL,fcntl_flags|O_NDELAY) <0) { OS_error(); }
  994.             ergebnis = read(handle,&c,1); # Zeichen lesen versuchen
  995.             if ( fcntl(handle,F_SETFL,fcntl_flags) <0) { OS_error(); }
  996.           }
  997.           #endif
  998.           if (ergebnis < 0)
  999.             { if (errno==EINTR) goto restart_read_tty;
  1000.               #ifdef FIONBIO
  1001.               if (errno==EWOULDBLOCK) # BSD 4.2 Error-Code
  1002.               #else
  1003.               if ((errno==EAGAIN) # Posix Error-Code
  1004.                   #ifdef EWOULDBLOCK
  1005.                   || (errno==EWOULDBLOCK)
  1006.                   #endif
  1007.                  )
  1008.               #endif
  1009.                 { return signean_plus; } # kein Zeichen verfⁿgbar
  1010.               OS_error();
  1011.             }
  1012.           end_system_call();
  1013.           if (ergebnis==0)
  1014.             # kein Zeichen verfⁿgbar
  1015.             { return signean_plus; }
  1016.             else
  1017.             # Zeichen verfⁿgbar
  1018.             { TheStream(stream)->strm_rd_ch_last = fixnum(c);
  1019.               return signean_null;
  1020.             }
  1021.           # Sollte das nicht gehen, einen Timer von 1/10 sec verwenden??
  1022.         }
  1023.         else
  1024.       #endif
  1025.         # File (oder Pipe)
  1026.         { # ein Zeichen lesen versuchen (wie bei peek_char):
  1027.           begin_system_call();
  1028.           restart_read_other:
  1029.          {var uintB c;
  1030.           var reg1 int ergebnis = read(handle,&c,1); # Zeichen lesen versuchen
  1031.           if (ergebnis<0)
  1032.             { if (errno==EINTR) goto restart_read_other;
  1033.               OS_error();
  1034.             }
  1035.           end_system_call();
  1036.           if (ergebnis==0)
  1037.             # kein Zeichen verfⁿgbar -> EOF erkennen
  1038.             { TheStream(stream)->strm_rd_ch_last = eof_value;
  1039.               return signean_minus;
  1040.             }
  1041.             else # Zeichen verfⁿgbar
  1042.             { TheStream(stream)->strm_rd_ch_last = fixnum(c);
  1043.               return signean_null;
  1044.             }
  1045.         }}
  1046.       #else # defined(AMIGAOS)
  1047.       begin_system_call();
  1048.       if (!nullp(TheStream(stream)->strm_isatty))
  1049.         # interaktiv
  1050.         { if (WaitForChar(handle,1000L)) # 1/1000 sec auf ein Zeichen warten
  1051.             { end_system_call(); return signean_null; } # eins da
  1052.             else
  1053.             { end_system_call(); return signean_plus; } # keins da
  1054.         }
  1055.         else
  1056.         # nicht interaktiv
  1057.         { # ein Zeichen lesen versuchen (wie bei peek_char):
  1058.           var uintB c;
  1059.           var reg1 long ergebnis = Read(handle,&c,1L); # Zeichen lesen versuchen
  1060.           end_system_call();
  1061.           if (ergebnis<0) { OS_error(); }
  1062.           if (ergebnis==0)
  1063.             # kein Zeichen verfⁿgbar -> EOF erkennen
  1064.             { TheStream(stream)->strm_rd_ch_last = eof_value;
  1065.               return signean_minus;
  1066.             }
  1067.             else # Zeichen verfⁿgbar
  1068.             { TheStream(stream)->strm_rd_ch_last = fixnum(c);
  1069.               return signean_null;
  1070.             }
  1071.         }
  1072.       #endif
  1073.     }}
  1074.  
  1075. # UP: L÷scht bereits eingegebenen interaktiven Input von einem Handle-Stream.
  1076. # clear_input_handle(stream);
  1077. # > stream: Handle-Stream
  1078. # < ergebnis: TRUE falls Input gel÷scht wurde, FALSE sonst
  1079.   local boolean clear_input_handle (object stream);
  1080.   local boolean clear_input_handle(stream)
  1081.     var reg1 object stream;
  1082.     { var reg1 Handle handle = TheHandle(TheStream(stream)->strm_ihandle);
  1083.       if (nullp(TheStream(stream)->strm_isatty))
  1084.         # File -> nichts tun
  1085.         { return FALSE; }
  1086.       #if !defined(AMIGAOS)
  1087.       # Terminal
  1088.       TheStream(stream)->strm_rd_ch_last = NIL; # gewesenes EOF vergessen
  1089.       clear_tty_input(handle);
  1090.       # Fⁿr den Fall, das das nicht funktionierte:
  1091.       # Zeichen lesen, solange listen_handle() 0 liefert.
  1092.       pushSTACK(stream);
  1093.       while (listen_handle(STACK_0) == 0) { read_char(&STACK_0); }
  1094.       skipSTACK(1);
  1095.       return TRUE;
  1096.       #else # defined(AMIGAOS)
  1097.       # interaktiv
  1098.       { begin_system_call();
  1099.         loop
  1100.           { if (!WaitForChar(handle,1000L)) # 1/1000 sec auf ein Zeichen warten
  1101.               break; # keins mehr da -> fertig
  1102.            {var uintB c;
  1103.             var reg1 long ergebnis = Read(handle,&c,1L); # Zeichen lesen versuchen
  1104.             if (ergebnis<0) { OS_error(); }
  1105.           }}
  1106.         end_system_call();
  1107.         return TRUE;
  1108.       }
  1109.       #endif
  1110.     }
  1111.  
  1112. # WRITE-CHAR - Pseudofunktion fⁿr Handle-Streams:
  1113.   local void wr_ch_handle (object* stream_, object ch);
  1114.   local void wr_ch_handle(stream_,ch)
  1115.     var reg3 object* stream_;
  1116.     var reg1 object ch;
  1117.     { var reg2 Handle handle = TheHandle(TheStream(*stream_)->strm_ohandle);
  1118.       # ch sollte String-Char sein:
  1119.       if (!string_char_p(ch)) { fehler_wr_string_char(*stream_,ch); }
  1120.      {var uintB c = char_code(ch); # Code des Zeichens
  1121.       restart_it:
  1122.       #ifdef GRAPHICS_SWITCH
  1123.       if (handle == stdout_handle) switch_text_mode();
  1124.       #endif
  1125.       begin_system_call();
  1126.       {
  1127.        #if !defined(AMIGAOS)
  1128.        var reg4 int ergebnis = write(handle,&c,1); # Zeichen auszugeben versuchen
  1129.        end_system_call();
  1130.        if (ergebnis<0)
  1131.          { if (errno==EINTR) # Unterbrechung (evtl. durch Ctrl-C) ?
  1132.              { interruptp({ pushSTACK(S(write_char)); tast_break(); }); # Break-Schleife aufrufen
  1133.                goto restart_it;
  1134.              }
  1135.            OS_error(); # Error melden
  1136.          }
  1137.        #else # defined(AMIGAOS)
  1138.        var reg4 long ergebnis = Write(handle,&c,1L); # Zeichen auszugeben versuchen
  1139.        end_system_call();
  1140.        if (ergebnis<0) { OS_error(); } # Error melden
  1141.        interruptp({ pushSTACK(S(write_char)); tast_break(); }); # Ctrl-C -> Break-Schleife aufrufen
  1142.        #endif
  1143.        if (ergebnis==0) # nicht erfolgreich?
  1144.          { fehler_unwritable(S(write_char),*stream_); }
  1145.       }
  1146.     }}
  1147.  
  1148. # WRITE-CHAR-SEQUENCE fⁿr Handle-Streams:
  1149.   local uintB* write_schar_array_handle (object stream, uintB* ptr, uintL len);
  1150.   local uintB* write_schar_array_handle(stream,ptr,len)
  1151.     var reg5 object stream;
  1152.     var reg1 uintB* ptr;
  1153.     var reg6 uintL len;
  1154.     { var reg4 Handle handle = TheHandle(TheStream(stream)->strm_ohandle);
  1155.       var reg2 uintL remaining = len;
  1156.       #ifdef GRAPHICS_SWITCH
  1157.       if (handle == stdout_handle) switch_text_mode();
  1158.       #endif
  1159.       begin_system_call();
  1160.       restart_it:
  1161.       loop
  1162.         {
  1163.           #if !defined(AMIGAOS)
  1164.           var reg3 int ergebnis = write(handle,ptr,remaining); # Zeichen auszugeben versuchen
  1165.           if (ergebnis<0)
  1166.             { if (errno==EINTR) goto restart_it;
  1167.               OS_error(); # Error melden
  1168.             }
  1169.           #else # defined(AMIGAOS)
  1170.           var reg3 long ergebnis = Write(handle,ptr,remaining); # Zeichen auszugeben versuchen
  1171.           if (ergebnis<0) { OS_error(); } # Error melden
  1172.           #endif
  1173.           if (ergebnis==0) # nicht erfolgreich?
  1174.             { fehler_unwritable(S(write_string),stream); }
  1175.           ptr += ergebnis; remaining -= ergebnis;
  1176.           if (remaining==0) break; # fertig?
  1177.         }
  1178.       end_system_call();
  1179.       wr_ss_lpos(stream,ptr,len); # Line-Position aktualisieren
  1180.       return ptr;
  1181.     }
  1182.  
  1183. #ifdef STRM_WR_SS
  1184. # WRITE-SIMPLE-STRING - Pseudofunktion fⁿr Handle-Streams:
  1185.   local void wr_ss_handle (object* stream_, object string, uintL start, uintL len);
  1186.   local void wr_ss_handle(stream_,string,start,len)
  1187.     var reg1 object* stream_;
  1188.     var reg2 object string;
  1189.     var reg4 uintL start;
  1190.     var reg3 uintL len;
  1191.     { if (len==0) return;
  1192.       write_schar_array_handle(*stream_,&TheSstring(string)->data[start],len);
  1193.     }
  1194. #endif
  1195.  
  1196. # UP: Bringt den wartenden Output eines Handle-Stream ans Ziel.
  1197. # finish_output_handle(stream);
  1198. # > stream: Handle-Stream
  1199. # kann GC ausl÷sen
  1200.   local void finish_output_handle (object stream);
  1201.   local void finish_output_handle(stream)
  1202.     var reg1 object stream;
  1203.     { finish_tty_output(TheHandle(TheStream(stream)->strm_ohandle)); }
  1204.  
  1205. # UP: Bringt den wartenden Output eines Handle-Stream ans Ziel.
  1206. # force_output_handle(stream);
  1207. # > stream: Handle-Stream
  1208. # kann GC ausl÷sen
  1209.   local void force_output_handle (object stream);
  1210.   local void force_output_handle(stream)
  1211.     var reg1 object stream;
  1212.     { force_tty_output(TheHandle(TheStream(stream)->strm_ohandle)); }
  1213.  
  1214. # UP: L÷scht den wartenden Output eines Handle-Stream.
  1215. # clear_output_handle(stream);
  1216. # > stream: Handle-Stream
  1217. # kann GC ausl÷sen
  1218.   local void clear_output_handle (object stream);
  1219.   local void clear_output_handle(stream)
  1220.     var reg1 object stream;
  1221.     { clear_tty_output(TheHandle(TheStream(stream)->strm_ohandle)); }
  1222.  
  1223. #if defined(HANDLES) || defined(SOCKETS)
  1224.  
  1225. # READ-BYTE - Pseudofunktion fⁿr Handle-Streams:
  1226.   local object rd_by_handle (object stream);
  1227.   local object rd_by_handle(stream)
  1228.     var reg1 object stream;
  1229.     { pushSTACK(stream);
  1230.      {var reg1 object obj = read_char(&STACK_0);
  1231.       skipSTACK(1);
  1232.       if (!eq(obj,eof_value)) { obj = char_to_fixnum(obj); }
  1233.       return obj;
  1234.     }}
  1235.  
  1236. # WRITE-BYTE - Pseudofunktion fⁿr Handle-Streams:
  1237.   local void wr_by_handle (object stream, object obj);
  1238.   local void wr_by_handle(stream,obj)
  1239.     var reg1 object stream;
  1240.     var reg2 object obj;
  1241.     { # obj ⁿberprⁿfen:
  1242.       if (!integerp(obj)) { fehler_wr_integer(stream,obj); }
  1243.       if (!(posfixnump(obj) && (posfixnum_to_L(obj) < char_code_limit)))
  1244.         { fehler_bad_integer(stream,obj); }
  1245.       pushSTACK(stream);
  1246.       wr_ch_handle(&STACK_0,fixnum_to_char(obj));
  1247.       skipSTACK(1);
  1248.     }
  1249.  
  1250. #endif
  1251.  
  1252. #if defined(HANDLES) || (defined(PIPES) && defined(UNIX)) || defined(SOCKETS)
  1253.  
  1254. # Schlie▀t einen Handle-Stream.
  1255. # close_ihandle(stream);
  1256. # close_ohandle(stream);
  1257. # > stream : Handle-Stream
  1258.   local void close_ihandle (object stream);
  1259.   local void close_ohandle (object stream);
  1260.   local void close_ihandle(stream)
  1261.     var reg1 object stream;
  1262.     { var reg2 Handle handle = TheHandle(TheStream(stream)->strm_ihandle);
  1263.       begin_system_call();
  1264.       if (!( CLOSE(handle) ==0)) { OS_error(); }
  1265.       end_system_call();
  1266.     }
  1267.   local void close_ohandle(stream)
  1268.     var reg1 object stream;
  1269.     { var reg2 Handle handle = TheHandle(TheStream(stream)->strm_ohandle);
  1270.       begin_system_call();
  1271.       if (!( CLOSE(handle) ==0)) { OS_error(); }
  1272.       end_system_call();
  1273.     }
  1274.  
  1275. #endif
  1276.  
  1277. #if defined(HANDLES)
  1278.  
  1279. #define close_handle  close_ihandle
  1280.  
  1281. # UP: erzeugt ein File-Handle-Stream
  1282. # make_handle_stream(handle,direction)
  1283. # > handle: Handle des ge÷ffneten Files
  1284. # > STACK_1: Filename, ein Pathname
  1285. # > STACK_0: Truename, ein Pathname
  1286. # > direction: Modus (0 = :PROBE, 1 = :INPUT, 4 = :OUTPUT, 5 = :IO, 3 = :INPUT-IMMUTABLE)
  1287. # < ergebnis: File-Handle-Stream
  1288. # < STACK: aufgerΣumt
  1289. # kann GC ausl÷sen
  1290.   local object make_handle_stream (object handle, uintB direction);
  1291.   local object make_handle_stream(handle,direction)
  1292.     var reg4 object handle;
  1293.     var reg2 uintB direction;
  1294.     { # Flags:
  1295.       var reg3 uintB flags =
  1296.           ((direction & bit(0)) ? strmflags_rd_B : 0) # evtl. READ-CHAR, READ-BYTE erlaubt
  1297.         | ((direction & bit(2)) ? strmflags_wr_B : 0) # evtl. WRITE-CHAR, WRITE-BYTE erlaubt
  1298.         #ifdef IMMUTABLE
  1299.         | ((direction & bit(1)) ? strmflags_immut_B : 0) # evtl. immutable Objekte
  1300.         #endif
  1301.         ;
  1302.       #if defined(FOREIGN_HANDLE) || !NIL_IS_CONSTANT
  1303.       pushSTACK(handle); # Handle retten
  1304.       #endif
  1305.      {# Stream allozieren:
  1306.       var reg1 object stream = allocate_stream(flags,strmtype_handle,strm_len+5);
  1307.       # und fⁿllen:
  1308.       if (direction & bit(0))
  1309.         { TheStream(stream)->strm_rd_by = P(rd_by_handle);
  1310.           TheStream(stream)->strm_rd_ch = P(rd_ch_handle);
  1311.         }
  1312.         else
  1313.         { TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  1314.           TheStream(stream)->strm_rd_ch = P(rd_ch_dummy);
  1315.         }
  1316.       if (direction & bit(2))
  1317.         { TheStream(stream)->strm_wr_by = P(wr_by_handle);
  1318.           TheStream(stream)->strm_wr_ch = P(wr_ch_handle);
  1319.           #ifdef STRM_WR_SS
  1320.           TheStream(stream)->strm_wr_ss = P(wr_ss_handle);
  1321.           #endif
  1322.         }
  1323.         else
  1324.         { TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  1325.           TheStream(stream)->strm_wr_ch = P(wr_ch_dummy);
  1326.           #ifdef STRM_WR_SS
  1327.           TheStream(stream)->strm_wr_ss = P(wr_ss_dummy);
  1328.           #endif
  1329.         }
  1330.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  1331.       #if defined(FOREIGN_HANDLE) || !NIL_IS_CONSTANT
  1332.       handle = popSTACK(); # Handle zurⁿck
  1333.       #endif
  1334.       TheStream(stream)->strm_ihandle =
  1335.       TheStream(stream)->strm_ohandle = handle; # Handle eintragen
  1336.       # Flag isatty = (handle_tty ? T : NIL) bestimmen:
  1337.       begin_system_call();
  1338.       #if !defined(AMIGAOS)
  1339.       TheStream(stream)->strm_isatty = (isatty(TheHandle(handle)) ? T : NIL);
  1340.       #else # defined(AMIGAOS)
  1341.       TheStream(stream)->strm_isatty = (IsInteractive(TheHandle(handle)) ? T : NIL);
  1342.       #endif
  1343.       end_system_call();
  1344.       # File-Handle-Streams werden fⁿr Pathname-Zwecke wie File-Streams behandelt.
  1345.       # Daher ist (vgl. file_write_date) strm_file_handle == strm_ohandle,
  1346.       # und wir tragen nun die Pathnames ein:
  1347.       TheStream(stream)->strm_file_truename = popSTACK(); # Truename eintragen
  1348.       TheStream(stream)->strm_file_name = popSTACK(); # Filename eintragen
  1349.       # Liste der offenen Streams um stream erweitern:
  1350.       pushSTACK(stream);
  1351.       {var reg2 object new_cons = allocate_cons();
  1352.        Car(new_cons) = stream = popSTACK();
  1353.        Cdr(new_cons) = O(open_files);
  1354.        O(open_files) = new_cons;
  1355.       }
  1356.       return stream;
  1357.     }}
  1358.  
  1359. #endif
  1360.  
  1361. #endif # (UNIX || DJUNIX || EMUNIX || WATCOM || AMIGAOS || RISCOS) && (brauche Handle-Streams)
  1362.  
  1363.  
  1364. #ifdef KEYBOARD
  1365.  
  1366. # Keyboard-Stream
  1367. # ===============
  1368.  
  1369. # Funktionsweise:
  1370. # Liest ein Zeichen von Tastatur.
  1371. # Liefert ein Character mit Font=0 und folgenden Bits:
  1372. #   HYPER      falls Sondertaste.
  1373. #              Zu den Sondertasten zΣhlen die Non-Standard-Tasten.
  1374. #              ATARI:
  1375. #                Funktionstasten, Cursorblock, Ziffernblock, Delete-Taste.
  1376. #              MSDOS:
  1377. #                Funktionstasten, Cursorbl÷cke, Ziffernblock.
  1378. #   CHAR-CODE  Bei normalen Tasten der Ascii-Code,
  1379. #              bei Sondertasten:
  1380. #              ATARI:
  1381. #                F1 -> #\F1, ..., F9 -> #\F9, F10 -> #\F10,
  1382. #                Help -> #\Help, Undo -> #\Undo,
  1383. #                Insert -> #\Insert, Delete -> #\Delete,
  1384. #                ClrHome -> #\Home,
  1385. #                 -> #\Up,  -> #\Down,  -> #\Left,  -> #\Right.
  1386. #              MSDOS:
  1387. #                F1 -> #\F1, ..., F10 -> #\F10, F11 -> #\F11, F12 -> #\F12,
  1388. #                Insert -> #\Insert, Delete -> #\Delete,
  1389. #                Home -> #\Home, End -> #\End, PgUp -> #\PgUp, PgDn -> #\PgDn,
  1390. #                Pfeiltasten -> #\Up, #\Down, #\Left, #\Right.
  1391. #   SUPER      falls mit Shift-Taste(n) gedrⁿckt und sich ohne Shift
  1392. #              ein anderer Code ergeben hΣtte,
  1393. #   CONTROL    falls mit Control-Taste gedrⁿckt,
  1394. #   META       falls mit Alternate-Taste gedrⁿckt.
  1395.  
  1396. #if defined(UNIX) || defined(RISCOS)
  1397.   # ZusΣtzliche Komponenten:
  1398.   #define strm_keyboard_isatty  strm_isatty   # Flag, ob stdin ein Terminal ist
  1399.   #define strm_keyboard_handle  strm_ihandle  # Handle fⁿr listen_handle()
  1400.   #define strm_keyboard_buffer  strm_other[2] # Liste der noch zu liefernden Zeichen
  1401.   #define strm_keyboard_keytab  strm_other[3] # Liste aller Tastenzuordnungen
  1402.                                               # jeweils (char1 ... charn . result)
  1403.   #define strm_keyboard_len  4
  1404. #else
  1405.   # Keine zusΣtzlichen Komponenten.
  1406.   #define strm_keyboard_len  0
  1407. #endif
  1408.  
  1409. #ifdef MSDOS
  1410.  
  1411. #ifdef WINDOWS
  1412.  
  1413. # Tastaturabfrage im Haupt-Fenster
  1414. #define kbhit  win_main_kbhit
  1415. #define getch  win_main_getch
  1416.  
  1417. #else
  1418.  
  1419. # Fⁿr Tastaturabfrage unter DOS:
  1420. #
  1421. # INT 16 documentation:
  1422. #   INT 16,00 - Wait for keystroke and read
  1423. #   INT 16,01 - Get keystroke status
  1424. #   INT 16,02 - Get shift status
  1425. #   INT 16,03 - Set keyboard typematic rate (AT+)
  1426. #   INT 16,04 - Keyboard click adjustment (AT+)
  1427. #   INT 16,05 - Keyboard buffer write  (AT,PS/2 enhanced keyboards)
  1428. #   INT 16,10 - Wait for keystroke and read  (AT,PS/2 enhanced keyboards)
  1429. #   INT 16,11 - Get keystroke status  (AT,PS/2 enhanced keyboards)
  1430. #   INT 16,12 - Get shift status  (AT,PS/2 enhanced keyboards)
  1431. #
  1432. # INT 16,00 - Wait for Keypress and Read Character
  1433. #     AH = 00
  1434. #     on return:
  1435. #     AH = keyboard scan code
  1436. #     AL = ASCII character or zero if special function key
  1437. #     - halts program until key with a scancode is pressed
  1438. #     - see  SCAN CODES
  1439. #
  1440. # INT 16,01 - Get Keyboard Status
  1441. #     AH = 01
  1442. #     on return:
  1443. #     ZF = 0 if a key pressed (even Ctrl-Break)
  1444. #     AX = 0 if no scan code is available
  1445. #     AH = scan code
  1446. #     AL = ASCII character or zero if special function key
  1447. #     - data code is not removed from buffer
  1448. #     - Ctrl-Break places a zero word in the keyboard buffer but does
  1449. #       register a keypress.
  1450. #
  1451. # INT 16,10 - Extended Wait for Keypress and Read Character  (AT+)
  1452. #     AH = 10h
  1453. #     on return:
  1454. #     AH = scan code
  1455. #     AL = ASCII character or zero if special function key
  1456. #     - available on AT and PS/2 machines with extended keyboard support
  1457. #     - similar to INT 16,00
  1458. #
  1459. # INT 16,11 - Extended Get Keyboard Status  (AT+)
  1460. #       AH = 11h
  1461. #       on return:
  1462. #       ZF = 0 if key pressed (data waiting)
  1463. #       AX = 0 if no scan code is available
  1464. #       AH = scan code
  1465. #       AL = ASCII character or zero if special function key
  1466. #       - available on AT and PS/2 machines with extended keyboard support
  1467. #       - data is not removed from buffer
  1468. #       - similar to INT 16,01
  1469. #
  1470.  
  1471. #if defined(DJUNIX) || defined(WATCOM)
  1472.  
  1473.   # Liefert den nΣchsten Tastendruck incl. Scan-Code:
  1474.   # high byte = Scan-Code oder 0, low byte = Ascii-Code oder 0 oder 0xE0.
  1475.   local boolean kbhit()
  1476.     { var union REGS in;
  1477.       var union REGS out;
  1478.       in.regB.ah = 0x11;
  1479.       int86(0x16,&in,&out);
  1480.       return ((out.reg_flags & 0x40) == 0); # Zero-Flag abfragen
  1481.     }
  1482.   local uintW getch()
  1483.     { var union REGS in;
  1484.       var union REGS out;
  1485.       in.regB.ah = 0x10;
  1486.       int86(0x16,&in,&out);
  1487.       return out.regW.ax;
  1488.     }
  1489.  
  1490. #endif
  1491.  
  1492. #ifdef EMUNIX
  1493.  
  1494.   # Unter DOS:
  1495.   #   Bis emx 0.8e ist uns der INT 16,10 offenbar versperrt.
  1496.   #   Wir bekommen keine Extended-Keystrokes, k÷nnen aber immerhin die Return-
  1497.   #   von der Enter-Taste unterscheiden.
  1498.   # Unter OS/2:
  1499.   #   INT 16 funktioniert nicht, dafⁿr geht _read_kbd() prΣziser als unter DOS.
  1500.  
  1501.   # Liefert unter DOS den nΣchsten Tastendruck incl. Scan-Code:
  1502.   # high byte = Scan-Code oder 0, low byte = Ascii-Code oder 0 oder 0xE0.
  1503.   #ifdef EMUNIX_OLD_8e
  1504.     #define int16_wait "0x00"
  1505.     #define int16_stat "0x01"
  1506.   #else # EMUNIX_NEW_8f
  1507.     #define int16_wait "0x10"
  1508.     #define int16_stat "0x11"
  1509.   #endif
  1510.   local boolean kbhit()
  1511.     { var reg1 boolean result;
  1512.       __asm__ __volatile__ ("movb $"int16_stat",%%ah ; .byte 0xcd ; .byte 0x16 ; "
  1513.                             "movl $0,%%eax ; jz 1f ; incl %%eax ; 1: "
  1514.                             : "=a" /* %eax */ (result) /* OUT */
  1515.                             :                          /* IN */
  1516.                             : "bx","cx","dx","si","di" /* %ebx,%ecx,%edx,%esi,%edi */ /* CLOBBER */
  1517.                            );
  1518.       return result;
  1519.     }
  1520.   local uintW getch()
  1521.     { var reg1 uintW ch;
  1522.       __asm__ __volatile__ ("movb $"int16_wait",%%ah ; .byte 0xcd ; .byte 0x16"
  1523.                             : "=a" /* %ax */ (ch)      /* OUT */
  1524.                             :                          /* IN */
  1525.                             : "bx","cx","dx","si","di" /* %ebx,%ecx,%edx,%esi,%edi */ /* CLOBBER */
  1526.                            );
  1527.       return ch;
  1528.     }
  1529.  
  1530. #endif
  1531.  
  1532.   # Tabelle der Characters, die den Scan-Codes 0..166 (als Sondertasten)
  1533.   # entsprechen:
  1534.   local cint scancode_table [167] =
  1535.     { 0,
  1536.       ESC | char_meta_c, # 1 -> Alt-Escape
  1537.       '1' | char_control_c, # [2 = Ctrl-1 -> #\CONTROL-1]
  1538.       '2' | char_control_c, # 3 = Ctrl-2 -> #\CONTROL-2
  1539.       '3' | char_control_c, # [4 = Ctrl-3 -> #\CONTROL-3]
  1540.       '4' | char_control_c, # [5 = Ctrl-4 -> #\CONTROL-4]
  1541.       '5' | char_control_c, # [6 = Ctrl-5 -> #\CONTROL-5]
  1542.       '6' | char_control_c, # 7 = Ctrl-6 -> #\CONTROL-6
  1543.       '7' | char_control_c, # [8 = Ctrl-7 -> #\CONTROL-7]
  1544.       '8' | char_control_c, # [9 = Ctrl-8 -> #\CONTROL-8]
  1545.       '9' | char_control_c, # [10 = Ctrl-9 -> #\CONTROL-9]
  1546.       '0' | char_control_c, # [11 = Ctrl-0 -> #\CONTROL-0]
  1547.       '-' | char_meta_c, # [12 = Ctrl-- -> #\CONTROL-- # nicht international portabel]
  1548.       '=' | char_meta_c, # [13 = Ctrl-= -> #\CONTROL-= # nicht international portabel]
  1549.        BS | char_meta_c, # 14 -> Alt-Backspace
  1550.         9 | char_super_c, # 15 -> Shift-Tab
  1551.       'Q' | char_meta_c, # 16 -> Alt-Q
  1552.       'W' | char_meta_c, # 17 -> Alt-W
  1553.       'E' | char_meta_c, # 18 -> Alt-E
  1554.       'R' | char_meta_c, # 19 -> Alt-R
  1555.       'T' | char_meta_c, # 20 -> Alt-T
  1556.       'Y' | char_meta_c, # 21 -> Alt-Y
  1557.       'U' | char_meta_c, # 22 -> Alt-U
  1558.       'I' | char_meta_c, # 23 -> Alt-I
  1559.       'O' | char_meta_c, # 24 -> Alt-O
  1560.       'P' | char_meta_c, # 25 -> Alt-P
  1561.       '[' | char_meta_c, # 26 -> Alt-[ # nicht international portabel
  1562.       ']' | char_meta_c, # 27 -> Alt-] # nicht international portabel
  1563.        CR | char_meta_c, # 28 = Alt-Return -> #\META-Return
  1564.       0,
  1565.       'A' | char_meta_c, # 30 -> Alt-A
  1566.       'S' | char_meta_c, # 31 -> Alt-S
  1567.       'D' | char_meta_c, # 32 -> Alt-D
  1568.       'F' | char_meta_c, # 33 -> Alt-F
  1569.       'G' | char_meta_c, # 34 -> Alt-G
  1570.       'H' | char_meta_c, # 35 -> Alt-H
  1571.       'J' | char_meta_c, # 36 -> Alt-J
  1572.       'K' | char_meta_c, # 37 -> Alt-K
  1573.       'L' | char_meta_c, # 38 -> Alt-L oder Alt-\ ??
  1574.       ';' | char_meta_c, # 39 -> Alt-; # nicht international portabel
  1575.       '\''| char_meta_c, # 40 -> Alt-' # nicht international portabel
  1576.       '`' | char_meta_c, # 41 -> Alt-` # nicht international portabel
  1577.       0,
  1578.       '\\'| char_meta_c, # 43 -> Alt-\ # nicht international portabel
  1579.       'Z' | char_meta_c, # 44 -> Alt-Z
  1580.       'X' | char_meta_c, # 45 -> Alt-X
  1581.       'C' | char_meta_c, # 46 -> Alt-C
  1582.       'V' | char_meta_c, # 47 -> Alt-V
  1583.       'B' | char_meta_c, # 48 -> Alt-B
  1584.       'N' | char_meta_c, # 49 -> Alt-N
  1585.       'M' | char_meta_c, # 50 -> Alt-M
  1586.       ',' | char_meta_c, # 51 = Alt-, -> #\META-',' # nicht international portabel
  1587.       '.' | char_meta_c, # 52 = Alt-. -> #\META-'.' # nicht international portabel
  1588.       '/' | char_meta_c, # 53 = Alt-/ -> #\META-'/' # nicht international portabel
  1589.       0,
  1590.       '*' | char_meta_c | char_hyper_c, # 55 = Alt-* -> #\META-HYPER-'*'
  1591.       0,
  1592.       ' ' | char_meta_c, # 57 = Alt-Space -> #\META-Space
  1593.       0,
  1594.       'A' | char_hyper_c, #  59 = F1 -> #\F1 = #\HYPER-A
  1595.       'B' | char_hyper_c, #  60 = F2 -> #\F2 = #\HYPER-B
  1596.       'C' | char_hyper_c, #  61 = F3 -> #\F3 = #\HYPER-C
  1597.       'D' | char_hyper_c, #  62 = F4 -> #\F4 = #\HYPER-D
  1598.       'E' | char_hyper_c, #  63 = F5 -> #\F5 = #\HYPER-E
  1599.       'F' | char_hyper_c, #  64 = F6 -> #\F6 = #\HYPER-F
  1600.       'G' | char_hyper_c, #  65 = F7 -> #\F7 = #\HYPER-G
  1601.       'H' | char_hyper_c, #  66 = F8 -> #\F8 = #\HYPER-H
  1602.       'I' | char_hyper_c, #  67 = F9 -> #\F9 = #\HYPER-I
  1603.       'J' | char_hyper_c, #  68 = F10 -> #\F10 = #\HYPER-J
  1604.       'K' | char_hyper_c, # [69 = F11 -> #\F11 = #\HYPER-K]
  1605.       'L' | char_hyper_c, # [70 = F12 -> #\F12 = #\HYPER-L]
  1606.        23 | char_hyper_c, #  71 = Home -> #\Home = #\HYPER-Code23
  1607.        24 | char_hyper_c, #  72 = Up -> #\Up = #\HYPER-Code24
  1608.        25 | char_hyper_c, #  73 = PgUp -> #\PgUp = #\HYPER-Code25
  1609.       '-' | char_meta_c | char_hyper_c, #  74 = Alt-- -> #\META-HYPER--
  1610.        20 | char_hyper_c, #  75 = Left -> #\Left = #\HYPER-Code20
  1611.        21 | char_hyper_c, # [76 -> #\HYPER-Code21]
  1612.        22 | char_hyper_c, #  77 = Right -> #\Right = #\HYPER-Code22
  1613.       '+' | char_meta_c | char_hyper_c, #  78 = Alt-+ -> #\META-HYPER-+
  1614.        17 | char_hyper_c, #  79 = End -> #\End = #\HYPER-Code17
  1615.        18 | char_hyper_c, #  80 = Down -> #\Down = #\HYPER-Code18
  1616.        19 | char_hyper_c, #  81 = PgDn -> #\PgDn = #\HYPER-Code19
  1617.        16 | char_hyper_c, #  82 = Insert -> #\Insert = #\HYPER-Code16
  1618.       127 | char_hyper_c, #  83 = Delete -> #\Delete = #\HYPER-Code127
  1619.       'A' | char_super_c | char_hyper_c, #  84 = Shift-F1 -> #\S-F1 = #\SUPER-HYPER-A
  1620.       'B' | char_super_c | char_hyper_c, #  85 = Shift-F2 -> #\S-F2 = #\SUPER-HYPER-B
  1621.       'C' | char_super_c | char_hyper_c, #  86 = Shift-F3 -> #\S-F3 = #\SUPER-HYPER-C
  1622.       'D' | char_super_c | char_hyper_c, #  87 = Shift-F4 -> #\S-F4 = #\SUPER-HYPER-D
  1623.       'E' | char_super_c | char_hyper_c, #  88 = Shift-F5 -> #\S-F5 = #\SUPER-HYPER-E
  1624.       'F' | char_super_c | char_hyper_c, #  89 = Shift-F6 -> #\S-F6 = #\SUPER-HYPER-F
  1625.       'G' | char_super_c | char_hyper_c, #  90 = Shift-F7 -> #\S-F7 = #\SUPER-HYPER-G
  1626.       'H' | char_super_c | char_hyper_c, #  91 = Shift-F8 -> #\S-F8 = #\SUPER-HYPER-H
  1627.       'I' | char_super_c | char_hyper_c, #  92 = Shift-F9 -> #\S-F9 = #\SUPER-HYPER-I
  1628.       'J' | char_super_c | char_hyper_c, #  93 = Shift-F10 -> #\S-F10 = #\SUPER-HYPER-J
  1629.       'A' | char_control_c | char_hyper_c, #  94 = Control-F1 -> #\C-F1 = #\CONTROL-HYPER-A
  1630.       'B' | char_control_c | char_hyper_c, #  95 = Control-F2 -> #\C-F2 = #\CONTROL-HYPER-B
  1631.       'C' | char_control_c | char_hyper_c, #  96 = Control-F3 -> #\C-F3 = #\CONTROL-HYPER-C
  1632.       'D' | char_control_c | char_hyper_c, #  97 = Control-F4 -> #\C-F4 = #\CONTROL-HYPER-D
  1633.       'E' | char_control_c | char_hyper_c, #  98 = Control-F5 -> #\C-F5 = #\CONTROL-HYPER-E
  1634.       'F' | char_control_c | char_hyper_c, #  99 = Control-F6 -> #\C-F6 = #\CONTROL-HYPER-F
  1635.       'G' | char_control_c | char_hyper_c, #  100 = Control-F7 -> #\C-F7 = #\CONTROL-HYPER-G
  1636.       'H' | char_control_c | char_hyper_c, #  101 = Control-F8 -> #\C-F8 = #\CONTROL-HYPER-H
  1637.       'I' | char_control_c | char_hyper_c, #  102 = Control-F9 -> #\C-F9 = #\CONTROL-HYPER-I
  1638.       'J' | char_control_c | char_hyper_c, #  103 = Control-F10 -> #\C-F10 = #\CONTROL-HYPER-J
  1639.       'A' | char_meta_c | char_hyper_c, #  104 = Alt-F1 -> #\M-F1 = #\META-HYPER-A
  1640.       'B' | char_meta_c | char_hyper_c, #  105 = Alt-F2 -> #\M-F2 = #\META-HYPER-B
  1641.       'C' | char_meta_c | char_hyper_c, #  106 = Alt-F3 -> #\M-F3 = #\META-HYPER-C
  1642.       'D' | char_meta_c | char_hyper_c, #  107 = Alt-F4 -> #\M-F4 = #\META-HYPER-D
  1643.       'E' | char_meta_c | char_hyper_c, #  108 = Alt-F5 -> #\M-F5 = #\META-HYPER-E
  1644.       'F' | char_meta_c | char_hyper_c, #  109 = Alt-F6 -> #\M-F6 = #\META-HYPER-F
  1645.       'G' | char_meta_c | char_hyper_c, #  110 = Alt-F7 -> #\M-F7 = #\META-HYPER-G
  1646.       'H' | char_meta_c | char_hyper_c, #  111 = Alt-F8 -> #\M-F8 = #\META-HYPER-H
  1647.       'I' | char_meta_c | char_hyper_c, #  112 = Alt-F9 -> #\M-F9 = #\META-HYPER-I
  1648.       'J' | char_meta_c | char_hyper_c, #  113 = Alt-F10 -> #\M-F10 = #\META-HYPER-J
  1649.        29 | char_control_c | char_hyper_c, # 114 = Control-PrtScr -> #\CONTROL-HYPER-Code29
  1650.        20 | char_control_c | char_hyper_c, # 115 = Control-Left -> #\C-Left = #\CONTROL-HYPER-Code20
  1651.        22 | char_control_c | char_hyper_c, # 116 = Control-Right -> #\C-Right = #\CONTROL-HYPER-Code22
  1652.        17 | char_control_c | char_hyper_c, # 117 = Control-End -> #\C-End = #\CONTROL-HYPER-Code17
  1653.        19 | char_control_c | char_hyper_c, # 118 = Control-PgDn -> #\C-PgDn = #\CONTROL-HYPER-Code19
  1654.        23 | char_control_c | char_hyper_c, # 119 = Control-Home -> #\C-Home = #\CONTROL-HYPER-Code23
  1655.       '1' | char_meta_c, #  120 = Alt-1 -> #\META-1
  1656.       '2' | char_meta_c, #  121 = Alt-2 -> #\META-2
  1657.       '3' | char_meta_c, #  122 = Alt-3 -> #\META-3
  1658.       '4' | char_meta_c, #  123 = Alt-4 -> #\META-4
  1659.       '5' | char_meta_c, #  124 = Alt-5 -> #\META-5
  1660.       '6' | char_meta_c, #  125 = Alt-6 -> #\META-6
  1661.       '7' | char_meta_c, #  126 = Alt-7 -> #\META-7
  1662.       '8' | char_meta_c, #  127 = Alt-8 -> #\META-8
  1663.       '9' | char_meta_c, #  128 = Alt-9 -> #\META-9
  1664.       '0' | char_meta_c, #  129 = Alt-0 -> #\META-0
  1665.       '-' | char_meta_c, #  130 = Alt-- -> #\META-- # nicht international portabel
  1666.       '=' | char_meta_c, #  131 = Alt-= -> #\META-= # nicht international portabel
  1667.        25 | char_control_c | char_hyper_c, # 132 = Control-PgUp -> #\C-PgUp = #\CONTROL-HYPER-Code25
  1668.       'K' | char_hyper_c, #  133 = F11 -> #\F11 = #\HYPER-K
  1669.       'L' | char_hyper_c, #  134 = F12 -> #\F12 = #\HYPER-L
  1670.       'K' | char_super_c | char_hyper_c, #  135 = Shift-F11 -> #\S-F11 = #\SUPER-HYPER-K
  1671.       'L' | char_super_c | char_hyper_c, #  136 = Shift-F12 -> #\S-F12 = #\SUPER-HYPER-L
  1672.       'K' | char_control_c | char_hyper_c, #  137 = Control-F11 -> #\C-F11 = #\CONTROL-HYPER-K
  1673.       'L' | char_control_c | char_hyper_c, #  138 = Control-F12 -> #\C-F12 = #\CONTROL-HYPER-L
  1674.       'K' | char_meta_c | char_hyper_c, #  139 = Alt-F1 -> #\M-F11 = #\META-HYPER-K
  1675.       'L' | char_meta_c | char_hyper_c, #  140 = Alt-F2 -> #\M-F12 = #\META-HYPER-L
  1676.        24 | char_control_c | char_hyper_c, # 141 = Control-Up -> #\C-Up = #\CONTROL-HYPER-Code24
  1677.       '-' | char_control_c | char_hyper_c, # 142 = Control-- -> #\CONTROL-HYPER--
  1678.        21 | char_control_c | char_hyper_c, # 143 = Control-Keypad5 -> #\CONTROL-HYPER-Code21
  1679.       '+' | char_control_c | char_hyper_c, # 142 = Control-+ -> #\CONTROL-HYPER-+
  1680.        18 | char_control_c | char_hyper_c, # 145 = Control-Down -> #\C-Down = #\CONTROL-HYPER-Code18
  1681.        16 | char_control_c | char_hyper_c, # 146 = Control-Insert -> #\C-Insert = #\CONTROL-HYPER-Code16
  1682.       127 | char_control_c | char_hyper_c, # 147 = Control-Delete -> #\CONTROL-HYPER-Delete
  1683.         9 | char_control_c, # 148 = Control-Tab -> #\CONTROL-Tab
  1684.       '/' | char_control_c | char_hyper_c, # 149 = Control-/ -> #\CONTROL-HYPER-'/'
  1685.       '*' | char_control_c | char_hyper_c, # 150 = Control-* -> #\CONTROL-HYPER-'*'
  1686.        23 | char_meta_c | char_hyper_c, # 151 = Alt-Home -> #\M-Home = #\META-HYPER-Code23
  1687.        24 | char_meta_c | char_hyper_c, # 152 = Alt-Up -> #\M-Up = #\META-HYPER-Code24
  1688.        25 | char_meta_c | char_hyper_c, # 153 = Alt-PgUp -> #\M-PgUp = #\META-HYPER-Code25
  1689.       0,
  1690.        20 | char_meta_c | char_hyper_c, # 155 = Alt-Left -> #\M-Left = #\META-HYPER-Code20
  1691.        21 | char_meta_c | char_hyper_c, # [156 -> #\META-HYPER-Code21]
  1692.        22 | char_meta_c | char_hyper_c, # 157 = Alt-Right -> #\M-Right = #\META-HYPER-Code22
  1693.       0,
  1694.        17 | char_meta_c | char_hyper_c, # 159 = Alt-End -> #\M-End = #\META-HYPER-Code17
  1695.        18 | char_meta_c | char_hyper_c, # 160 = Alt-Down -> #\M-Down = #\META-HYPER-Code18
  1696.        19 | char_meta_c | char_hyper_c, # 161 = Alt-PgDn -> #\M-PgDn = #\META-HYPER-Code19
  1697.        16 | char_meta_c | char_hyper_c, # 162 = Alt-Insert -> #\M-Insert = #\META-HYPER-Code16
  1698.       127 | char_meta_c | char_hyper_c, # 163 = Alt-Delete -> #\META-HYPER-Delete
  1699.       '/' | char_meta_c | char_hyper_c, # 164 = Alt-/ -> #\META-HYPER-'/'
  1700.         9 | char_meta_c, # 165 = Alt-Tab -> #\META-Tab
  1701.        CR | char_meta_c | char_hyper_c, # 166 = Alt-Enter -> #\META-HYPER-Return
  1702.     };
  1703.  
  1704. #ifdef EMUNIX_PORTABEL
  1705.  
  1706. # Wir haben, um portabel zu bleiben, nur die Funktion _read_kbd zur Verfⁿgung.
  1707. # Diese erkennt unter DOS aber nur recht wenige Sondertasten: nur die mit
  1708. # Scan-Codes 3, 7, 15-25, 30-38, 44-50, 59-131 (ungefΣhr).
  1709. # Insbesondere fehlen F11, F12, Ctrl-Up, Ctrl-Down, und man kann
  1710. # Enter von Return, Tab von Ctrl-I, Backspace von Ctrl-H nicht unterscheiden.
  1711. # Trotzdem!
  1712. #ifdef EMUNIX_NEW_8f
  1713. # Da INT 16,10 unter DOS nun endlich befriedigend funktioniert, verwenden wir
  1714. # dieses. Zur Laufzeit wird _osmode abgefragt.
  1715. #endif
  1716.  
  1717. #endif # EMUNIX_PORTABEL
  1718.  
  1719. #endif # !WINDOWS
  1720.  
  1721. #endif # MSDOS
  1722.  
  1723. # Stellt fest, ob der Keyboard-Stream ein Zeichen verfⁿgbar hat.
  1724. # listen_keyboard(stream)
  1725. # > stream: Stream
  1726. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  1727. #             -1 falls bei EOF angelangt,
  1728. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  1729.   local signean listen_keyboard (object stream);
  1730.   #if defined(ATARI) || defined(MSDOS)
  1731.   local signean listen_keyboard(stream)
  1732.     var reg1 object stream;
  1733.     {
  1734.       #ifdef ATARI
  1735.         if (GEMDOS_ConStat()==0) # inzwischen wieder Tasten gedrⁿckt?
  1736.           { return signean_plus; } # nein
  1737.           else
  1738.           { return signean_null; } # ja
  1739.       #endif
  1740.       #ifdef MSDOS
  1741.         #ifdef EMUNIX_PORTABEL
  1742.         if (!(_osmode == DOS_MODE))
  1743.           # OS/2
  1744.           { var reg2 int ch = _read_kbd(FALSE,FALSE,FALSE);
  1745.             if (ch < 0) { return signean_plus; } # nein
  1746.            {var reg2 cint c =
  1747.               (ch==0 ? scancode_table[(uintB)_read_kbd(FALSE,TRUE,FALSE)]
  1748.                      : (ch <= 26) && !(ch == BS) && !(ch == CR) && !(ch == TAB)
  1749.                        ? # Ctrl-A bis Ctrl-Z -> Buchstabe mit CONTROL-Bit draus machen:
  1750.                          ((cint)(ch==LF ? CR : (ch | bit(6))) << char_code_shift_c) | char_control_c
  1751.                        : (cint)(uintB)ch << char_code_shift_c
  1752.               );
  1753.             /* asciz_out("{"); hex_out(ch); asciz_out("}"); _sleep2(500); */ # Test
  1754.             TheStream(stream)->strm_rd_ch_last = char_to_fixnum(int_char(c));
  1755.             return signean_null;
  1756.           }}
  1757.           else
  1758.         #endif
  1759.         # DOS
  1760.         if (kbhit()) # inzwischen wieder Tasten gedrⁿckt?
  1761.           { return signean_null; } # ja
  1762.           else
  1763.           { return signean_plus; } # nein
  1764.       #endif
  1765.     }
  1766.   #endif
  1767.   #if defined(UNIX) || defined(RISCOS)
  1768.     #define listen_keyboard  listen_handle
  1769.   #endif
  1770.  
  1771. # UP: L÷scht bereits eingegebenen interaktiven Input vom Keyboard-Stream.
  1772. # clear_input_keyboard(stream);
  1773. # > stream: Stream
  1774. # < ergebnis: TRUE falls Input gel÷scht wurde, FALSE sonst
  1775.   local boolean clear_input_keyboard (object stream);
  1776.   local boolean clear_input_keyboard(stream)
  1777.     var reg1 object stream;
  1778.     {
  1779.       #ifdef ATARI
  1780.         # GEMDOS-Tastaturbuffer leeren:
  1781.         loop
  1782.           { if (GEMDOS_ConStat()==0) break; # Buffer leer -> fertig
  1783.             GEMDOS_DirConIn(); # sonst: Zeichen verbrauchen
  1784.           }
  1785.       #endif
  1786.       #ifdef MSDOS
  1787.         #ifdef EMUNIX_PORTABEL
  1788.         if (!(_osmode == DOS_MODE))
  1789.           # OS/2
  1790.            { while (listen_keyboard(stream)) { /* das Zeichen wurde schon geholt! */ } }
  1791.           else
  1792.         #endif
  1793.         # DOS
  1794.         while (kbhit()) { getch(); }
  1795.       #endif
  1796.       #if defined(UNIX) || defined(RISCOS)
  1797.         if (nullp(TheStream(stream)->strm_keyboard_isatty))
  1798.           # File -> nichts tun
  1799.           { return FALSE; }
  1800.         # Terminal
  1801.         TheStream(stream)->strm_rd_ch_last = NIL; # gewesenes EOF vergessen
  1802.         clear_tty_input(stdin_handle);
  1803.         pushSTACK(stream);
  1804.         while (listen_keyboard(STACK_0) == 0) { read_char(&STACK_0); }
  1805.         skipSTACK(1);
  1806.       #endif
  1807.       return TRUE;
  1808.     }
  1809.  
  1810. # Lesen eines Zeichens vom Keyboard:
  1811.   local object rd_ch_keyboard (object* stream_);
  1812.   #ifdef ATARI
  1813.   #
  1814.   # Um einerseits bei normalen Tasten feststellen zu k÷nnen, ob sie mit
  1815.   # und ohne Shift dasselbe ergeben hΣtte, und um andererseits von nationalen
  1816.   # SpezialitΣten wie 'Σ', '÷', 'ⁿ', Shift-ⁿ = @ usw. unabhΣngig zu sein,
  1817.   # lassen wir uns vom BIOS den Ascii-Code geben, entscheiden aber hinterher
  1818.   # aufgrund der BIOS-Tastaturtabellen, ob wir ihn verwenden wollen oder nicht.
  1819.   #
  1820.   # Zeiger auf die drei Tastaturtabellen des BIOS (LΣnge 128, Index: Scancode,
  1821.   # Wert: Ascii-Code), werden beim Programmstart initialisiert:
  1822.     local KEYTAB keytables;
  1823.   #
  1824.   local object rd_ch_keyboard(stream_)
  1825.     var reg7 object* stream_;
  1826.     { var reg6 object ch;
  1827.       run_time_stop(); # Run-Time-Stoppuhr anhalten
  1828.       restart_it:
  1829.      {# Tastendruck abwarten, nichts ausgeben:
  1830.       var reg5 uintL erg = GEMDOS_DirConIn();
  1831.       # Wegen der Initialisierung in SPVW.Q ist
  1832.       # in (erg>>24) der Sondertasten-Status enthalten.
  1833.       { var reg4 uintB code = (uintB)erg; # Ascii-Code
  1834.         var reg3 uintB scancode = (uintB)(erg>>16); # Scan-Code
  1835.         var reg2 uintB kbshift = (uintB)(erg>>24); # Sondertasten-Status
  1836.         var reg1 cint c = 0; # neues Character
  1837.         # Eine kleine Merkwⁿrdigkeit beheben:
  1838.         #   Beim Drⁿcken von [Shift-]Ctrl-Alt-Insert bzw. -ClrHome liefert
  1839.         #   das BIOS zwei TastenanschlΣge (und doppelten Tastaturclick):
  1840.         #   Ctrl-Alt-Insert:  0x0C520000, 0x0CD20000,
  1841.         #   Ctrl-Alt-ClrHome: 0x0C770000, 0x0CC70000,
  1842.         #   und zwischen diesen beiden AnschlΣgen wird die linke bzw.
  1843.         #   rechte Maustaste als gedrⁿckt betrachtet (z.B. BIOS_KBSHIFT,
  1844.         #   liefert gesetztes Bit 6 bzw. Bit 5).
  1845.         #   0xD2 und 0xC7 sind natⁿrlich irrsinnige Scan-Codes. Sie ent-
  1846.         #   stehen offenbar als 82+128 bzw. 71+128.
  1847.         # Wir behandeln das Problem so, da▀ wir den zweiten Tastenanschlag
  1848.         # dann ignorieren, wenn er kommt.
  1849.         if (scancode >= 59+128) # Scancode einer Sondertaste, um 128 erh÷ht?
  1850.           goto restart_it; # ja -> diesen Tastenanschlag ⁿbergehen
  1851.           # (Eigentlich mⁿ▀te deswegen auch listen_keyboard geΣndert werden.)
  1852.        {var reg8 uintB* keytables_normal = keytables.unshift;
  1853.         # Scan-Code aufbereiten und auf Sondertaste entscheiden:
  1854.         if ((scancode >= 120) && (scancode <= 131))
  1855.           { scancode = scancode-118; } # 120->2,121->3,... 'Alt i' -> 'i'
  1856.         if ((scancode >= 59) && !(scancode == 96)) # Scancode <59 oder =96 -> normale Taste
  1857.           # Sondertaste
  1858.           { if ((scancode < 96) && (scancode >= 84))
  1859.               { scancode = scancode-25; } # 84->59,85->60,... 'Shift Fi' -> 'Fi'
  1860.             switch (scancode)
  1861.               { case 115: scancode = 75; break; # 115->75 'Ctrl-' -> ''
  1862.                 case 116: scancode = 77; break; # 116->77 'Ctrl-' -> ''
  1863.                 case 117: scancode = 79; break; # 117->79 'Ctrl-End' -> 'End'
  1864.                 case 118: scancode = 81; break; # 118->81 'Ctrl-PgDn' -> 'PgDn'
  1865.                 case 119: scancode = 71; break; # 119->71 'Ctrl-Home' -> 'Home'
  1866.                 case 132: scancode = 73; break; # 132->73 'Ctrl-PgUp' -> 'PgUp'
  1867.                 default: ;
  1868.               }
  1869.             c |= char_hyper_c; # HYPER-Bit setzen
  1870.           }
  1871.         # Ascii-Code ⁿbernehmen bzw. neu berechnen:
  1872.         if (kbshift & (CapsLockKey_mask | CtrlKey_mask | AltKey_mask))
  1873.           # Caps-Lock eingeschaltet oder Control- oder Alternate-Taste gedrⁿckt
  1874.           { keytables_normal = keytables.capslock; }
  1875.         if ((code >= 32) && ((scancode < 71) || (scancode >= 84)))
  1876.           # Ascii-Code ⁿbernehmen, evtl. Shift- oder Alt-Bit killen:
  1877.           { if (kbshift & BothShiftKey_mask)
  1878.               # Shift gedrⁿckt
  1879.               { if (keytables.shift[scancode] == code)
  1880.                   # Ascii-Code kam aus der Tabelle 'shift'
  1881.                   { if (keytables_normal[scancode] == code)
  1882.                       # Ascii-Code kam auch aus der Tabelle 'unshift'/'capslock'
  1883.                       { c |= char_super_c; } # 'Shift' war unn÷tig -> SUPER-Bit setzen
  1884.                       else
  1885.                       {} # 'Shift' war n÷tig, 'Shift' killen
  1886.                   }
  1887.                   else
  1888.                   # Ascii-Code kam wohl durch 'Alt' zustande, 'Alt' und 'Shift' killen
  1889.                   { kbshift &= ~AltKey_mask; }
  1890.               }
  1891.               else
  1892.               # Shift nicht gedrⁿckt
  1893.               { if (keytables_normal[scancode] == code)
  1894.                   {} # Ascii-Code kam aus der Tabelle 'unshift'/'capslock'
  1895.                   else
  1896.                   # Ascii-Code kam wohl durch 'Alt' zustande, 'Alt' killen
  1897.                   { kbshift &= ~AltKey_mask; }
  1898.               }
  1899.           }
  1900.           else
  1901.           # Ascii-Code <32 (oder >=32, aber vom Cursorblock),
  1902.           # das erkennen wir nicht an.
  1903.           { if (kbshift & BothShiftKey_mask)
  1904.               # Shift gedrⁿckt
  1905.               { # Ascii-Code nicht (immer) aus der Tabelle 'shift' nehmen, denn
  1906.                 # die enthΣlt Ziffern 024678 bei Tasten aus dem Cursorblock!
  1907.                 code = keytables.capslock[scancode]; # Ascii-Code aus der Tabelle 'capslock'
  1908.                 c |= char_super_c; # SUPER-Bit setzen
  1909.               }
  1910.               else
  1911.               # Shift nicht gedrⁿckt
  1912.               { code = keytables_normal[scancode]; } # Ascii-Code aus der Tabelle 'unshift'/'capslock'
  1913.           }
  1914.         if (code==0)
  1915.           # setze den Scan-Code in eine Sondertaste um:
  1916.           { if ((scancode >= 59) && (scancode < 99))
  1917.               { # Tabelle der Characters, die den Scan-Codes 59..98 entsprechen:
  1918.                 local uintB table [99-59] =
  1919.                   {
  1920.                     'A', #  59 = F1 -> #\F1 = #\HYPER-A
  1921.                     'B', #  60 = F2 -> #\F2 = #\HYPER-B
  1922.                     'C', #  61 = F3 -> #\F3 = #\HYPER-C
  1923.                     'D', #  62 = F4 -> #\F4 = #\HYPER-D
  1924.                     'E', #  63 = F5 -> #\F5 = #\HYPER-E
  1925.                     'F', #  64 = F6 -> #\F6 = #\HYPER-F
  1926.                     'G', #  65 = F7 -> #\F7 = #\HYPER-G
  1927.                     'H', #  66 = F8 -> #\F8 = #\HYPER-H
  1928.                     'I', #  67 = F9 -> #\F9 = #\HYPER-I
  1929.                     'J', #  68 = F10 -> #\F10 = #\HYPER-J
  1930.                     'K', # [69 = F11 -> #\F11 = #\HYPER-K]
  1931.                     'L', # [70 = F12 -> #\F12 = #\HYPER-L]
  1932.                      23, #  71 = ClrHome -> #\Home = #\HYPER-Code23
  1933.                      24, #  72 =  -> #\Up = #\HYPER-Code24
  1934.                      25, # [73 = PgUp -> #\PgUp = #\HYPER-Code25]
  1935.                     '-', # [74 = - -> #\HYPER--]
  1936.                      20, #  75 =  -> #\Left = #\HYPER-Code20
  1937.                       0,
  1938.                      22, #  77 =  -> #\Right = #\HYPER-Code22
  1939.                     '+', # [78 = + -> #\HYPER-+]
  1940.                      17, # [79 = End -> #\End = #\HYPER-Code17]
  1941.                      18, #  80 =  -> #\Down = #\HYPER-Code18
  1942.                      19, # [81 = PgDn -> #\PgDn = #\HYPER-Code19]
  1943.                      16, #  82 = Insert -> #\Insert = #\HYPER-Code16
  1944.                     127, # [83 = Delete -> #\Delete = #\HYPER-Code127]
  1945.                     'A', # [84 = Shift F1 -> #\SUPER-F1 = #\SUPER-HYPER-A]
  1946.                     'B', # [85 = Shift F2 -> #\SUPER-F2 = #\SUPER-HYPER-B]
  1947.                     'C', # [86 = Shift F3 -> #\SUPER-F3 = #\SUPER-HYPER-C]
  1948.                     'D', # [87 = Shift F4 -> #\SUPER-F4 = #\SUPER-HYPER-D]
  1949.                     'E', # [88 = Shift F5 -> #\SUPER-F5 = #\SUPER-HYPER-E]
  1950.                     'F', # [89 = Shift F6 -> #\SUPER-F6 = #\SUPER-HYPER-F]
  1951.                     'G', # [90 = Shift F7 -> #\SUPER-F7 = #\SUPER-HYPER-G]
  1952.                     'H', # [91 = Shift F8 -> #\SUPER-F8 = #\SUPER-HYPER-H]
  1953.                     'I', # [92 = Shift F9 -> #\SUPER-F9 = #\SUPER-HYPER-I]
  1954.                     'J', # [93 = Shift F10 -> #\SUPER-F10 = #\SUPER-HYPER-J]
  1955.                     'K', # [94 = Shift F11 -> #\SUPER-F11 = #\SUPER-HYPER-K]
  1956.                     'L', # [95 = Shift F12 -> #\SUPER-F12 = #\SUPER-HYPER-L]
  1957.                       0,
  1958.                      29, #  97 = Undo -> #\Undo = #\HYPER-Code29
  1959.                      28, #  98 = Help -> #\Help = #\HYPER-Code28
  1960.                   };
  1961.                 code = table[scancode-59];
  1962.           }   }
  1963.         # Ascii-Code fertig
  1964.         if (kbshift & CtrlKey_mask) # Control-Taste gedrⁿckt?
  1965.           { c |= char_control_c; } # ja -> CONTROL-Bit setzen
  1966.         if (kbshift & AltKey_mask) # Alternate-Taste gedrⁿckt?
  1967.           { c |= char_meta_c; } # ja -> META-Bit setzen
  1968.         c |= ((cint)code << char_code_shift_c);
  1969.         ch = int_char(c);
  1970.      }}}
  1971.       run_time_restart(); # Run-Time-Stoppuhr weiterlaufen lassen
  1972.       return ch;
  1973.     }
  1974.   #endif
  1975.  
  1976.   #ifdef MSDOS
  1977.   local object rd_ch_keyboard(stream_)
  1978.     var reg7 object* stream_;
  1979.     {
  1980.       #ifdef EMUNIX_PORTABEL
  1981.       if (!(_osmode == DOS_MODE))
  1982.         # OS/2
  1983.         { run_time_stop(); # Run-Time-Stoppuhr anhalten
  1984.          {var reg1 int ch = _read_kbd(FALSE,TRUE,FALSE);
  1985.           var reg2 cint c =
  1986.             (ch==0 ? scancode_table[(uintB)_read_kbd(FALSE,TRUE,FALSE)]
  1987.                    : (ch <= 26) && !(ch == BS) && !(ch == CR) && !(ch == TAB)
  1988.                      ? # Ctrl-A bis Ctrl-Z -> Buchstabe mit CONTROL-Bit draus machen:
  1989.                        ((cint)(ch==LF ? CR : (ch | bit(6))) << char_code_shift_c) | char_control_c
  1990.                      : (cint)(uintB)ch << char_code_shift_c
  1991.             );
  1992.           # noch zu behandeln: ??
  1993.           # Ctrl-2 -> #\Control-2, Ctrl-6 -> #\Code30, Ctrl-▀ -> #\Code28,
  1994.           # Ctrl-+ -> #\Code29, Ctrl-ⁿ -> #\Code27 = #\Escape
  1995.           /* asciz_out("{"); hex_out(ch); asciz_out("}"); _sleep2(500); */ # Test
  1996.           run_time_restart(); # Run-Time-Stoppuhr weiterlaufen lassen
  1997.           return int_char(c);
  1998.         }}
  1999.         else
  2000.       #endif
  2001.       # DOS
  2002.      {var reg6 object ch;
  2003.       run_time_stop(); # Run-Time-Stoppuhr anhalten
  2004.       { # Tastendruck abwarten, nichts ausgeben:
  2005.         var reg5 uintW erg = getch();
  2006.         #ifdef WINDOWS
  2007.         var reg1 cint c = erg;
  2008.         #else
  2009.         var reg4 uintB code = (uintB)erg; # Ascii-Code
  2010.         var reg3 uintB scancode = (uintB)(erg>>8); # Scan-Code
  2011.         var reg1 cint c = 0; # neues Character
  2012.         if (scancode == 0)
  2013.           # Multikey-Event, z.B. accent+space oder Alt xyz
  2014.           { c = (cint)code << char_code_shift_c; }
  2015.         else
  2016.           { if ((code == 0) || (code == 0xE0))
  2017.               # Sondertaste
  2018.               { c = (scancode < 167 ? scancode_table[scancode] : 0); }
  2019.               else
  2020.               { if (((scancode >= 71) && (scancode < 84)) || (scancode == 55)
  2021.                     || ((scancode == 0xE0) && (code >= 32))
  2022.                    )
  2023.                   # Ziffernblocktaste au▀er Enter (auch nicht F1 bis F12 !)
  2024.                   { c = ((cint)code << char_code_shift_c) | char_hyper_c; }
  2025.                 elif ((scancode == 14) || (scancode == 28)
  2026.                       || ((scancode == 0xE0) && (code < 32))
  2027.                      )
  2028.                   # Backspace-Taste, Return-Taste, Enter-Taste
  2029.                   { var reg5 uintB defaultcode = (scancode==14 ? BS : CR);
  2030.                     c = (cint)defaultcode << char_code_shift_c;
  2031.                     if (scancode == 0xE0) { c |= char_hyper_c; }
  2032.                     if (!(code == defaultcode)) { c |= char_control_c; }
  2033.                   }
  2034.                 else
  2035.                   { if ((code < 32) && ((scancode >= 16) && (scancode <= 53)))
  2036.                       # Ctrl-A bis Ctrl-Z -> Buchstabe mit CONTROL-Bit draus machen:
  2037.                       { c = ((cint)(code | bit(6)) << char_code_shift_c) | char_control_c; }
  2038.                     else
  2039.                       # normales Zeichen
  2040.                       { c = (cint)code << char_code_shift_c; }
  2041.           }   }   }
  2042.         # noch zu behandeln: ??
  2043.         # Ctrl-2          0300
  2044.         # Ctrl-6          071E
  2045.         # Ctrl-▀          0C1C
  2046.         # Ctrl--          0C1F
  2047.         #endif
  2048.         ch = int_char(c);
  2049.       }
  2050.       run_time_restart(); # Run-Time-Stoppuhr weiterlaufen lassen
  2051.       return ch;
  2052.     }}
  2053.   #endif
  2054.  
  2055.   #if defined(UNIX) || defined(RISCOS)
  2056.  
  2057.   # vgl. rd_ch_handle() :
  2058.   local object rd_ch_keyboard(stream_)
  2059.     var reg3 object* stream_;
  2060.     { restart_it:
  2061.      {var reg2 object stream = *stream_;
  2062.       if (eq(TheStream(stream)->strm_rd_ch_last,eof_value)) # schon EOF?
  2063.         { return eof_value; }
  2064.       # Noch etwas im Buffer?
  2065.       if (mconsp(TheStream(stream)->strm_keyboard_buffer))
  2066.         goto empty_buffer;
  2067.       # Ein Zeichen lesen:
  2068.       { var uintB c;
  2069.         read_next_char:
  2070.         {run_time_stop(); # Run-Time-Stoppuhr anhalten
  2071.          begin_system_call();
  2072.          {var reg1 int ergebnis = read(stdin_handle,&c,1); # Zeichen lesen versuchen
  2073.           end_system_call();
  2074.           run_time_restart(); # Run-Time-Stoppuhr weiterlaufen lassen
  2075.           if (ergebnis<0)
  2076.             { if (errno==EINTR) # Unterbrechung (evtl. durch Ctrl-C) ?
  2077.                 { interruptp({ pushSTACK(S(read_char)); tast_break(); }); # Break-Schleife aufrufen
  2078.                   goto restart_it;
  2079.                 }
  2080.               OS_error();
  2081.             }
  2082.           if (ergebnis==0)
  2083.             # kein Zeichen verfⁿgbar -> EOF erkennen
  2084.             { TheStream(stream)->strm_rd_ch_last = eof_value; return eof_value; }
  2085.         }}
  2086.         next_char_is_read:
  2087.         # Es verlΣngert den Buffer:
  2088.         {var reg4 object new_cons = allocate_cons();
  2089.          Car(new_cons) = code_char(c);
  2090.          stream = *stream_;
  2091.          {var reg1 object* last_ = &TheStream(stream)->strm_keyboard_buffer;
  2092.           while (mconsp(*last_)) { last_ = &Cdr(*last_); }
  2093.           *last_ = new_cons;
  2094.         }}
  2095.         # Ist der Buffer eine vollstΣndige Zeichenfolge zu einer Taste,
  2096.         # so liefern wir diese Taste. Ist der Buffer ein echtes Anfangsstⁿck
  2097.         # einer Zeichenfolge zu einer Taste, so warten wir noch ein wenig.
  2098.         # Ansonsten fangen wir an, den Buffer Zeichen fⁿr Zeichen zu leeren.
  2099.         { var reg4 object keytab = TheStream(stream)->strm_keyboard_keytab;
  2100.           while (consp(keytab))
  2101.             { var reg1 object L1 = Car(keytab);
  2102.               keytab = Cdr(keytab);
  2103.              {var reg1 object L2 = TheStream(stream)->strm_keyboard_buffer;
  2104.               while (consp(L1) && consp(L2) && eq(Car(L1),Car(L2)))
  2105.                 { L1 = Cdr(L1); L2 = Cdr(L2); }
  2106.               if (atomp(L2))
  2107.                 { if (atomp(L1))
  2108.                     # vollstΣndige Zeichenfolge
  2109.                     { TheStream(stream)->strm_keyboard_buffer = NIL;
  2110.                       return L1;
  2111.         }   }}  }   }
  2112.         { var reg4 object keytab = TheStream(stream)->strm_keyboard_keytab;
  2113.           while (consp(keytab))
  2114.             { var reg1 object L1 = Car(keytab);
  2115.               keytab = Cdr(keytab);
  2116.              {var reg1 object L2 = TheStream(stream)->strm_keyboard_buffer;
  2117.               while (consp(L1) && consp(L2) && eq(Car(L1),Car(L2)))
  2118.                 { L1 = Cdr(L1); L2 = Cdr(L2); }
  2119.               if (atomp(L2))
  2120.                 # Da consp(L1), liegt ein Anfangsstⁿck einer Zeichenfolge vor.
  2121.                 goto wait_for_another;
  2122.         }   }}
  2123.         goto empty_buffer;
  2124.         wait_for_another:
  2125.         #ifdef HAVE_SELECT
  2126.         { # Verwende select mit readfds = einelementige Menge {stdin_handle}
  2127.           # und timeout = kleines Zeitintervall.
  2128.           var fd_set handle_menge; # Menge von Handles := {stdin_handle}
  2129.           var struct timeval small_time; # Zeitintervall := 0
  2130.           FD_ZERO(&handle_menge); FD_SET(stdin_handle,&handle_menge);
  2131.           restart_select:
  2132.           small_time.tv_sec = 0; small_time.tv_usec = 1000000/10; # 1/10 sec
  2133.           run_time_stop(); # Run-Time-Stoppuhr anhalten
  2134.           begin_system_call();
  2135.          {var reg1 int ergebnis;
  2136.           ergebnis = select(FD_SETSIZE,&handle_menge,NULL,NULL,&small_time);
  2137.           end_system_call();
  2138.           run_time_restart(); # Run-Time-Stoppuhr weiterlaufen lassen
  2139.           if (ergebnis<0)
  2140.             { if (errno==EINTR) goto restart_select;
  2141.               if (!(errno == EBADF)) { OS_error(); }
  2142.             }
  2143.             else
  2144.             { # ergebnis = Anzahl der Handles in handle_menge, bei denen read
  2145.               # sofort ein Ergebnis liefern wⁿrde.
  2146.               if (ergebnis==0)
  2147.                 goto empty_buffer; # kein Zeichen verfⁿgbar
  2148.               # ergebnis=1 -> Zeichen verfⁿgbar
  2149.         }}  }
  2150.         #else
  2151.         #if defined(UNIX_TERM_TERMIOS) || defined(UNIX_TERM_TERMIO)
  2152.         { # Verwende die Termio-Elemente VMIN und VTIME.
  2153.           #ifdef UNIX_TERM_TERMIOS
  2154.           var struct termios oldtermio;
  2155.           var struct termios newtermio;
  2156.           #else # UNIX_TERM_TERMIO
  2157.           var struct termio oldtermio;
  2158.           var struct termio newtermio;
  2159.           #endif
  2160.           run_time_stop(); # Run-Time-Stoppuhr anhalten
  2161.           begin_system_call();
  2162.           #ifdef UNIX_TERM_TERMIOS
  2163.           if (!( tcgetattr(stdin_handle,&oldtermio) ==0))
  2164.             { if (!(errno==ENOTTY)) { OS_error(); } }
  2165.           #else
  2166.           if (!( ioctl(stdin_handle,TCGETA,&oldtermio) ==0))
  2167.             { if (!(errno==ENOTTY)) { OS_error(); } }
  2168.           #endif
  2169.           # Wir gehen nun davon aus, da▀ oldtermio nun mit dem newtermio aus
  2170.           # term_raw() (s.u.) identisch ist. Das ist dann gewΣhrleistet, wenn
  2171.           # 1. (SYS::TERMINAL-RAW T) aufgerufen wurde und
  2172.           # 2. stdin_handle und stdout_handle beide dasselbe Terminal sind. ??
  2173.           newtermio = oldtermio;
  2174.           newtermio.c_cc[VMIN] = 0;
  2175.           newtermio.c_cc[VTIME] = 1; # 1/10 Sekunde Timeout
  2176.           #ifdef UNIX_TERM_TERMIOS
  2177.           if (!( TCSETATTR(stdin_handle,TCSANOW,&newtermio) ==0))
  2178.             { if (!(errno==ENOTTY)) { OS_error(); } }
  2179.           #else
  2180.           if (!( ioctl(stdin_handle,TCSETA,&newtermio) ==0))
  2181.             { if (!(errno==ENOTTY)) { OS_error(); } }
  2182.           #endif
  2183.          {var reg1 int ergebnis = read(stdin_handle,&c,1); # Zeichen lesen versuchen, mit Timeout
  2184.           #ifdef UNIX_TERM_TERMIOS
  2185.           if (!( TCSETATTR(stdin_handle,TCSANOW,&oldtermio) ==0))
  2186.             { if (!(errno==ENOTTY)) { OS_error(); } }
  2187.           #else
  2188.           if (!( ioctl(stdin_handle,TCSETA,&oldtermio) ==0))
  2189.             { if (!(errno==ENOTTY)) { OS_error(); } }
  2190.           #endif
  2191.           end_system_call();
  2192.           run_time_restart(); # Run-Time-Stoppuhr weiterlaufen lassen
  2193.           if (ergebnis<0)
  2194.             { if (errno==EINTR) # Unterbrechung (evtl. durch Ctrl-C) ?
  2195.                 { interruptp({ pushSTACK(S(read_char)); tast_break(); }); # Break-Schleife aufrufen
  2196.                   goto restart_it;
  2197.                 }
  2198.               OS_error();
  2199.             }
  2200.           if (ergebnis==0)
  2201.             goto empty_buffer; # kein Zeichen verfⁿgbar
  2202.           goto next_char_is_read; # ergebnis=1 -> Zeichen verfⁿgbar
  2203.         }}
  2204.         #else
  2205.         # Man k÷nnte hier fcntl(stdin_handle,F_SETFL,...|FASYNC) verwenden
  2206.         # und auf Signal SIGIO warten. Allerdings funktioniert das auf so
  2207.         # wenigen Systemen (siehe Emacs), da▀ es sich wohl nicht lohnt.
  2208.         #endif
  2209.         #endif
  2210.         goto read_next_char;
  2211.       }
  2212.       # Buffer Zeichen fⁿr Zeichen liefern:
  2213.       empty_buffer:
  2214.       { var reg1 object l = TheStream(stream)->strm_keyboard_buffer;
  2215.         TheStream(stream)->strm_keyboard_buffer = Cdr(l);
  2216.        {var reg1 uintB c = char_code(Car(l));
  2217.         if ((c >= ' ') || (c == ESC) || (c == TAB) || (c == CR) || (c == BS))
  2218.           { return code_char(c); }
  2219.           else
  2220.           # Taste vermutlich mit Ctrl getippt
  2221.           { return int_char(((64 | c) << char_code_shift_c) | char_control_c); }
  2222.       }}
  2223.     }}
  2224.  
  2225.   # UP: Erweitert die Liste STACK_0 um eine Tastenzuordnung.
  2226.   # kann GC ausl÷sen
  2227.     local void keybinding (char* cap, cint key);
  2228.     local void keybinding(cap,key)
  2229.       var reg4 char* cap;
  2230.       var reg3 cint key;
  2231.       { var reg1 uintB* ptr = (uintB*)cap;
  2232.         if (*ptr=='\0') return; # leere Tastenfolge vermeiden
  2233.         pushSTACK(allocate_cons());
  2234.         # Liste (char1 ... charn . key) bilden:
  2235.         {var reg2 uintC count = 0;
  2236.          do { pushSTACK(code_char(*ptr)); ptr++; count++; } until (*ptr=='\0');
  2237.          pushSTACK(int_char(key)); count++;
  2238.          funcall(L(liststern),count);
  2239.         }
  2240.         # und auf STACK_0 pushen:
  2241.         {var reg2 object l = popSTACK();
  2242.          Car(l) = value1; Cdr(l) = STACK_0; STACK_0 = l;
  2243.       } }
  2244.  
  2245.   #endif
  2246.  
  2247. # Liefert einen Keyboard-Stream.
  2248. # make_keyboard_stream()
  2249. # kann GC ausl÷sen
  2250.   local object make_keyboard_stream (void);
  2251.   local object make_keyboard_stream()
  2252.     {
  2253.      #if defined(UNIX) || defined(RISCOS)
  2254.       # Tabelle aller Zuordnungen Zeichenfolge -> Taste bilden:
  2255.       pushSTACK(NIL);
  2256.       # Terminal-Typ abfragen:
  2257.       begin_system_call();
  2258.      {var reg3 char* s = getenv("TERM");
  2259.       if (s==NULL)
  2260.         { end_system_call(); }
  2261.         else
  2262.         { var char tbuf[4096]; # interner Buffer fⁿr die Termcap-Routinen
  2263.           if (!(tgetent(tbuf,s)==1))
  2264.             { end_system_call(); }
  2265.             else
  2266.             { var char tentry[4096]; # Buffer fⁿr von mir ben÷tigte Capabilities und Pointer da hinein
  2267.               var char* tp = &tentry[0];
  2268.               var reg1 char* cap;
  2269.               end_system_call();
  2270.               # Backspace:
  2271.               begin_system_call(); cap = tgetstr("kb",&tp); end_system_call();
  2272.               if (cap) { keybinding(cap,BS); } # #\Backspace
  2273.               # Insert, Delete:
  2274.               begin_system_call(); cap = tgetstr("kI",&tp); end_system_call();
  2275.               if (cap) { keybinding(cap,16 | char_hyper_c); } # #\Insert
  2276.               #ifdef UNIX_COHERENT
  2277.               begin_system_call(); cap = tgetstr("Ku",&tp); end_system_call();
  2278.               if (cap) { keybinding(cap,16 | char_hyper_c); } # #\Insert
  2279.               #endif
  2280.               begin_system_call(); cap = tgetstr("kD",&tp); end_system_call();
  2281.               if (cap) { keybinding(cap,127); } # #\Delete
  2282.               #ifdef UNIX_COHERENT
  2283.               begin_system_call(); cap = tgetstr("Kd",&tp); end_system_call();
  2284.               if (cap) { keybinding(cap,127); } # #\Delete
  2285.               #endif
  2286.               # Pfeiltasten:
  2287.               begin_system_call(); cap = tgetstr("ku",&tp); end_system_call();
  2288.               if (cap) { keybinding(cap,24 | char_hyper_c); } # #\Up
  2289.               if (cap && (cap[0] == ESC) && (cap[1] == 'O') && (cap[2] == 'A') && (cap[3] == '\0'))
  2290.                 { keybinding(ESCstring"[A",24 | char_hyper_c); } # #\Up
  2291.               begin_system_call(); cap = tgetstr("kd",&tp); end_system_call();
  2292.               if (cap) { keybinding(cap,18 | char_hyper_c); } # #\Down
  2293.               if (cap && (cap[0] == ESC) && (cap[1] == 'O') && (cap[2] == 'B') && (cap[3] == '\0'))
  2294.                 { keybinding(ESCstring"[B",18 | char_hyper_c); } # #\Down
  2295.               begin_system_call(); cap = tgetstr("kr",&tp); end_system_call();
  2296.               if (cap) { keybinding(cap,22 | char_hyper_c); } # #\Right
  2297.               if (cap && (cap[0] == ESC) && (cap[1] == 'O') && (cap[2] == 'C') && (cap[3] == '\0'))
  2298.                 { keybinding(ESCstring"[C",22 | char_hyper_c); } # #\Right
  2299.               begin_system_call(); cap = tgetstr("kl",&tp); end_system_call();
  2300.               if (cap) { keybinding(cap,20 | char_hyper_c); } # #\Left
  2301.               if (cap && (cap[0] == ESC) && (cap[1] == 'O') && (cap[2] == 'D') && (cap[3] == '\0'))
  2302.                 { keybinding(ESCstring"[D",20 | char_hyper_c); } # #\Left
  2303.               # sonstige Cursorblock-Tasten:
  2304.               begin_system_call(); cap = tgetstr("kh",&tp); end_system_call();
  2305.               if (cap) { keybinding(cap,23 | char_hyper_c); } # #\Home
  2306.               begin_system_call(); cap = tgetstr("K1",&tp); end_system_call();
  2307.               if (cap) { keybinding(cap,23 | char_hyper_c); } # #\Home
  2308.               begin_system_call(); cap = tgetstr("KH",&tp); end_system_call();
  2309.               if (cap) { keybinding(cap,17 | char_hyper_c); } # #\End
  2310.               begin_system_call(); cap = tgetstr("K4",&tp); end_system_call();
  2311.               if (cap) { keybinding(cap,17 | char_hyper_c); } # #\End
  2312.               #ifdef UNIX_COHERENT
  2313.               begin_system_call(); cap = tgetstr("KE",&tp); end_system_call();
  2314.               if (cap) { keybinding(cap,17 | char_hyper_c); } # #\End
  2315.               #endif
  2316.               begin_system_call(); cap = tgetstr("kP",&tp); end_system_call();
  2317.               if (cap) { keybinding(cap,25 | char_hyper_c); } # #\PgUp
  2318.               begin_system_call(); cap = tgetstr("K3",&tp); end_system_call();
  2319.               if (cap) { keybinding(cap,25 | char_hyper_c); } # #\PgUp
  2320.               #ifdef UNIX_COHERENT
  2321.               begin_system_call(); cap = tgetstr("Kp",&tp); end_system_call();
  2322.               if (cap) { keybinding(cap,25 | char_hyper_c); } # #\PgUp
  2323.               #endif
  2324.               begin_system_call(); cap = tgetstr("kN",&tp); end_system_call();
  2325.               if (cap) { keybinding(cap,19 | char_hyper_c); } # #\PgDn
  2326.               begin_system_call(); cap = tgetstr("K5",&tp); end_system_call();
  2327.               if (cap) { keybinding(cap,19 | char_hyper_c); } # #\PgDn
  2328.               begin_system_call(); cap = tgetstr("K2",&tp); end_system_call();
  2329.               #ifdef UNIX_COHERENT
  2330.               begin_system_call(); cap = tgetstr("KP",&tp); end_system_call();
  2331.               if (cap) { keybinding(cap,19 | char_hyper_c); } # #\PgDn
  2332.               #endif
  2333.               if (cap) { keybinding(cap,21 | char_hyper_c); } # #\Center
  2334.               # Funktionstasten:
  2335.               { typedef struct { char* capname; cint key; } funkey;
  2336.                 local var funkey funkey_tab[] = {
  2337.                   { "k1", 'A' | char_hyper_c }, # #\F1
  2338.                   { "k2", 'B' | char_hyper_c }, # #\F2
  2339.                   { "k3", 'C' | char_hyper_c }, # #\F3
  2340.                   { "k4", 'D' | char_hyper_c }, # #\F4
  2341.                   { "k5", 'E' | char_hyper_c }, # #\F5
  2342.                   { "k6", 'F' | char_hyper_c }, # #\F6
  2343.                   { "k7", 'G' | char_hyper_c }, # #\F7
  2344.                   { "k8", 'H' | char_hyper_c }, # #\F8
  2345.                   { "k9", 'I' | char_hyper_c }, # #\F9
  2346.                   { "k0", 'J' | char_hyper_c }, # #\F10
  2347.                   { "k;", 'J' | char_hyper_c }, # #\F10
  2348.                   { "F1", 'K' | char_hyper_c }, # #\F11
  2349.                   { "F2", 'L' | char_hyper_c }, # #\F12
  2350.                   };
  2351.                 var reg2 uintL i;
  2352.                 for (i=0; i < sizeof(funkey_tab)/sizeof(funkey); i++)
  2353.                   { begin_system_call();
  2354.                     cap = tgetstr(funkey_tab[i].capname,&tp);
  2355.                     end_system_call();
  2356.                     if (cap) { keybinding(cap,funkey_tab[i].key); }
  2357.                     #ifdef UNIX_COHERENT
  2358.                     # Shift-F1 bis Shift-F10 funktionieren, fehlen aber in /etc/termcap
  2359.                     if ((i<10) # F1 bis F10
  2360.                         && cap
  2361.                         && (cap[0] == ESC) && (cap[1] == '[')
  2362.                         && (cap[2] == funkey_tab[i].capname[1])
  2363.                         && (cap[3] == 'x')
  2364.                         && (cap[4] == '\0')
  2365.                        )
  2366.                       { # neue Capability fⁿr Shift-Fi bilden und eintragen:
  2367.                         var reg4 char* newcap = tp;
  2368.                         *tp++ = ESC; *tp++ = '['; *tp++ = cap[2]; *tp++ = 'y'; *tp++ = '\0';
  2369.                         keybinding(newcap,char_super_c | funkey_tab[i].key);
  2370.                       }
  2371.                     #endif
  2372.               }   }
  2373.               # Spezielle xterm-Behandlung:
  2374.               begin_system_call();
  2375.               cap = tgetstr("ku",&tp);
  2376.               if (!(cap && (cap[0] == ESC) && (cap[1] == 'O') && (cap[2] == 'A') && (cap[3] == '\0')))
  2377.                 goto not_xterm;
  2378.               cap = tgetstr("kd",&tp);
  2379.               if (!(cap && (cap[0] == ESC) && (cap[1] == 'O') && (cap[2] == 'B') && (cap[3] == '\0')))
  2380.                 goto not_xterm;
  2381.               cap = tgetstr("kr",&tp);
  2382.               if (!(cap && (cap[0] == ESC) && (cap[1] == 'O') && (cap[2] == 'C') && (cap[3] == '\0')))
  2383.                 goto not_xterm;
  2384.               cap = tgetstr("kl",&tp);
  2385.               if (!(cap && (cap[0] == ESC) && (cap[1] == 'O') && (cap[2] == 'D') && (cap[3] == '\0')))
  2386.                 goto not_xterm;
  2387.               if (!tgetflag("km"))
  2388.                 goto not_xterm;
  2389.               end_system_call();
  2390.               { # Insert, Delete:
  2391.                 keybinding(ESCstring"[2~",16 | char_hyper_c); # #\Insert
  2392.                 keybinding(ESCstring"[3~",127); # #\Delete
  2393.               }
  2394.               { # Application Keypad: ESC O M -> Return,
  2395.                 # ESC O k -> +, ESC O m -> -, ESC O j -> *, ESC O o -> /
  2396.                 # (ohne Hyper-Bit, da das zu Terminal-abhΣngig wⁿrde)
  2397.                 var char cap[4];
  2398.                 cap[0] = ESC; cap[1] = 'O'; cap[3] = '\0';
  2399.                {var reg1 uintB c;
  2400.                 for (c='E'; c<='z'; c++) { cap[2] = c; keybinding(&!cap,c-64); }
  2401.               }}
  2402.               begin_system_call();
  2403.               if (!(tgetnum("kn")==4))
  2404.                 goto not_xterm;
  2405.               end_system_call();
  2406.               xterm:
  2407.               { # Pfeiltasten s.o.
  2408.                 # sonstige Cursorblock-Tasten:
  2409.                 keybinding(ESCstring"[5~",25 | char_hyper_c); # #\PgUp
  2410.                 keybinding(ESCstring"[6~",19 | char_hyper_c); # #\PgDn
  2411.                 # Funktionstasten:
  2412.                 keybinding(ESCstring"[11~",'A' | char_hyper_c); # #\F1
  2413.                 keybinding(ESCstring"[12~",'B' | char_hyper_c); # #\F2
  2414.                 keybinding(ESCstring"[13~",'C' | char_hyper_c); # #\F3
  2415.                 keybinding(ESCstring"[14~",'D' | char_hyper_c); # #\F4
  2416.                 keybinding(ESCstring"[15~",'E' | char_hyper_c); # #\F5
  2417.                 keybinding(ESCstring"[17~",'F' | char_hyper_c); # #\F6
  2418.                 keybinding(ESCstring"[18~",'G' | char_hyper_c); # #\F7
  2419.                 keybinding(ESCstring"[19~",'H' | char_hyper_c); # #\F8
  2420.                 keybinding(ESCstring"[20~",'I' | char_hyper_c); # #\F9
  2421.                 keybinding(ESCstring"[21~",'J' | char_hyper_c); # #\F10
  2422.                 keybinding(ESCstring"[23~",'K' | char_hyper_c); # #\F11
  2423.                 keybinding(ESCstring"[24~",'L' | char_hyper_c); # #\F12
  2424.               }
  2425.               not_xterm:
  2426.               end_system_call();
  2427.      }  }   }
  2428.      #endif
  2429.      {# neuen Stream allozieren:
  2430.       var reg2 object stream =
  2431.         allocate_stream(strmflags_rd_ch_B,strmtype_keyboard,strm_len+strm_keyboard_len);
  2432.         # Flags: nur READ-CHAR erlaubt
  2433.       # und fⁿllen:
  2434.       var reg1 Stream s = TheStream(stream);
  2435.         s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  2436.         s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  2437.         s->strm_rd_ch = P(rd_ch_keyboard); # READ-CHAR-Pseudofunktion
  2438.         s->strm_rd_ch_last = NIL; # Lastchar := NIL
  2439.         s->strm_wr_ch = P(wr_ch_dummy); # WRITE-CHAR unm÷glich
  2440.         s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  2441.         #ifdef STRM_WR_SS
  2442.         s->strm_wr_ss = P(wr_ss_dummy);
  2443.         #endif
  2444.         #if defined(UNIX) || defined(RISCOS)
  2445.         # Flag isatty = (stdin_tty ? T : NIL) bestimmen:
  2446.         begin_system_call();
  2447.         s->strm_keyboard_isatty = (isatty(stdin_handle) ? T : NIL);
  2448.         end_system_call();
  2449.         s->strm_keyboard_handle = allocate_handle(stdin_handle);
  2450.         s->strm_keyboard_buffer = NIL;
  2451.         s->strm_keyboard_keytab = popSTACK();
  2452.         #endif
  2453.       return stream;
  2454.     }}
  2455.  
  2456. #endif # KEYBOARD
  2457.  
  2458.  
  2459. # Interaktiver Terminalstream
  2460. # ===========================
  2461.  
  2462. #ifdef ATARI
  2463.  
  2464. # Funktionsweise:
  2465. # Bei Bedarf an Zeichen wird eine ganze Zeile eingelesen. Sie wird durch NL
  2466. # abgeschlossen. Die eingegebenen Zeichen bleiben am Bildschirm stehen.
  2467. # Das abschlie▀ende NL wird erst dann ausgegeben (als CR/LF), wenn n÷tig,
  2468. # d.h. bei der nΣchsten Bildschirmaktion (Lesen einer weiteren Eingabezeile
  2469. # oder Ausgeben eines Zeichens). Ist diese nΣchste Bildschirmaktion eine
  2470. # NL-Ausgabe, so wird NL nur einfach und nicht doppelt ausgegeben.
  2471. # Ausgabe von Control-Zeichen:  als Grafik-Zeichen,
  2472. # alle anderen als Steuerzeichen.
  2473. # Bug: Wird eine Zeile eingelesen, die mehr als einen ganzen Bildschirm
  2474. # umfa▀t, so kann der Bildschirm bei Backspace usw. durcheinanderkommen.
  2475.  
  2476. # ZusΣtzliche Komponenten:
  2477.   # INBUFF : Eingabebuffer, ein Simple-String
  2478.   #define strm_terminal_inbuff  strm_other[0]
  2479.   # COUNT  : Anzahl der Zeichen im Eingabebuffer
  2480.   #define strm_terminal_count   strm_other[1]
  2481.   # INDEX  : Anzahl der bereits verbrauchten Zeichen
  2482.   #define strm_terminal_index   strm_other[2]
  2483.   # NLflag : 1 falls NL wartet, 0 sonst
  2484.   #define strm_terminal_NLflag  strm_other[3]
  2485.  
  2486. # ZusΣtzliche Information: Gr÷▀e des Ausgabe-Fensters in x- bzw. y-Richtung
  2487.   local struct { uintC x; uintC y; } window_size;
  2488.   #define window_width  window_size.x
  2489.  
  2490. # Stellt fest, ob ein Terminal-Stream ein Zeichen verfⁿgbar hat.
  2491. # listen_terminal(stream)
  2492. # > stream: Terminal-Stream
  2493. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  2494. #             -1 falls bei EOF angelangt,
  2495. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  2496.   local signean listen_terminal (object stream);
  2497.   local signean listen_terminal(stream)
  2498.     var reg1 object stream;
  2499.     { # Buffer der gelesenen Zeichen leer ?
  2500.       if (TheStream(stream)->strm_terminal_index >= TheStream(stream)->strm_terminal_count)
  2501.         { return listen_keyboard(stream); } # ja -> Tastatur abfragen
  2502.         else
  2503.         { return signean_null; } # nein -> Es sind noch Zeichen im Buffer
  2504.     }
  2505.  
  2506. # UP: L÷scht bereits eingegebenen interaktiven Input von einem Terminal-Stream.
  2507. # clear_input_terminal(stream);
  2508. # > stream: Terminal-Stream
  2509. # < ergebnis: TRUE falls Input gel÷scht wurde, FALSE sonst
  2510.   local boolean clear_input_terminal (object stream);
  2511.   local boolean clear_input_terminal(stream)
  2512.     var reg1 object stream;
  2513.     { # Buffer der gelesenen Zeichen leeren durch INDEX := COUNT :
  2514.       TheStream(stream)->strm_terminal_index = TheStream(stream)->strm_terminal_count;
  2515.       # GEMDOS-Tastaturbuffer leeren:
  2516.       return clear_input_keyboard(stream);
  2517.     }
  2518.  
  2519. # UP: Newline auf einen Terminal-Stream ausgeben und Flag l÷schen.
  2520. # wr_NL_terminal(stream);
  2521. # > stream: Terminal-Stream
  2522.   local void wr_NL_terminal (object stream);
  2523.   local void wr_NL_terminal(stream)
  2524.     var reg1 object stream;
  2525.     { asciz_out(CRLFstring); # Newline als CR/LF ausgeben
  2526.       TheStream(stream)->strm_terminal_NLflag = Fixnum_0; # nun wartet kein NL mehr
  2527.     }
  2528.  
  2529. # UP: Ein Zeichen auf einen Terminal-Stream ausgeben.
  2530. # wr_ch_terminal(&stream,ch);
  2531. # > stream: Terminal-Stream
  2532. # > ch: auszugebendes Zeichen
  2533.   local void wr_ch_terminal (object* stream_, object ch);
  2534.   local void wr_ch_terminal(stream_,ch)
  2535.     var reg4 object* stream_;
  2536.     var reg2 object ch;
  2537.     { var reg1 object stream = *stream_;
  2538.       if (!string_char_p(ch)) { fehler_wr_string_char(stream,ch); } # ch sollte String-Char sein
  2539.      {var reg2 uintB c = char_code(ch); # Code des Zeichens
  2540.       if (c == NL) # Newline ?
  2541.         { wr_NL_terminal(stream); }
  2542.         else
  2543.         { # kein Newline
  2544.           # Wartet noch ein NL, wird es jetzt ausgegeben:
  2545.           if (eq(TheStream(stream)->strm_terminal_NLflag,Fixnum_1))
  2546.             { wr_NL_terminal(stream); }
  2547.           # Code c ⁿbers BIOS auf den Bildschirm ausgeben:
  2548.           # c >= ' ' -> ⁿbers GEMDOS
  2549.           # 0 <= c < 32 -> genau c in {1,..,4}u{14,..,25}u{28,..,31} ⁿbers BIOS
  2550.           #                sonst als Steuerzeichen ⁿbers BIOS
  2551.           if ((c < ' ') &&
  2552.               # Bit c aus der 32-Bit-Zahl %11110011111111111100000000011110 holen:
  2553.               (0xF3FFC01EUL & bit(c))
  2554.              )
  2555.             { BIOS_GrConOut(c); }
  2556.             else
  2557.             { BIOS_ConOut(c); }
  2558.         }
  2559.       # Line Position aktualisieren (es wartet kein NL mehr!):
  2560.       TheStream(stream)->strm_wr_ch_lpos =
  2561.         fixnum((UWORD)(vdiesc.v_cur_x));
  2562.       # Bei gedrⁿckter rechter Maustaste warten:
  2563.       if (LineA_MouseButtons() & bit(1)) # rechte Maustaste gedrⁿckt?
  2564.         { run_time_stop(); # Run-Time-Stoppuhr anhalten
  2565.           if (!(c==NL))
  2566.             # Maus wird bewegt -> Zeitschleife von 0.005 Sekunden.
  2567.             # Maus wird nicht bewegt -> warten, bis die rechte Maustaste los-
  2568.             # gelassen oder die Maus bewegt wurde, mind. jedoch 0.005 Sekunden.
  2569.             { interruptp( {goto interrupt;} ); # erst auf Tastatur-Interrupt testen
  2570.               # Zeitschleife von 0.005 Sekunden:
  2571.               { var reg1 uintC count; dotimespC(count,4096, ; ); }
  2572.             }
  2573.             else
  2574.             # Maus wird bewegt -> Zeitschleife von 0.4 Sekunden.
  2575.             # Maus wird nicht bewegt -> warten, bis die rechte Maustaste los-
  2576.             # gelassen oder die Maus bewegt wurde, mind. jedoch 0.4 Sekunden.
  2577.             { # Zeitschleife von 0.4 Sekunden mit Tastatur-Interrupt-Abfrage:
  2578.               var reg1 uintL endtime = get_real_time() + ticks_per_second*2/5; # zur momentanen Real-Time 0.4 sec. addieren,
  2579.               # ergibt Zeit, bis zu der zu warten ist.
  2580.               # warten, bis die Real-Time bei endtime angelangt ist:
  2581.               do { interruptp( {goto interrupt;} ); }
  2582.                  until (get_real_time() == endtime);
  2583.             }
  2584.           # Ende der Warteschleife oder nicht, je nach Mausstatus:
  2585.           loop
  2586.             { if (LineA_MouseStatus() & bit(5)) # Maus wurde bewegt?
  2587.                 { LineA_MouseStatus() &= ~bit(5); break; } # ja -> nicht weiter warten
  2588.               interruptp( {goto interrupt;} );
  2589.               if ((LineA_MouseButtons() & bit(1)) ==0) # rechte Maustaste gedrⁿckt?
  2590.                 break; # nein -> nicht mehr warten
  2591.             }
  2592.           if (TRUE)
  2593.             { run_time_restart(); } # Run-Time-Stoppuhr weiterlaufen lassen
  2594.             else
  2595.             { interrupt: # Tastatur-Interrupt
  2596.               run_time_restart(); # Run-Time-Stoppuhr weiterlaufen lassen
  2597.               pushSTACK(S(write_char)); tast_break(); # Break-Schleife aufrufen
  2598.         }   }
  2599.     }}
  2600.  
  2601. # UP: Bringt den wartenden Output eines Terminal-Stream ans Ziel.
  2602. # finish_output_terminal(stream);
  2603. # > stream: Terminal-Stream
  2604. # kann GC ausl÷sen
  2605.   local void finish_output_terminal (object stream);
  2606.   local void finish_output_terminal(stream)
  2607.     var reg1 object stream;
  2608.     { # Wartet noch ein NL, wird es jetzt ausgegeben:
  2609.       if (eq(TheStream(stream)->strm_terminal_NLflag,Fixnum_1))
  2610.         { wr_NL_terminal(stream); }
  2611.     }
  2612.  
  2613. # UP: Bringt den wartenden Output eines Terminal-Stream ans Ziel.
  2614. # force_output_terminal(stream);
  2615. # > stream: Terminal-Stream
  2616. # kann GC ausl÷sen
  2617.   #define force_output_terminal  finish_output_terminal
  2618.  
  2619. # Lesen eines Zeichens vom Terminal-Stream
  2620.   local object rd_ch_terminal (object* stream_);
  2621.   #
  2622.   # Zustand wΣhrend der Eingabe:
  2623.   typedef struct { uintL linelen; # LΣnge der Eingabezeile (<= length(inbuff))
  2624.                    uintL linepos; # Position in der Eingabezeile
  2625.                    uintC screenpos; # Line Position am Bildschirm (>=0, <window_width)
  2626.                  }
  2627.           rd_ch_vars;
  2628.   # > v : aktueller Zustand
  2629.   # > STACK_1 : Terminal-Stream
  2630.   # > STACK_0 : Eingabebuffer INBUFF
  2631.   #
  2632.   # Unterprogramme wΣhrend der Eingabe einer Zeile:
  2633.   #
  2634.   # UP: macht String so lang, da▀ noch ein Zeichen hineinpa▀t,
  2635.   # und erh÷ht die ZeilenlΣnge um 1.
  2636.   # add1char(&v);
  2637.     local void add1char (rd_ch_vars* v);
  2638.     local void add1char(v)
  2639.       var reg4 rd_ch_vars* v;
  2640.       { if (v->linelen >= TheSstring(STACK_0)->length)
  2641.           # Bei linelen < StringlΣnge ist im String noch Platz.
  2642.           # Bei linelen = StringlΣnge mu▀ man den String verlΣngern:
  2643.           { var reg3 uintL count = TheSstring(STACK_0)->length; # alte StringlΣnge (>0)
  2644.             var reg5 object new_string = allocate_string(2*count); # doppelt so langer String
  2645.             # Stringinhalt von String STACK_0 nach String new_string kopieren:
  2646.             var reg2 uintB* ptr1 = &TheSstring(STACK_0)->data[0];
  2647.             var reg1 uintB* ptr2 = &TheSstring(new_string)->data[0];
  2648.             dotimespL(count,count, { *ptr2++ = *ptr1++; } );
  2649.             # new_string ablegen:
  2650.             STACK_0 = new_string;
  2651.             TheStream(STACK_1)->strm_terminal_inbuff = new_string;
  2652.           }
  2653.         v->linelen++; # ZeilenlΣnge erh÷hen
  2654.       }
  2655.   #
  2656.   # UP: akzeptiert ein normales Zeichen c.
  2657.   # normalchar(&v,c);
  2658.     local void normalchar (rd_ch_vars* v, uintB c);
  2659.     local void normalchar(v,c)
  2660.       var reg1 rd_ch_vars* v;
  2661.       var reg2 uintB c;
  2662.       { # Eventuell die Eingabezeile verlΣngern:
  2663.         if (v->linepos >= v->linelen) { add1char(v); }
  2664.         # Jetzt ist sicher linepos < linelen.
  2665.         TheSstring(STACK_0)->data[v->linepos] = c; # Zeichen in den String
  2666.         v->linepos++; # und Index erh÷hen
  2667.         BIOS_ConOut(c); # Zeichen ausgeben
  2668.         # Bildschirmposition adjustieren:
  2669.         v->screenpos++; if (v->screenpos == window_width) { v->screenpos = 0; }
  2670.       }
  2671.   #
  2672.   # UP: Cursor in derselben Zeile an eine neue Position setzen.
  2673.   # cursor_gotox(&v,x);
  2674.   # > x: neue Position (>=0, <window_width)
  2675.     local void cursor_gotox (rd_ch_vars* v, uintC x);
  2676.     local void cursor_gotox(v,x)
  2677.       var reg2 rd_ch_vars* v;
  2678.       var reg3 uintC x;
  2679.       { if (x < floor(v->screenpos,2))
  2680.           { BIOS_ConOut(CR); v->screenpos = 0; } # nach links geht's mit CR schneller
  2681.        {var reg4 sintC rel_pos = x - v->screenpos; # relative Positionierung
  2682.         if (rel_pos >= 0)
  2683.           { var reg1 uintC count;
  2684.             dotimesC(count,rel_pos, { asciz_out(ESCstring "C"); } ); # Cursor nach rechts
  2685.           }
  2686.         else
  2687.           { var reg1 uintC count;
  2688.             dotimespC(count,-rel_pos, { asciz_out(ESCstring "D"); } ); # Cursor nach links
  2689.           }
  2690.         v->screenpos = x; # Cursor steht jetzt in Spalte x
  2691.       }}
  2692.   #
  2693.   # UP: Cursor ein Zeichen weiter nach links, linepos mitfⁿhren.
  2694.   # cursor_1left(&v);
  2695.     local void cursor_1left (rd_ch_vars* v);
  2696.     local void cursor_1left(v)
  2697.       var reg2 rd_ch_vars* v;
  2698.       { v->linepos--;
  2699.         if (!(v->screenpos == 0))
  2700.           # Cursor war nicht ganz links -> Cursor eins nach links
  2701.           { v->screenpos--; asciz_out(ESCstring "D"); }
  2702.           else
  2703.           # Cursor war ganz links -> zeilenⁿbergreifend:
  2704.           { v->screenpos = window_width-1;
  2705.             # bewege den Cursor vom linken Rand einer Zeile
  2706.             # an den rechten Rand der nΣchsth÷heren Zeile:
  2707.             asciz_out(ESCstring "f"); # Cursor ausschalten
  2708.             asciz_out(ESCstring "I"); # Cursor eine Zeile hoch
  2709.             { var reg1 uintC count;
  2710.               dotimesC(count,window_width-1, # 79 mal:
  2711.                 { asciz_out(ESCstring "C"); } # Cursor ein Zeichen rechts
  2712.                 );
  2713.             }
  2714.             asciz_out(ESCstring "e"); # Cursor wieder einschalten
  2715.           }
  2716.       }
  2717.   #
  2718.   # UP: Cursor einige Zeichen nach links.
  2719.   # cursor_left(&v,count);
  2720.   # > count: Zeichenzahl (>=0)
  2721.     local void cursor_left (rd_ch_vars* v, sintL count);
  2722.     local void cursor_left(v, count)
  2723.       var reg3 rd_ch_vars* v;
  2724.       var reg2 sintL count;
  2725.       { asciz_out(ESCstring "f"); # Cursor ausschalten
  2726.         # Cursor einige Zeilen hoch:
  2727.         {var reg1 sintL countlimit = v->screenpos;
  2728.          while (count > countlimit)
  2729.            { asciz_out(ESCstring "I"); # Cursor eine Zeile hoch
  2730.              count -= window_width;
  2731.         }  }
  2732.         # Nun ist screenpos-window_width < count <= screenpos.
  2733.         # Der Cursor befindet sich also in der richtigen Zeile.
  2734.         cursor_gotox(v,v->screenpos-count);
  2735.         # Der Cursor steht jetzt richtig.
  2736.         asciz_out(ESCstring "e"); # Cursor wieder einschalten
  2737.       }
  2738.   #
  2739.   # UP: Cursor ein Zeichen weiter nach rechts, linepos mitfⁿhren.
  2740.   # cursor_1right(&v);
  2741.     local void cursor_1right (rd_ch_vars* v);
  2742.     local void cursor_1right(v)
  2743.       var reg2 rd_ch_vars* v;
  2744.       { if (!(v->screenpos == window_width-1))
  2745.           # Cursor war nicht ganz rechts -> Cursor eins nach rechts
  2746.           { v->screenpos++; asciz_out(ESCstring "C"); }
  2747.           else
  2748.           # Cursor war ganz rechts -> zeilenⁿbergreifend:
  2749.           { v->screenpos = 0;
  2750.             BIOS_ConOut(TheSstring(STACK_0)->data[v->linepos]);
  2751.           }
  2752.         v->linepos++;
  2753.       }
  2754.   #
  2755.   # UP: Cursor einige Zeichen nach rechts.
  2756.   # cursor_right(&v,count);
  2757.   # > count: Zeichenzahl (>=0)
  2758.     local void cursor_right (rd_ch_vars* v, sintL count);
  2759.     local void cursor_right(v, count)
  2760.       var reg3 rd_ch_vars* v;
  2761.       var reg2 sintL count;
  2762.       { asciz_out(ESCstring "f"); # Cursor ausschalten
  2763.         # Cursor einige Zeilen runter:
  2764.         {var reg1 sintL countlimit = window_width - v->screenpos;
  2765.          while (count >= countlimit)
  2766.            { asciz_out(ESCstring "B"); # Cursor eine Zeile runter
  2767.              count -= window_width;
  2768.         }  }
  2769.         # Nun ist -screenpos <= count < window_width-screenpos.
  2770.         # Der Cursor befindet sich also in der richtigen Zeile.
  2771.         cursor_gotox(v,v->screenpos+count);
  2772.         # Der Cursor steht jetzt richtig.
  2773.         asciz_out(ESCstring "e"); # Cursor wieder einschalten
  2774.       }
  2775.   #
  2776.   # UP: An der Cursor-Position Platz fⁿr ein Zeichen machen.
  2777.   # insert1char(&v,c);
  2778.     local void insert1char (rd_ch_vars* v, uintB c);
  2779.     local void insert1char(v,c)
  2780.       var reg5 rd_ch_vars* v;
  2781.       var reg2 uintB c;
  2782.       { add1char(v); # Eingabezeile wird 1 Zeichen lΣnger
  2783.        {var reg5 uintL charcount = v->linelen - v->linepos;
  2784.         # = Anzahl der zu verschiebenden Zeichen + 1
  2785.         asciz_out(ESCstring "f"); # Cursor ausschalten
  2786.         {var reg1 uintB* ptr = &TheSstring(STACK_0)->data[v->linepos];
  2787.          var reg3 uintL count;
  2788.          # Schleife charcount mal durchlaufen, dabei beim ersten
  2789.          # Durchlauf das ⁿbergebene c einfⁿgen:
  2790.          dotimespL(count,charcount,
  2791.            { var reg4 uintB nextc = *ptr; # nΣchstes Zeichen
  2792.              *ptr++ = c; # durch c ersetzen
  2793.              BIOS_ConOut(c); # und c ausgeben
  2794.              c = nextc;
  2795.            });
  2796.         }
  2797.         # neue Spalte am Bildschirm berechnen:
  2798.         {var reg1 uintL new_screenpos = v->screenpos + charcount;
  2799.          while (new_screenpos >= window_width) { new_screenpos -= window_width; }
  2800.          v->screenpos = new_screenpos;
  2801.         }
  2802.         # Cursor an die alte Position und Cursor wieder einschalten:
  2803.         cursor_left(v,charcount);
  2804.       }}
  2805.   #
  2806.   # UP: An der Cursor-Position ein Zeichen l÷schen.
  2807.   # > 0 <= linepos < linelen.
  2808.   # delete1(&v);
  2809.     local void delete1 (rd_ch_vars* v);
  2810.     local void delete1(v)
  2811.       var reg5 rd_ch_vars* v;
  2812.       { v->linelen--; # Gesamtzeichenzahl decrementieren
  2813.        {var reg4 uintL charcount = v->linelen - v->linepos;
  2814.         # = Anzahl der zu verschiebenden Zeichen
  2815.         asciz_out(ESCstring "f"); # Cursor ausschalten
  2816.         {var reg1 uintB* ptr = &TheSstring(STACK_0)->data[v->linepos];
  2817.          var reg3 uintL count;
  2818.          dotimesL(count,charcount,
  2819.            { var reg2 uintB nextch = *(ptr+1); # nΣchstes Zeichen
  2820.              *ptr++ = nextch; # um eine Position nach vorne schieben
  2821.              BIOS_ConOut(nextch); # und ausgeben
  2822.            });
  2823.         }
  2824.         BIOS_ConOut(' '); # letztes Zeichen mit ' ' ⁿberschreiben
  2825.         charcount++;
  2826.         # neue Spalte am Bildschirm berechnen:
  2827.         {var reg1 uintL new_screenpos = v->screenpos + charcount;
  2828.          while (new_screenpos >= window_width) { new_screenpos -= window_width; }
  2829.          v->screenpos = new_screenpos;
  2830.         }
  2831.         # Cursor an die alte Position und Cursor wieder einschalten:
  2832.         cursor_left(v,charcount);
  2833.       }}
  2834.   #
  2835.   local object rd_ch_terminal(stream_)
  2836.     var reg1 object* stream_;
  2837.     { var reg1 object stream = *stream_;
  2838.       var reg1 object inbuff = TheStream(stream)->strm_terminal_inbuff; # Eingabebuffer
  2839.       if (posfixnum_to_L(TheStream(stream)->strm_terminal_index)
  2840.           >= posfixnum_to_L(TheStream(stream)->strm_terminal_count)
  2841.          )
  2842.         # index<count -> Es sind noch Zeichen im Buffer
  2843.         # index=count -> mu▀ eine ganze Zeile von Tastatur lesen:
  2844.         { # Wartet noch ein NL, wird es jetzt ausgegeben:
  2845.           if (eq(TheStream(stream)->strm_terminal_NLflag,Fixnum_1))
  2846.             { wr_NL_terminal(stream); }
  2847.           TheStream(stream)->strm_terminal_index = Fixnum_0; # index := 0
  2848.           TheStream(stream)->strm_terminal_count = Fixnum_0; # count := 0
  2849.          {var rd_ch_vars v; # Zustandsvariablen
  2850.           v.linelen = 0; v.linepos = 0; # Zeile noch leer
  2851.           v.screenpos = posfixnum_to_L(TheStream(stream)->strm_wr_ch_lpos); # aktuelle Screen-Spalte
  2852.           pushSTACK(stream);
  2853.           pushSTACK(inbuff);
  2854.           asciz_out(ESCstring "e"); # Cursor einschalten
  2855.           loop
  2856.             { # Eingabeschleife.
  2857.               # STACK_1 : Terminal-Stream,
  2858.               # STACK_0 : Eingabezeile inbuff (ein Simple-String),
  2859.               # v.linelen : LΣnge der Eingabezeile (<= length(inbuff))
  2860.               # Eingabezeile = TheSstring(STACK_0)->data[0..(v.linelen-1)]
  2861.               # v.linepos : Position in der Eingabezeile (>=0, <=linelen)
  2862.               # v.screenpos : Line Position am Bildschirm (>=0, <window_width)
  2863.               {var object kbstream = var_stream(S(keyboard_input)); # Stream *KEYBOARD-INPUT*
  2864.                var reg2 object ch = read_char(&kbstream); # Zeichen holen
  2865.                if (eq(ch,eof_value)) # EOF (sollte nicht auftreten) ?
  2866.                  { funcall(L(exit),0); } # ja -> LISP beenden
  2867.                if (v.linelen == 0)
  2868.                  # Eine Taste aus SYS::*KEY-BINDINGS* ?
  2869.                  { var reg1 object alist = Symbol_value(S(key_bindings));
  2870.                    while (consp(alist))
  2871.                      { if (mconsp(Car(alist)) && eq(ch,Car(Car(alist))))
  2872.                          { asciz_out(ESCstring "f"); # Cursor ausschalten
  2873.                            funcall(Cdr(Car(alist)),0); # Funktion dazu aufrufen
  2874.                            # und wenn diese Funktion zurⁿckkehren sollte:
  2875.                            asciz_out(ESCstring "e"); # Cursor einschalten
  2876.                            break;
  2877.                          }
  2878.                        alist = Cdr(alist);
  2879.                  }   }
  2880.                {var reg1 cint c = char_int(ch);
  2881.                 if (!(c & char_hyper_c)) # HYPER-Bit gesetzt ?
  2882.                   # nein -> normales Zeichen
  2883.                   { c &= char_code_mask_c; # SUPER-Bit usw. wegmaskieren
  2884.                     switch (c)
  2885.                       { case BS: # Backspace
  2886.                           # Backspace-Taste entfernt das Zeichen links vom Cursor
  2887.                           if (v.linepos == 0) goto nix; # war der Cursor schon ganz links -> Fehleingabe
  2888.                           cursor_1left(&v); # Cursor ein Zeichen nach links
  2889.                           delete1(&v); # Zeichen unterm Cursor l÷schen
  2890.                           break;
  2891.                         case TAB: # Tab
  2892.                           # Tab fⁿgt Spaces ein, bis der Cursor auf einer
  2893.                           # durch 8 teilbaren Spalte steht.
  2894.                           do { normalchar(&v,' '); }
  2895.                              until ((v.screenpos % 8) == 0);
  2896.                           break;
  2897.                         case CR: # Return/Enter
  2898.                           do_CR:
  2899.                           # Return/Enter beendet die Zeile mit einem NL.
  2900.                           cursor_right(&v,v.linelen - v.linepos);
  2901.                           add1char(&v);
  2902.                           # String mit NL abschlie▀en:
  2903.                           TheSstring(STACK_0)->data[v.linelen-1] = NL;
  2904.                           goto loopend;
  2905.                         default:
  2906.                           if (c < ' ') # sonstiges Control-Zeichen < ' ' ?
  2907.                             goto nix; # ja -> war nix
  2908.                           # normales Zeichen
  2909.                           normalchar(&v,c);
  2910.                   }   }
  2911.                   else
  2912.                   # ja -> Sondertaste
  2913.                   { switch (c)
  2914.                       { case char_hyper_c|CR : # Taste 'Enter'
  2915.                           goto do_CR;
  2916.                         case char_hyper_c|127: # Taste 'Delete'
  2917.                           # Delete-Taste entfernt das Zeichen unterm Cursor
  2918.                           if (v.linepos >= v.linelen) goto nix; # war der Cursor schon ganz rechts -> Fehleingabe
  2919.                           delete1(&v); # Zeichen unterm Cursor l÷schen
  2920.                           break;
  2921.                         case char_hyper_c|16 : # Taste 'Insert'
  2922.                           # Insert-Taste macht am Cursor Platz fⁿr ein Zeichen
  2923.                           insert1char(&v,' '); break;
  2924.                         case char_hyper_c|20 : # Taste '<-'
  2925.                           # Pfeil links -> Cursor eine Position nach links
  2926.                           if (v.linepos==0) goto nix; # war der Cursor schon ganz links -> Fehleingabe
  2927.                           cursor_1left(&v); break;
  2928.                         case char_hyper_c|char_super_c|20 : # Taste Shift '<-'
  2929.                           # Shift Pfeil links -> Cursor ganz nach links
  2930.                           cursor_left(&v,v.linepos);
  2931.                           v.linepos = 0;
  2932.                           break;
  2933.                         case char_hyper_c|22 : # Taste '->'
  2934.                           # Pfeil rechts -> Cursor eine Position nach rechts
  2935.                           if (v.linepos < v.linelen)
  2936.                             { cursor_1right(&v); }
  2937.                             else
  2938.                             { normalchar(&v,' '); } # am Zeilenende: Leerstelle anfⁿgen
  2939.                           break;
  2940.                         case char_hyper_c|char_super_c|22 : # Taste Shift '->'
  2941.                           # Shift Pfeil rechts -> Cursor ganz nach rechts
  2942.                           cursor_right(&v,v.linelen-v.linepos);
  2943.                           v.linepos = v.linelen;
  2944.                           break;
  2945.                         default:
  2946.                           { var reg1 uintB code = c & char_code_mask_c;
  2947.                             if ((code >= ' ') && (code < '@'))
  2948.                               # Sondertaste >=' ' und <'@' kommt vom Ziffernblock
  2949.                               # -> als normales Zeichen behandeln
  2950.                               { normalchar(&v,code); break; }
  2951.                           }
  2952.                         nix: # Zeichen war nix (Fehleingabe) -> Glocke lΣuten
  2953.                           BIOS_Bell();
  2954.                   }   }
  2955.             } }}
  2956.           loopend: # Eingabezeile fertig.
  2957.           asciz_out(ESCstring "f"); # Cursor ausschalten
  2958.           inbuff = popSTACK(); stream = popSTACK(); # STACK aufrΣumen
  2959.           TheStream(stream)->strm_terminal_index = Fixnum_0; # Index := 0
  2960.           TheStream(stream)->strm_terminal_count = fixnum(v.linelen);
  2961.           TheStream(stream)->strm_terminal_NLflag = Fixnum_1; # wartendes NL vormerken
  2962.           TheStream(stream)->strm_wr_ch_lpos = Fixnum_0; # und deswegen Line Position := 0
  2963.         }}
  2964.       # Jetzt sind genug Zeichen da, d.h. INDEX < COUNT .
  2965.      {var reg1 uintL index =
  2966.         posfixnum_to_L(TheStream(stream)->strm_terminal_index); # Index
  2967.       TheStream(stream)->strm_terminal_index =
  2968.         fixnum_inc(TheStream(stream)->strm_terminal_index,1); # Index erh÷hen
  2969.       return code_char(TheSstring(inbuff)->data[index]); # nΣchstes Character
  2970.     }}
  2971.  
  2972. # Liefert einen interaktiven Terminal-Stream.
  2973. # kann GC ausl÷sen
  2974.   local object make_terminal_stream_ (void);
  2975.   local object make_terminal_stream_()
  2976.     { # Bildschirmgr÷▀e bestimmen:
  2977.       window_size.x = (UWORD)(vdiesc.v_cel_mx+1);
  2978.       window_size.y = (UWORD)(vdiesc.v_cel_my+1);
  2979.      {# neuen Stream allozieren:
  2980.       var reg2 object stream =
  2981.         allocate_stream(strmflags_ch_B,strmtype_terminal,strm_len+4);
  2982.         # Flags: nur READ-CHAR und WRITE-CHAR erlaubt
  2983.       # und fⁿllen:
  2984.       var reg1 Stream s = TheStream(stream);
  2985.         s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  2986.         s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  2987.         s->strm_rd_ch = P(rd_ch_terminal); # READ-CHAR-Pseudofunktion
  2988.         s->strm_rd_ch_last = NIL; # Lastchar := NIL
  2989.         s->strm_wr_ch = P(wr_ch_terminal); # WRITE-CHAR-Pseudofunktion
  2990.         s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  2991.         #ifdef STRM_WR_SS
  2992.         s->strm_wr_ss = P(wr_ss_dummy);
  2993.         #endif
  2994.         {pushSTACK(stream);
  2995.          {var reg1 object inbuff = allocate_string(window_width+1); # neuer String
  2996.           # (reicht fⁿr alle einzeiligen Eingaben)
  2997.           stream = popSTACK(); s = TheStream(stream);
  2998.           s->strm_terminal_inbuff = inbuff; # als Eingabebuffer
  2999.         }}
  3000.         s->strm_terminal_count = Fixnum_0; # Buffer leer, Count:=0
  3001.         s->strm_terminal_index = Fixnum_0; # Index:=0
  3002.         s->strm_terminal_NLflag = Fixnum_0; # NL-Flag := false
  3003.       return stream;
  3004.     }}
  3005.  
  3006. #endif # ATARI
  3007.  
  3008. #ifdef WINDOWS
  3009.  
  3010. # Benutze die Eingabefunktionen aus wintext.d.
  3011. # Output:
  3012. extern void winterm_writechar (mywindow w, uintB c);
  3013. # Input:
  3014. extern signean winterm_listen (mywindow w);
  3015. extern boolean winterm_clear_input (mywindow w);
  3016. extern cint winterm_readchar (mywindow w);
  3017.  
  3018. # Stellt fest, ob ein Terminal-Stream ein Zeichen verfⁿgbar hat.
  3019. # listen_terminal(stream)
  3020. # > stream: Terminal-Stream
  3021. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  3022. #             -1 falls bei EOF angelangt,
  3023. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  3024.   local signean listen_terminal (object stream);
  3025.   local signean listen_terminal(stream)
  3026.     var reg1 object stream;
  3027.     { return winterm_listen(main_window); }
  3028.  
  3029. # UP: L÷scht bereits eingegebenen interaktiven Input von einem Terminal-Stream.
  3030. # clear_input_terminal(stream);
  3031. # > stream: Terminal-Stream
  3032. # < ergebnis: TRUE falls Input gel÷scht wurde, FALSE sonst
  3033.   local boolean clear_input_terminal (object stream);
  3034.   local boolean clear_input_terminal(stream)
  3035.     var reg1 object stream;
  3036.     { return winterm_clear_input(main_window); }
  3037.  
  3038. # UP: Ein Zeichen auf einen Terminal-Stream ausgeben.
  3039. # wr_ch_terminal(&stream,ch);
  3040. # > stream: Terminal-Stream
  3041. # > ch: auszugebendes Zeichen
  3042.   local void wr_ch_terminal (object* stream_, object ch);
  3043.   local void wr_ch_terminal(stream_,ch)
  3044.     var reg2 object* stream_;
  3045.     var reg1 object ch;
  3046.     { var reg3 object stream = *stream_;
  3047.       if (!string_char_p(ch)) { fehler_wr_string_char(stream,ch); } # ch sollte String-Char sein
  3048.       winterm_writechar(main_window,char_code(ch));
  3049.     }
  3050.  
  3051. # UP: Bringt den wartenden Output eines Terminal-Stream ans Ziel.
  3052. # finish_output_terminal(stream);
  3053. # > stream: Terminal-Stream
  3054. # kann GC ausl÷sen
  3055.   #define finish_output_terminal(stream)
  3056.  
  3057. # UP: Bringt den wartenden Output eines Terminal-Stream ans Ziel.
  3058. # force_output_terminal(stream);
  3059. # > stream: Terminal-Stream
  3060. # kann GC ausl÷sen
  3061.   #define force_output_terminal(stream)
  3062.  
  3063. # UP: Ein Zeichen von einem Terminal-Stream lesen.
  3064. # rd_ch_terminal(&stream)
  3065. # > stream: Terminal-Stream
  3066. # < object ch: eingegebenes Zeichen
  3067.   local object rd_ch_terminal (object* stream_);
  3068.   local object rd_ch_terminal(stream_)
  3069.     var reg1 object* stream_;
  3070.     { return int_char(winterm_readchar(main_window)); }
  3071.  
  3072. # Liefert einen interaktiven Terminal-Stream.
  3073. # kann GC ausl÷sen
  3074.   local object make_terminal_stream_ (void);
  3075.   local object make_terminal_stream_()
  3076.     { # neuen Stream allozieren:
  3077.       var reg2 object stream =
  3078.         allocate_stream(strmflags_ch_B,strmtype_terminal,strm_len+0);
  3079.         # Flags: nur READ-CHAR und WRITE-CHAR erlaubt
  3080.       # und fⁿllen:
  3081.       var reg1 Stream s = TheStream(stream);
  3082.         s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  3083.         s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  3084.         s->strm_rd_ch = P(rd_ch_terminal); # READ-CHAR-Pseudofunktion
  3085.         s->strm_rd_ch_last = NIL; # Lastchar := NIL
  3086.         s->strm_wr_ch = P(wr_ch_terminal); # WRITE-CHAR-Pseudofunktion
  3087.         s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  3088.         #ifdef STRM_WR_SS
  3089.         s->strm_wr_ss = P(wr_ss_dummy);
  3090.         #endif
  3091.       return stream;
  3092.     }
  3093.  
  3094. #endif # WINDOWS
  3095.  
  3096. #if defined(UNIX) || (defined(MSDOS) && !defined(WINDOWS)) || defined(AMIGAOS) || defined(RISCOS)
  3097.  
  3098. # Funktionsweise:
  3099. # Es wird auf Standard-Input und Standard-Output zugegriffen.
  3100. # Wegen Umleite-M÷glichkeit mⁿssen manche Funktionen unterscheiden, ob es
  3101. # sich bei Standard-Input um ein Terminal handelt oder nicht.
  3102. # Ob Standard-Output ein Terminal ist oder nicht, ist hier irrelevant.
  3103. # Relevant ist nur, ob Standard-Input und Standard-Output dasselbe Terminal
  3104. # sind; in diesem Falle nehmen wir an, da▀ nach Beendigung einer Eingabezeile
  3105. # (durch NL) von Standard-Input der Cursor von Standard-Output in Spalte 0
  3106. # steht, und in diesem Falle k÷nnen wir auch die GNU readline()-Library
  3107. # benutzen.
  3108.  
  3109. # Es gibt drei m÷gliche Varianten des Terminal-Streams:
  3110. # Wenn Standard-Input und Standard-Output nicht dasselbe Terminal sind:
  3111. #   * terminal1 normalerweise,
  3112. #   * terminal2 mit zeilenweiser Bufferung der Eingabe,
  3113. # Wenn Standard-Input und Standard-Output dasselbe Terminal sind:
  3114. #   * terminal3 benutzt readline()-Library, mit zeilenweiser Bufferung der
  3115. #     Eingabe und der Ausgabe.
  3116.  
  3117. #define HAVE_TERMINAL1
  3118.   # define TERMINAL_LINEBUFFERED  0
  3119.   # define TERMINAL_OUTBUFFERED   0
  3120.  
  3121. #ifdef MSDOS
  3122.   # Bei Eingabe einer Zeile von Tastatur wird das <Enter> am Ende der Zeile als
  3123.   # CR/LF ausgegeben. Jedoch: Das CR sofort, das LF jedoch erst dann, wenn das
  3124.   # <Enter> mit read() gelesen wird - das ist bei uns manchmal erst viel spΣter.
  3125.   # [Wer diesen Schwachsinn programmiert hat - im DOS vermutlich -
  3126.   # geh÷rt an die Wand gestellt und erschossen! :-(]
  3127.   # Aus diesem Grund mⁿssen wir den Terminal-Stream auf der Input-Seite
  3128.   # zeilengepuffert machen.
  3129. #define HAVE_TERMINAL2
  3130.   # define TERMINAL_LINEBUFFERED  1
  3131.   # define TERMINAL_OUTBUFFERED   0
  3132. #endif
  3133.  
  3134. #ifdef GNU_READLINE
  3135.   # Wir benutzen die GNU Readline-Library. Sie liefert den Input zeilenweise,
  3136.   # mit Editierm÷glichkeit, VervollstΣndigung und History. Leider mⁿssen wir
  3137.   # den Output zeilenweise zwischenspeichern, um die letzte angefangene Zeile
  3138.   # als "Prompt" verwenden zu k÷nnen.
  3139. #define HAVE_TERMINAL3
  3140.   # define TERMINAL_LINEBUFFERED  1
  3141.   # define TERMINAL_OUTBUFFERED   1
  3142. #endif
  3143.  
  3144. # ZusΣtzliche Komponenten:
  3145.   # ISATTY : Flag, ob stdin ein TTY ist und ob stdin und stdout dasselbe sind:
  3146.   #          NIL: stdin ein File o.Σ.
  3147.   #          T, EQUAL: stdin ein Terminal
  3148.   #          EQUAL: stdin und stdout dasselbe Terminal
  3149.   #define strm_terminal_isatty   strm_isatty
  3150.   #define strm_terminal_ihandle  strm_ihandle
  3151.   #define strm_terminal_ohandle  strm_ohandle
  3152. #if defined(HAVE_TERMINAL2) || defined(HAVE_TERMINAL3)
  3153.   # Komponenten wegen TERMINAL_LINEBUFFERED:
  3154.   # INBUFF : Eingabebuffer, ein Semi-Simple-String
  3155.   #define strm_terminal_inbuff  strm_other[3]
  3156.   # COUNT = sein Fill-Pointer : Anzahl der Zeichen im Eingabebuffer
  3157.   # INDEX : Anzahl der bereits verbrauchten Zeichen
  3158.   #define strm_terminal_index   strm_other[4]
  3159. #endif
  3160. #ifdef HAVE_TERMINAL3
  3161.   # Komponenten wegen TERMINAL_OUTBUFFERED:
  3162.   # OUTBUFF : Ausgabebuffer, ein Semi-Simple-String
  3163.   #define strm_terminal_outbuff strm_other[5]
  3164. #endif
  3165.  
  3166. # LΣngen der unterschiedlichen Terminal-Streams:
  3167.   #define strm_terminal1_len  (strm_len+3)
  3168.   #define strm_terminal2_len  (strm_len+5)
  3169.   #define strm_terminal3_len  (strm_len+6)
  3170.  
  3171. # Unterscheidung nach Art des Terminal-Streams:
  3172. # terminalcase(stream, statement1,statement2,statement3);
  3173.   #if defined(HAVE_TERMINAL2) && defined(HAVE_TERMINAL3)
  3174.     #define terminalcase(stream,statement1,statement2,statement3)  \
  3175.       switch (TheStream(stream)->reclength)          \
  3176.         { case strm_terminal1_len: statement1 break; \
  3177.           case strm_terminal2_len: statement2 break; \
  3178.           case strm_terminal3_len: statement3 break; \
  3179.           default: NOTREACHED                        \
  3180.         }
  3181.   #elif defined(HAVE_TERMINAL2)
  3182.     #define terminalcase(stream,statement1,statement2,statement3)  \
  3183.       if (TheStream(stream)->reclength == strm_terminal2_len) { statement2 } else { statement1 }
  3184.   #elif defined(HAVE_TERMINAL3)
  3185.     #define terminalcase(stream,statement1,statement2,statement3)  \
  3186.       if (TheStream(stream)->reclength == strm_terminal3_len) { statement3 } else { statement1 }
  3187.   #else
  3188.     #define terminalcase(stream,statement1,statement2,statement3)  \
  3189.       statement1
  3190.   #endif
  3191.  
  3192. #ifdef MSDOS
  3193.  
  3194.   # get_handle_info(handle)
  3195.   # > handle
  3196.   # < ergebnis: Handle-Info (INT 21,44,00)
  3197.   #ifdef DJUNIX
  3198.     #define get_handle_info(handle)  \
  3199.       ({ var reg1 uintW __info;                                                       \
  3200.          __asm__ (# DOS-Funktion 44H, Code 00H                                        \
  3201.                   " movw $0x4400,%%ax ; int $0x21 "                                   \
  3202.                   : "=d" /* %dx */ (__info)                                 # OUT     \
  3203.                   : "b" /* %bx */ ((uintW)(handle))                         # IN      \
  3204.                   : "ax","bx","cx","si","di" /* %eax,%ebx,%ecx,%esi,%edi */ # CLOBBER \
  3205.                  );                                                                   \
  3206.          __info;                                                                      \
  3207.        })
  3208.   #endif
  3209.   #ifdef EMUNIX
  3210.     #define get_handle_info(handle)  __ioctl1(handle,0x00)
  3211.   #endif
  3212.   #ifdef WATCOM
  3213.     local uintW get_handle_info (uintW handle);
  3214.     local uintW get_handle_info(handle)
  3215.       var uintW handle;
  3216.       { var union REGS in;
  3217.         var union REGS out;
  3218.         in.regW.ax = 0x4400; in.regW.bx = handle;
  3219.         intdos(&in,&out);
  3220.         return out.regW.dx;
  3221.       }
  3222.   #endif
  3223.  
  3224. #endif
  3225.  
  3226. #ifdef HAVE_TERMINAL1
  3227.  
  3228. # Lesen eines Zeichens von einem Terminal-Stream.
  3229.   local object rd_ch_terminal1 (object* stream_);
  3230.   local object rd_ch_terminal1(stream_)
  3231.     var reg3 object* stream_;
  3232.     { var reg1 object ch = rd_ch_handle(stream_);
  3233.       # Wenn stdin und stdout beide dasselbe Terminal sind,
  3234.       # und wir lesen ein NL, so k÷nnen wir davon ausgehen,
  3235.       # da▀ der Cursor danach in Spalte 0 steht.
  3236.       if (eq(ch,code_char(NL)))
  3237.         { var reg2 object stream = *stream_;
  3238.           if (eq(TheStream(stream)->strm_terminal_isatty,S(equal)))
  3239.             { TheStream(stream)->strm_wr_ch_lpos = Fixnum_0; }
  3240.         }
  3241.       return ch;
  3242.     }
  3243.  
  3244. # Stellt fest, ob ein Terminal-Stream ein Zeichen verfⁿgbar hat.
  3245. # listen_terminal1(stream)
  3246. # > stream: Terminal-Stream
  3247. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  3248. #             -1 falls bei EOF angelangt,
  3249. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  3250.   #define listen_terminal1  listen_handle
  3251.  
  3252. # UP: L÷scht bereits eingegebenen interaktiven Input von einem Terminal-Stream.
  3253. # clear_input_terminal1(stream);
  3254. # > stream: Terminal-Stream
  3255. # < ergebnis: TRUE falls Input gel÷scht wurde, FALSE sonst
  3256.   #define clear_input_terminal1  clear_input_handle
  3257.  
  3258. # UP: Ein Zeichen auf einen Terminal-Stream ausgeben.
  3259. # wr_ch_terminal1(&stream,ch);
  3260. # > stream: Terminal-Stream
  3261. # > ch: auszugebendes Zeichen
  3262.  #if !defined(AMIGAOS)
  3263.   #define wr_ch_terminal1  wr_ch_handle
  3264.  #else # defined(AMIGAOS)
  3265.   local void wr_ch_terminal1 (object* stream_, object ch);
  3266.   local void wr_ch_terminal1(stream_,ch)
  3267.     var reg6 object* stream_;
  3268.     var reg5 object ch;
  3269.     { # ch sollte ein Character mit h÷chstens Font, aber ohne Bits sein:
  3270.       if (!((as_oint(ch) & ~(((oint)char_code_mask_c|(oint)char_font_mask_c)<<oint_data_shift)) == as_oint(type_data_object(char_type,0))))
  3271.         { pushSTACK(*stream_);
  3272.           pushSTACK(ch);
  3273.           fehler(error,
  3274.                  DEUTSCH ? "Character ~ enthΣlt Bits und kann daher nicht auf ~ ausgegeben werden." :
  3275.                  ENGLISH ? "character ~ contains bits, cannot be output onto ~" :
  3276.                  FRANCAIS ? "Le caractΦre ~ contient des ½bits╗ et ne peut pas Ωtre Θcrit dans ~." :
  3277.                  ""
  3278.                 );
  3279.         }
  3280.      #if (!(char_font_len_c == 4))
  3281.        #error "char_font_len_c neu einstellen oder wr_ch_terminal neu schreiben!"
  3282.      #endif
  3283.      {var uintB outbuffer[14];
  3284.       var reg1 uintB* ptr = &outbuffer[0];
  3285.       var reg3 uintL count = 1;
  3286.       var reg2 uintB f = (char_int(ch) & char_font_mask_c) >> char_font_shift_c; # Font des Zeichens
  3287.       var reg4 uintB c = char_code(ch); # Code des Zeichens
  3288.       if (f==0)
  3289.         { *ptr++ = c; }
  3290.         else
  3291.         { *ptr++ = CSI; # Kontroll-Sequenz zum Umschalten auf den richtigen Font:
  3292.           if (f & bit(0)) { *ptr++ = '1'; *ptr++ = ';'; count += 2; } # Fettschrift ein
  3293.           if (f & bit(1)) { *ptr++ = '3'; *ptr++ = ';'; count += 2; } # Kursiv ein
  3294.           if (f & bit(2)) { *ptr++ = '4'; *ptr++ = ';'; count += 2; } # Unterstreichung ein
  3295.           if (f & bit(3)) { *ptr++ = '7'; *ptr++ = ';'; count += 2; } # Reverse ein
  3296.           *ptr++ = 0x6D;
  3297.           *ptr++ = c; # dann das Zeichen ausgeben
  3298.           *ptr++ = CSI; *ptr++ = '0'; *ptr++ = 0x6D; # Wieder Normalschrift
  3299.           count += 5;
  3300.         }
  3301.       begin_system_call();
  3302.       {var reg1 long ergebnis = Write(Output_handle,&outbuffer[0],count); # Zeichen auszugeben versuchen
  3303.        end_system_call();
  3304.        if (ergebnis<0) { OS_error(); } # Error melden
  3305.        if (ergebnis<count) # nicht erfolgreich?
  3306.          { fehler_unwritable(S(write_char),*stream_); }
  3307.     }}}
  3308.  #endif
  3309.  
  3310. #ifdef STRM_WR_SS
  3311. # UP: Mehrere Zeichen auf einen Terminal-Stream ausgeben.
  3312. # wr_ss_terminal1(&stream,string,start,len);
  3313. # > stream: Terminal-Stream
  3314. # > string: Simple-String
  3315. # > start: Startindex
  3316. # > len: Anzahl der auszugebenden Zeichen
  3317.   #define wr_ss_terminal1  wr_ss_handle
  3318. #endif
  3319.  
  3320. # UP: L÷scht den wartenden Output eines Terminal-Stream.
  3321. # clear_output_terminal1(stream);
  3322. # > stream: Terminal-Stream
  3323. # kann GC ausl÷sen
  3324.   #define clear_output_terminal1  clear_output_handle
  3325.  
  3326. #endif # HAVE_TERMINAL1
  3327.  
  3328. #ifdef HAVE_TERMINAL2
  3329.  
  3330. #define TERMINAL_LINEBUFFERED  TRUE
  3331.  
  3332. # Lesen eines Zeichens von einem Terminal-Stream.
  3333.   local object rd_ch_terminal2 (object* stream_);
  3334.   # vgl. rd_ch_handle() :
  3335.   local object rd_ch_terminal2(stream_)
  3336.     var reg3 object* stream_;
  3337.     { restart_it:
  3338.      {var reg2 object stream = *stream_;
  3339.       if (eq(TheStream(stream)->strm_rd_ch_last,eof_value)) # schon EOF ?
  3340.         { return eof_value; }
  3341.       #if TERMINAL_LINEBUFFERED
  3342.       { var reg4 object inbuff = TheStream(stream)->strm_terminal_inbuff; # Eingabebuffer
  3343.         if (posfixnum_to_L(TheStream(stream)->strm_terminal_index)
  3344.             < TheArray(inbuff)->dims[1]
  3345.            )
  3346.           # index<count -> Es sind noch Zeichen im Buffer
  3347.           { var reg1 uintL index =
  3348.               posfixnum_to_L(TheStream(stream)->strm_terminal_index); # Index
  3349.             TheStream(stream)->strm_terminal_index =
  3350.               fixnum_inc(TheStream(stream)->strm_terminal_index,1); # Index erh÷hen
  3351.             return code_char(TheSstring(TheArray(inbuff)->data)->data[index]); # nΣchstes Character
  3352.           }
  3353.         # index=count -> mu▀ eine ganze Zeile von Tastatur lesen:
  3354.         TheStream(stream)->strm_terminal_index = Fixnum_0; # index := 0
  3355.         TheArray(inbuff)->dims[1] = 0; # count := 0
  3356.       }
  3357.       continue_line:
  3358.       #endif
  3359.       {var uintB c;
  3360.        run_time_stop(); # Run-Time-Stoppuhr anhalten
  3361.        #ifdef GRAPHICS_SWITCH
  3362.        switch_text_mode();
  3363.        #endif
  3364.        begin_system_call();
  3365.        {var reg1 int ergebnis = read(stdin_handle,&c,1); # Zeichen lesen versuchen
  3366.         end_system_call();
  3367.         run_time_restart(); # Run-Time-Stoppuhr weiterlaufen lassen
  3368.         if (ergebnis<0)
  3369.           { if (errno==EINTR) # Unterbrechung (evtl. durch Ctrl-C) ?
  3370.               { interruptp({ pushSTACK(S(read_char)); tast_break(); }); # Break-Schleife aufrufen
  3371.                 goto restart_it;
  3372.               }
  3373.             OS_error();
  3374.           }
  3375.         if (ergebnis==0)
  3376.           # kein Zeichen verfⁿgbar -> EOF erkennen
  3377.           #if TERMINAL_LINEBUFFERED
  3378.           if (TheArray(TheStream(stream)->strm_terminal_inbuff)->dims[1] > 0)
  3379.             goto restart_it; # Zeichen des Buffers liefern, dann erst eof_value liefern
  3380.             else
  3381.           #endif
  3382.             { TheStream(stream)->strm_rd_ch_last = eof_value; return eof_value; }
  3383.        }
  3384.        #if TERMINAL_LINEBUFFERED
  3385.        # Zeichen c zur Eingabezeile dazunehmen, evtl. die Zeile vergr÷▀ern:
  3386.        ssstring_push_extend(TheStream(stream)->strm_terminal_inbuff,c);
  3387.        stream = *stream_;
  3388.        #endif
  3389.        # Wenn stdin und stdout beide dasselbe Terminal sind,
  3390.        # und wir lesen ein NL, so k÷nnen wir davon ausgehen,
  3391.        # da▀ der Cursor danach in Spalte 0 steht.
  3392.        if (c==NL)
  3393.          { if (eq(TheStream(stream)->strm_terminal_isatty,S(equal)))
  3394.              { TheStream(stream)->strm_wr_ch_lpos = Fixnum_0; }
  3395.          }
  3396.        #if TERMINAL_LINEBUFFERED
  3397.          else
  3398.          goto continue_line; # so lang weiterlesen, bis ein NL kommt...
  3399.        # Kam ein NL, so fangen wir an, die Zeichen des Buffers zu liefern:
  3400.        goto restart_it;
  3401.        #endif
  3402.       }
  3403.     }}
  3404.  
  3405. # Stellt fest, ob ein Terminal-Stream ein Zeichen verfⁿgbar hat.
  3406. # listen_terminal2(stream)
  3407. # > stream: Terminal-Stream
  3408. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  3409. #             -1 falls bei EOF angelangt,
  3410. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  3411.   local signean listen_terminal2 (object stream);
  3412.   local signean listen_terminal2(stream)
  3413.     var reg1 object stream;
  3414.     { if (eq(TheStream(stream)->strm_rd_ch_last,eof_value)) # schon EOF ?
  3415.         { return signean_minus; }
  3416.       if (posfixnum_to_L(TheStream(stream)->strm_terminal_index)
  3417.           < TheArray(TheStream(stream)->strm_terminal_inbuff)->dims[1]
  3418.          )
  3419.         # index<count -> Es sind noch Zeichen im Buffer
  3420.         { return signean_null; }
  3421.       return listen_handle(stream);
  3422.     }
  3423.  
  3424. # UP: L÷scht bereits eingegebenen interaktiven Input von einem Terminal-Stream.
  3425. # clear_input_terminal2(stream);
  3426. # > stream: Terminal-Stream
  3427. # < ergebnis: TRUE falls Input gel÷scht wurde, FALSE sonst
  3428.   local boolean clear_input_terminal2 (object stream);
  3429.   local boolean clear_input_terminal2(stream)
  3430.     var reg1 object stream;
  3431.     { if (nullp(TheStream(stream)->strm_terminal_isatty))
  3432.         # File -> nichts tun
  3433.         { return FALSE; }
  3434.       # Terminal
  3435.       TheStream(stream)->strm_rd_ch_last = NIL; # gewesenes EOF vergessen
  3436.       #if TERMINAL_LINEBUFFERED
  3437.       TheStream(stream)->strm_terminal_index = Fixnum_0; # index := 0
  3438.       TheArray(TheStream(stream)->strm_terminal_inbuff)->dims[1] = 0; # count := 0
  3439.       #endif
  3440.       clear_tty_input(stdin_handle);
  3441.       pushSTACK(stream);
  3442.       while (listen_terminal2(STACK_0) == 0) { read_char(&STACK_0); }
  3443.       skipSTACK(1);
  3444.       return TRUE;
  3445.     }
  3446.  
  3447. # UP: Ein Zeichen auf einen Terminal-Stream ausgeben.
  3448. # wr_ch_terminal2(&stream,ch);
  3449. # > stream: Terminal-Stream
  3450. # > ch: auszugebendes Zeichen
  3451.   #define wr_ch_terminal2  wr_ch_handle
  3452.  
  3453. #ifdef STRM_WR_SS
  3454. # UP: Mehrere Zeichen auf einen Terminal-Stream ausgeben.
  3455. # wr_ss_terminal2(&stream,string,start,len);
  3456. # > stream: Terminal-Stream
  3457. # > string: Simple-String
  3458. # > start: Startindex
  3459. # > len: Anzahl der auszugebenden Zeichen
  3460.   #define wr_ss_terminal2  wr_ss_handle
  3461. #endif
  3462.  
  3463. # UP: L÷scht den wartenden Output eines Terminal-Stream.
  3464. # clear_output_terminal2(stream);
  3465. # > stream: Terminal-Stream
  3466. # kann GC ausl÷sen
  3467.   #define clear_output_terminal2  clear_output_handle
  3468.  
  3469. #endif # HAVE_TERMINAL2
  3470.  
  3471. #ifdef HAVE_TERMINAL3
  3472.  
  3473. #define TERMINAL_LINEBUFFERED  TRUE
  3474. #define TERMINAL_OUTBUFFERED   TRUE
  3475.  
  3476. # Unsere eigene VervollstΣndigungs-Funktion, imitiert completion_matches()
  3477. # aus readline.c.
  3478.   local char** lisp_completion (char* text, int start, int end);
  3479.   local boolean want_filename_completion;
  3480.   extern char* filename_completion_function (char* text, int state); # siehe readline.c
  3481.   local char** lisp_completion(text,start,end)
  3482.     var reg7 char* text; # text[0..end-start-1] = the_line[start..end-1]
  3483.     var reg8 int start;
  3484.     var reg9 int end;
  3485.     { # Dies ist eine Callback-Funktion, wir mⁿssen den Stack wieder korrekt setzen:
  3486.       begin_callback();
  3487.       if ((start>=2) && (rl_line_buffer[start-2]=='#') && (rl_line_buffer[start-1]== '\"'))
  3488.         # VervollstΣndigung nach #" bezieht sich auf Filenamen:
  3489.         { want_filename_completion = TRUE; return NULL; }
  3490.       # (SYS::COMPLETION text start end) aufrufen:
  3491.       pushSTACK(asciz_to_string(rl_line_buffer));
  3492.       pushSTACK(fixnum((uintL)start));
  3493.       pushSTACK(fixnum((uintL)end));
  3494.       funcall(S(completion),3);
  3495.       want_filename_completion = FALSE;
  3496.       end_callback();
  3497.      {var reg4 object mlist = value1; # Liste der M÷glichkeiten
  3498.       # Liste von Simple-Strings in mallozierten Array von mallozierten
  3499.       # Asciz-Strings umbauen:
  3500.       if (nullp(mlist)) { return NULL; }
  3501.       {var reg6 char** array = malloc((llength(mlist)+1)*sizeof(char*));
  3502.        if (array==NULL) { return NULL; }
  3503.        {var reg5 char** ptr = array;
  3504.         while (consp(mlist))
  3505.           { var reg3 uintC count = TheSstring(Car(mlist))->length;
  3506.             var reg2 uintB* ptr1 = &TheSstring(Car(mlist))->data[0];
  3507.             var reg1 char* ptr2 = malloc((count+1)*sizeof(char));
  3508.             if (ptr2==NULL) # malloc scheitert -> alles zurⁿckgeben
  3509.               { until (ptr==array) { free(*--ptr); }
  3510.                 free(array);
  3511.                 return NULL;
  3512.               }
  3513.             *ptr++ = ptr2;
  3514.             dotimesC(count,count, { *ptr2++ = *ptr1++; });
  3515.             *ptr2 = '\0';
  3516.             mlist = Cdr(mlist);
  3517.           }
  3518.         *ptr = NULL;
  3519.        }
  3520.        return array;
  3521.     }}}
  3522.  
  3523. # Falls obige Funktion NULL (keine Matches) lieferte, wird die folgende
  3524. # Funktion so lange aufgerufen, bis sie ihrerseits NULL liefert.
  3525.   local char* lisp_completion_more (char* text, int state);
  3526.   local char* lisp_completion_more(text,state)
  3527.     var reg2 char* text;
  3528.     var reg1 int state;
  3529.     { if (want_filename_completion)
  3530.         { return filename_completion_function(text,state); }
  3531.         else
  3532.         { return NULL; }
  3533.     }
  3534.  
  3535. # Lesen eines Zeichens von einem Terminal-Stream.
  3536.   local object rd_ch_terminal3 (object* stream_);
  3537.   # vgl. rd_ch_handle() :
  3538.   local object rd_ch_terminal3(stream_)
  3539.     var reg3 object* stream_;
  3540.     { restart_it:
  3541.      {var reg2 object stream = *stream_;
  3542.       if (eq(TheStream(stream)->strm_rd_ch_last,eof_value)) # schon EOF ?
  3543.         { return eof_value; }
  3544.       #if TERMINAL_LINEBUFFERED
  3545.       { var reg4 object inbuff = TheStream(stream)->strm_terminal_inbuff; # Eingabebuffer
  3546.         if (posfixnum_to_L(TheStream(stream)->strm_terminal_index)
  3547.             < TheArray(inbuff)->dims[1]
  3548.            )
  3549.           # index<count -> Es sind noch Zeichen im Buffer
  3550.           { var reg1 uintL index =
  3551.               posfixnum_to_L(TheStream(stream)->strm_terminal_index); # Index
  3552.             TheStream(stream)->strm_terminal_index =
  3553.               fixnum_inc(TheStream(stream)->strm_terminal_index,1); # Index erh÷hen
  3554.             return code_char(TheSstring(TheArray(inbuff)->data)->data[index]); # nΣchstes Character
  3555.           }
  3556.         # index=count -> mu▀ eine ganze Zeile von Tastatur lesen:
  3557.         TheStream(stream)->strm_terminal_index = Fixnum_0; # index := 0
  3558.         TheArray(inbuff)->dims[1] = 0; # count := 0
  3559.       }
  3560.       #endif
  3561.       { var reg5 char* prompt; # Prompt: letzte bisher ausgegebene Zeile
  3562.        {var reg6 object lastline = string_to_asciz(TheStream(*stream_)->strm_terminal_outbuff);
  3563.         prompt = (char*) malloc(TheSstring(lastline)->length+1);
  3564.         if (!(prompt==NULL))
  3565.           { strcpy(prompt,TheAsciz(lastline));
  3566.             # Die readline()-Library arbeitet mit einer anderen Bildschirmbreite,
  3567.             # als sie bei der Ausgabe des Prompts benutzt wurde. Bei Prompts
  3568.             # lΣnger als eine Bildschirmzeile gibt das Probleme. Wir behelfen
  3569.             # uns, indem wir an passender Stelle ein '\n' einfⁿgen.
  3570.             { var reg8 uintL prompt_length = asciz_length(prompt);
  3571.               var reg7 uintL screenwidth = posfixnum_to_L(Symbol_value(S(prin_linelength)))+1;
  3572.               if (prompt_length >= screenwidth)
  3573.                 { var reg4 uintL insertpos = round_down(prompt_length,screenwidth);
  3574.                   var reg1 uintL i;
  3575.                   for (i = prompt_length; i >= insertpos; i--)
  3576.                     { prompt[i+1] = prompt[i]; }
  3577.                   prompt[insertpos] = '\n';
  3578.        }  } }   }
  3579.        # Lexem-trennende Characters: die mit Syntaxcode whsp,tmac,nmac
  3580.        # (siehe IO.D, eigentlich von der Readtable abhΣngig):
  3581.        rl_basic_word_break_characters = "\t" NLstring " \"#'(),;`";
  3582.        rl_basic_quote_characters = "\"|";
  3583.        rl_completer_quote_characters = "\\|";
  3584.        run_time_stop(); # Run-Time-Stoppuhr anhalten
  3585.        #ifdef GRAPHICS_SWITCH
  3586.        switch_text_mode();
  3587.        #endif
  3588.        begin_call();
  3589.        rl_already_prompted = TRUE;
  3590.        {var reg4 uintB* line = (uintB*)readline(prompt==NULL ? "" : prompt); # Zeile lesen
  3591.         end_call();
  3592.         run_time_restart(); # Run-Time-Stoppuhr weiterlaufen lassen
  3593.         if (!(prompt==NULL)) { free(prompt); }
  3594.         if (line==NULL)
  3595.           # EOF (am Zeilenanfang) erkennen
  3596.           { TheStream(stream)->strm_rd_ch_last = eof_value; return eof_value; }
  3597.         # gelesene Zeile zur Eingabezeile dazunehmen:
  3598.         {var reg1 uintB* ptr = line;
  3599.          until (*ptr == '\0')
  3600.            { ssstring_push_extend(TheStream(*stream_)->strm_terminal_inbuff,*ptr++); }
  3601.          ssstring_push_extend(TheStream(*stream_)->strm_terminal_inbuff,NL);
  3602.         }
  3603.         # und in die History ⁿbernehmen, falls nicht leer:
  3604.         if (!(line[0]=='\0')) { add_history(line); }
  3605.         # Freigeben mⁿssen wir die Zeile!
  3606.         free(line);
  3607.       }}
  3608.       # Wenn stdin und stdout beide dasselbe Terminal sind, k÷nnen
  3609.       # wir davon ausgehen, da▀ der Cursor in Spalte 0 steht.
  3610.       if (eq(TheStream(*stream_)->strm_terminal_isatty,S(equal)))
  3611.         { TheStream(*stream_)->strm_wr_ch_lpos = Fixnum_0;
  3612.           TheArray(TheStream(*stream_)->strm_terminal_outbuff)->dims[1] = 0; # Fill-Pointer := 0
  3613.         }
  3614.       # Nun fangen wir an, die Zeichen des Buffers zu liefern:
  3615.       goto restart_it;
  3616.     }}
  3617.  
  3618. # Stellt fest, ob ein Terminal-Stream ein Zeichen verfⁿgbar hat.
  3619. # listen_terminal3(stream)
  3620. # > stream: Terminal-Stream
  3621. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  3622. #             -1 falls bei EOF angelangt,
  3623. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  3624.   local signean listen_terminal3 (object stream);
  3625.   local signean listen_terminal3(stream)
  3626.     var reg1 object stream;
  3627.     { if (eq(TheStream(stream)->strm_rd_ch_last,eof_value)) # schon EOF ?
  3628.         { return signean_minus; }
  3629.       if (posfixnum_to_L(TheStream(stream)->strm_terminal_index)
  3630.           < TheArray(TheStream(stream)->strm_terminal_inbuff)->dims[1]
  3631.          )
  3632.         # index<count -> Es sind noch Zeichen im Buffer
  3633.         { return signean_null; }
  3634.       return listen_handle(stream);
  3635.     }
  3636.  
  3637. # UP: L÷scht bereits eingegebenen interaktiven Input von einem Terminal-Stream.
  3638. # clear_input_terminal3(stream);
  3639. # > stream: Terminal-Stream
  3640. # < ergebnis: TRUE falls Input gel÷scht wurde, FALSE sonst
  3641.   local boolean clear_input_terminal3 (object stream);
  3642.   local boolean clear_input_terminal3(stream)
  3643.     var reg1 object stream;
  3644.     { if (nullp(TheStream(stream)->strm_terminal_isatty))
  3645.         # File -> nichts tun
  3646.         { return FALSE; }
  3647.       # Terminal
  3648.       TheStream(stream)->strm_rd_ch_last = NIL; # gewesenes EOF vergessen
  3649.       #if TERMINAL_LINEBUFFERED
  3650.       TheStream(stream)->strm_terminal_index = Fixnum_0; # index := 0
  3651.       TheArray(TheStream(stream)->strm_terminal_inbuff)->dims[1] = 0; # count := 0
  3652.       #endif
  3653.       clear_tty_input(stdin_handle);
  3654.       pushSTACK(stream);
  3655.       while (listen_terminal3(STACK_0) == 0) { read_char(&STACK_0); }
  3656.       skipSTACK(1);
  3657.       return TRUE;
  3658.     }
  3659.  
  3660. # UP: Ein Zeichen auf einen Terminal-Stream ausgeben.
  3661. # wr_ch_terminal3(&stream,ch);
  3662. # > stream: Terminal-Stream
  3663. # > ch: auszugebendes Zeichen
  3664.   local void wr_ch_terminal3 (object* stream_, object ch);
  3665.   local void wr_ch_terminal3(stream_,ch)
  3666.     var reg3 object* stream_;
  3667.     var reg1 object ch;
  3668.     { if (!string_char_p(ch)) { fehler_wr_string_char(*stream_,ch); } # ch sollte String-Char sein
  3669.      {var uintB c = char_code(ch); # Code des Zeichens
  3670.       #if TERMINAL_OUTBUFFERED
  3671.       if (c==NL)
  3672.         TheArray(TheStream(*stream_)->strm_terminal_outbuff)->dims[1] = 0; # Fill-Pointer := 0
  3673.         else
  3674.         ssstring_push_extend(TheStream(*stream_)->strm_terminal_outbuff,c);
  3675.       #endif
  3676.       restart_it:
  3677.       #ifdef GRAPHICS_SWITCH
  3678.       switch_text_mode();
  3679.       #endif
  3680.       begin_system_call();
  3681.       {var reg2 int ergebnis = write(stdout_handle,&c,1); # Zeichen auszugeben versuchen
  3682.        end_system_call();
  3683.        if (ergebnis<0)
  3684.          { if (errno==EINTR) # Unterbrechung (evtl. durch Ctrl-C) ?
  3685.              { interruptp({ pushSTACK(S(write_char)); tast_break(); }); # Break-Schleife aufrufen
  3686.                goto restart_it;
  3687.              }
  3688.            OS_error(); # Error melden
  3689.          }
  3690.        if (ergebnis==0) # nicht erfolgreich?
  3691.          { fehler_unwritable(S(write_char),*stream_); }
  3692.       }
  3693.     }}
  3694.  
  3695. #ifdef STRM_WR_SS
  3696. # UP: Mehrere Zeichen auf einen Terminal-Stream ausgeben.
  3697. # wr_ss_terminal3(&stream,string,start,len);
  3698. # > stream: Terminal-Stream
  3699. # > string: Simple-String
  3700. # > start: Startindex
  3701. # > len: Anzahl der auszugebenden Zeichen
  3702.   local void wr_ss_terminal3 (object* stream_, object string, uintL start, uintL len);
  3703.   local void wr_ss_terminal3(stream_,string,start,len)
  3704.     var reg6 object* stream_;
  3705.     var reg4 object string;
  3706.     var reg5 uintL start;
  3707.     var reg7 uintL len;
  3708.     { if (len==0) return;
  3709.       #ifdef GRAPHICS_SWITCH
  3710.       switch_text_mode();
  3711.       #endif
  3712.      {var reg1 uintB* ptr = &TheSstring(string)->data[start];
  3713.       var reg2 uintL remaining = len;
  3714.       loop
  3715.         { restart_it:
  3716.           begin_system_call();
  3717.          {var reg3 int ergebnis = write(stdout_handle,ptr,remaining); # Zeichen auszugeben versuchen
  3718.           end_system_call();
  3719.           if (ergebnis<0)
  3720.             { if (errno==EINTR) goto restart_it;
  3721.               OS_error(); # Error melden
  3722.             }
  3723.           if (ergebnis==0) # nicht erfolgreich?
  3724.             { fehler_unwritable(S(write_string),*stream_); }
  3725.           ptr += ergebnis; remaining -= ergebnis;
  3726.           if (remaining==0) break; # fertig?
  3727.         }}
  3728.       #if TERMINAL_OUTBUFFERED
  3729.       # Zeichen seit dem letzten NL in den Buffer:
  3730.       { var reg3 uintL pos = 0; # zΣhle die Zahl der Zeichen seit dem letzten NL
  3731.         var reg2 uintL count;
  3732.         dotimespL(count,len, { if (*--ptr == NL) goto found_NL; pos++; } );
  3733.         if (FALSE)
  3734.           found_NL: # pos Zeichen seit dem letzten NL
  3735.           { ptr++;
  3736.             TheArray(TheStream(*stream_)->strm_terminal_outbuff)->dims[1] = 0; # Fill-Pointer := 0
  3737.           }
  3738.         dotimesL(count,pos,
  3739.           ssstring_push_extend(TheStream(*stream_)->strm_terminal_outbuff,*ptr++);
  3740.           );
  3741.       }
  3742.       #endif
  3743.       wr_ss_lpos(*stream_,ptr,len); # Line-Position aktualisieren
  3744.     }}
  3745. #endif
  3746.  
  3747. # UP: L÷scht den wartenden Output eines Terminal-Stream.
  3748. # clear_output_terminal3(stream);
  3749. # > stream: Terminal-Stream
  3750. # kann GC ausl÷sen
  3751.   local void clear_output_terminal3 (object stream);
  3752.   local void clear_output_terminal3(stream)
  3753.     var reg1 object stream;
  3754.     { clear_output_handle(stream);
  3755.       #if TERMINAL_OUTBUFFERED
  3756.       TheArray(TheStream(stream)->strm_terminal_outbuff)->dims[1] = 0; # Fill-Pointer := 0
  3757.       #endif
  3758.     }
  3759.  
  3760. #endif # HAVE_TERMINAL3
  3761.  
  3762. # UP: Bringt den wartenden Output eines Terminal-Stream ans Ziel.
  3763. # finish_output_terminal(stream);
  3764. # > stream: Terminal-Stream
  3765. # kann GC ausl÷sen
  3766.   #define finish_output_terminal  finish_output_handle
  3767.  
  3768. # UP: Bringt den wartenden Output eines Terminal-Stream ans Ziel.
  3769. # force_output_terminal(stream);
  3770. # > stream: Terminal-Stream
  3771. # kann GC ausl÷sen
  3772.   #define force_output_terminal  force_output_handle
  3773.  
  3774. # Liefert einen interaktiven Terminal-Stream.
  3775. # kann GC ausl÷sen
  3776.   local object make_terminal_stream_ (void);
  3777.   local object make_terminal_stream_()
  3778.     {
  3779.      #ifdef AMIGAOS
  3780.       # nur HAVE_TERMINAL1
  3781.       { pushSTACK(allocate_handle(Output_handle));
  3782.         pushSTACK(allocate_handle(Input_handle));
  3783.        {var reg2 object stream =
  3784.           allocate_stream(strmflags_ch_B,strmtype_terminal,strm_terminal1_len);
  3785.         # Flags: nur READ-CHAR und WRITE-CHAR erlaubt
  3786.         # und fⁿllen:
  3787.         var reg1 Stream s = TheStream(stream);
  3788.           s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  3789.           s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  3790.           s->strm_rd_ch = P(rd_ch_terminal1); # READ-CHAR-Pseudofunktion
  3791.           s->strm_rd_ch_last = NIL; # Lastchar := NIL
  3792.           s->strm_wr_ch = P(wr_ch_terminal1); # WRITE-CHAR-Pseudofunktion
  3793.           s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  3794.           #ifdef STRM_WR_SS
  3795.           s->strm_wr_ss = P(wr_ss_terminal1);
  3796.           #endif
  3797.           begin_system_call();
  3798.           s->strm_terminal_isatty =
  3799.             (IsInteractive(Input_handle)
  3800.               ? (IsInteractive(Output_handle)
  3801.                   ? S(equal) # Input und Output Terminals -> vermutlich dasselbe
  3802.                   : T
  3803.                 )
  3804.               : NIL
  3805.             );
  3806.           end_system_call();
  3807.           s->strm_terminal_ihandle = popSTACK();
  3808.           s->strm_terminal_ohandle = popSTACK();
  3809.         return stream;
  3810.       }}
  3811.      #else
  3812.       { var reg7 int stdin_tty;
  3813.         var reg8 int stdout_tty;
  3814.         var reg6 int same_tty;
  3815.         begin_system_call();
  3816.         stdin_tty = isatty(stdin_handle); # stdin ein Terminal?
  3817.         stdout_tty = isatty(stdout_handle); # stdout ein Terminal?
  3818.         same_tty = FALSE; # vorlΣufig
  3819.         if (stdin_tty && stdout_tty)
  3820.           # stdin und stdout Terminals.
  3821.           {
  3822.            #if defined(UNIX) || defined(RISCOS)
  3823.             #if 0
  3824.             var reg1 char* ergebnis;
  3825.             var reg2 object filename;
  3826.             ergebnis = ttyname(stdin_handle); # Filename von stdin holen
  3827.             if (!(ergebnis==NULL))
  3828.               { end_system_call();
  3829.                 filename = asciz_to_string(ergebnis);
  3830.                 begin_system_call();
  3831.                 ergebnis = ttyname(stdout_handle); # Filename von stdout holen
  3832.                 if (!(ergebnis==NULL))
  3833.                   { end_system_call();
  3834.                     pushSTACK(filename);
  3835.                     filename = asciz_to_string(ergebnis);
  3836.                     if (string_gleich(popSTACK(),filename)) # gleiche Filenamen?
  3837.                       { same_tty = TRUE; }
  3838.               }   }
  3839.             #else # ttyname() ist recht langsam, fstat() geht schneller.
  3840.             struct stat stdin_stat;
  3841.             struct stat stdout_stat;
  3842.             if ((fstat(stdin_handle,&stdin_stat) >= 0) && (fstat(stdout_handle,&stdout_stat) >= 0))
  3843.               if ((stdin_stat.st_dev == stdout_stat.st_dev) && (stdin_stat.st_ino == stdout_stat.st_ino))
  3844.                 { same_tty = TRUE; }
  3845.             #endif
  3846.            #endif
  3847.            #ifdef MSDOS
  3848.             if (   ((get_handle_info(stdin_handle) & (bit(7)|bit(0))) == (bit(7)|bit(0))) # stdin == console_input ?
  3849.                 && ((get_handle_info(stdout_handle) & (bit(7)|bit(1))) == (bit(7)|bit(1))) # stdout == console_output ?
  3850.                )
  3851.               { same_tty = TRUE; }
  3852.            #endif
  3853.           }
  3854.         end_system_call();
  3855.         #ifdef HAVE_TERMINAL3
  3856.         if (rl_present_p && same_tty)
  3857.           # Baue einen TERMINAL3-Stream:
  3858.           { pushSTACK(make_ssstring(80)); # Zeilenbuffer allozieren
  3859.             pushSTACK(make_ssstring(80)); # Zeilenbuffer allozieren
  3860.             # neuen Stream allozieren:
  3861.            {var reg2 object stream =
  3862.               allocate_stream(strmflags_ch_B,strmtype_terminal,strm_terminal3_len);
  3863.               # Flags: nur READ-CHAR und WRITE-CHAR erlaubt
  3864.             # und fⁿllen:
  3865.             var reg1 Stream s = TheStream(stream);
  3866.               s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  3867.               s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  3868.               s->strm_rd_ch = P(rd_ch_terminal3); # READ-CHAR-Pseudofunktion
  3869.               s->strm_rd_ch_last = NIL; # Lastchar := NIL
  3870.               s->strm_wr_ch = P(wr_ch_terminal3); # WRITE-CHAR-Pseudofunktion
  3871.               s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  3872.               #ifdef STRM_WR_SS
  3873.               s->strm_wr_ss = P(wr_ss_terminal3);
  3874.               #endif
  3875.               s->strm_terminal_isatty = S(equal); # stdout=stdin
  3876.               s->strm_terminal_ihandle = allocate_handle(stdin_handle); # Handle fⁿr listen_handle()
  3877.               s->strm_terminal_ohandle = allocate_handle(stdout_handle); # Handle fⁿr Output
  3878.               #if 1 # TERMINAL_LINEBUFFERED
  3879.               s->strm_terminal_inbuff = popSTACK(); # Zeilenbuffer eintragen, count := 0
  3880.               s->strm_terminal_index = Fixnum_0; # index := 0
  3881.               #endif
  3882.               #if 1 # TERMINAL_OUTBUFFERED
  3883.               s->strm_terminal_outbuff = popSTACK(); # Zeilenbuffer eintragen
  3884.               #endif
  3885.             return stream;
  3886.           }}
  3887.         #endif
  3888.         #ifdef HAVE_TERMINAL2
  3889.         if (stdin_tty)
  3890.           # Baue einen TERMINAL2-Stream:
  3891.           { pushSTACK(make_ssstring(80)); # Zeilenbuffer allozieren
  3892.             # neuen Stream allozieren:
  3893.            {var reg2 object stream =
  3894.               allocate_stream(strmflags_ch_B,strmtype_terminal,strm_terminal2_len);
  3895.               # Flags: nur READ-CHAR und WRITE-CHAR erlaubt
  3896.             # und fⁿllen:
  3897.             var reg1 Stream s = TheStream(stream);
  3898.               s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  3899.               s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  3900.               s->strm_rd_ch = P(rd_ch_terminal2); # READ-CHAR-Pseudofunktion
  3901.               s->strm_rd_ch_last = NIL; # Lastchar := NIL
  3902.               s->strm_wr_ch = P(wr_ch_terminal2); # WRITE-CHAR-Pseudofunktion
  3903.               s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  3904.               #ifdef STRM_WR_SS
  3905.               s->strm_wr_ss = P(wr_ss_terminal2);
  3906.               #endif
  3907.               s->strm_terminal_isatty = (stdin_tty ? (same_tty ? S(equal) : T) : NIL);
  3908.               s->strm_terminal_ihandle = allocate_handle(stdin_handle); # Handle fⁿr listen_handle()
  3909.               s->strm_terminal_ohandle = allocate_handle(stdout_handle); # Handle fⁿr Output
  3910.               #if 1 # TERMINAL_LINEBUFFERED
  3911.               s->strm_terminal_inbuff = popSTACK(); # Zeilenbuffer eintragen, count := 0
  3912.               s->strm_terminal_index = Fixnum_0; # index := 0
  3913.               #endif
  3914.             return stream;
  3915.           }}
  3916.         #endif
  3917.         # Baue einen TERMINAL1-Stream:
  3918.         { # neuen Stream allozieren:
  3919.           var reg2 object stream =
  3920.             allocate_stream(strmflags_ch_B,strmtype_terminal,strm_terminal1_len);
  3921.             # Flags: nur READ-CHAR und WRITE-CHAR erlaubt
  3922.           # und fⁿllen:
  3923.           var reg1 Stream s = TheStream(stream);
  3924.             s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  3925.             s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  3926.             s->strm_rd_ch = P(rd_ch_terminal1); # READ-CHAR-Pseudofunktion
  3927.             s->strm_rd_ch_last = NIL; # Lastchar := NIL
  3928.             s->strm_wr_ch = P(wr_ch_terminal1); # WRITE-CHAR-Pseudofunktion
  3929.             s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  3930.             #ifdef STRM_WR_SS
  3931.             s->strm_wr_ss = P(wr_ss_terminal1);
  3932.             #endif
  3933.             s->strm_terminal_isatty = (stdin_tty ? (same_tty ? S(equal) : T) : NIL);
  3934.             s->strm_terminal_ihandle = allocate_handle(stdin_handle); # Handle fⁿr listen_handle()
  3935.             s->strm_terminal_ohandle = allocate_handle(stdout_handle); # Handle fⁿr Output
  3936.           return stream;
  3937.         }
  3938.       }
  3939.      #endif
  3940.     }
  3941.  
  3942. #if defined(UNIX) || defined(AMIGAOS) || defined(RISCOS)
  3943.  
  3944. # (SYS::TERMINAL-RAW *terminal-io* flag)
  3945. # flag /= NIL: versetzt das Terminal in cbreak/noecho-Modus,
  3946. # flag = NIL: versetzt das Terminal in nocbreak/echo-Modus zurⁿck.
  3947. # Liefert T falls erfolgreich, NIL wenn es nicht geht.
  3948.  
  3949. # (SYS::TERMINAL-RAW *terminal-io* t) entspricht im wesentlichen
  3950. # (progn
  3951. #   ; keine Editierm÷glichkeiten, kein Echo, keine CR<-->NL-Umwandlungen:
  3952. #   (shell "stty -icanon -echo -icrnl -inlcr")
  3953. #   ; nichts abfangen:
  3954. #   ;              C-S   C-Q      Del     C-U       C-W      C-R      C-O      C-V     C-Y     C-C     C-\      C-Q     C-S    C-D
  3955. #   (shell "stty -ixon -ixoff erase ^- kill ^- werase ^- rprnt ^- flush ^- lnext ^- susp ^- intr ^- quit ^- start ^- stop ^- eof ^-")
  3956. #   ; 1 Zeichen auf einmal verlangen (nicht 4!):
  3957. #   (shell "stty min 1") ; das mu▀ seltsamerweise zuletzt kommen...
  3958. # )
  3959. # (SYS::TERMINAL-RAW *terminal-io* nil) entspricht im wesentlichen
  3960. # (shell "stty sane")
  3961.  
  3962. local void term_raw (void);
  3963. local void term_unraw (void);
  3964.  
  3965. #if defined(UNIX) || defined(RISCOS)
  3966.  
  3967. local boolean oldterm_initialized = FALSE;
  3968.  
  3969. #if defined(UNIX_TERM_TERMIOS)
  3970.   local struct termios oldtermio; # ursprⁿnglicher TTY-Modus
  3971.   local void term_raw()
  3972.     { if (!oldterm_initialized)
  3973.         { if (!( tcgetattr(stdout_handle,&oldtermio) ==0))
  3974.             { if (!(errno==ENOTTY)) { OS_error(); } }
  3975.           oldterm_initialized = TRUE;
  3976.         }
  3977.      {var struct termios newtermio;
  3978.       newtermio = oldtermio;
  3979.       newtermio.c_iflag &= ( /* IXON|IXOFF|IXANY| */ ISTRIP|IGNBRK);
  3980.       /* newtermio.c_oflag &= ~OPOST; */ # Curses st÷rt sich dran!
  3981.       newtermio.c_lflag &= ISIG;
  3982.       { var reg1 uintC i;
  3983.         for (i=0; i<NCCS; i++) { newtermio.c_cc[i] = 0; }
  3984.       }
  3985.       newtermio.c_cc[VMIN] = 1;
  3986.       newtermio.c_cc[VTIME] = 0;
  3987.       if (!( TCSETATTR(stdout_handle,TCSAFLUSH,&newtermio) ==0))
  3988.         { if (!(errno==ENOTTY)) { OS_error(); } }
  3989.     }}
  3990.   local void term_unraw()
  3991.     { if (oldterm_initialized)
  3992.         { if (!( TCSETATTR(stdout_handle,TCSAFLUSH,&oldtermio) ==0))
  3993.             { if (!(errno==ENOTTY)) { OS_error(); } }
  3994.     }   }
  3995.   # Manche machen's so:
  3996.     # define crmode()    (_tty.c_lflag &=~ICANON,_tty.c_cc[VMIN]=1,tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
  3997.     # define nocrmode()  (_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,tcsetattr(_tty_ch, TCSAFLUSH,&_tty))
  3998.     # define echo()      (_tty.c_lflag |= ECHO, tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
  3999.     # define noecho()    (_tty.c_lflag &=~ECHO, tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
  4000.     # define nl()        (_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
  4001.     # define nonl()      (_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
  4002.     # define savetty()   (tcgetattr(_tty_ch, &_oldtty),tcgetattr(_tty_ch, &_tty))
  4003.     # define resetty()   (tcsetattr(_tty_ch, TCSAFLUSH, &_oldtty))
  4004. #elif defined(UNIX_TERM_TERMIO) || defined(EMUNIX)
  4005.   local struct termio oldtermio; # ursprⁿnglicher TTY-Modus
  4006.   local void term_raw()
  4007.     { if (!oldterm_initialized)
  4008.         { if (!( ioctl(stdout_handle,TCGETA,&oldtermio) ==0))
  4009.             { if (!(errno==ENOTTY)) { OS_error(); } }
  4010.           oldterm_initialized = TRUE;
  4011.         }
  4012.      {var struct termio newtermio;
  4013.       newtermio = oldtermio;
  4014.       newtermio.c_iflag &= ( /* IXON|IXOFF|IXANY| */ ISTRIP|IGNBRK);
  4015.       /* newtermio.c_oflag &= ~OPOST; */ # Curses st÷rt sich dran!
  4016.       newtermio.c_lflag &= ISIG;
  4017.       { var reg1 uintC i;
  4018.         for (i=0; i<NCCS; i++) { newtermio.c_cc[i] = 0; }
  4019.       }
  4020.       newtermio.c_cc[VMIN] = 1;
  4021.       newtermio.c_cc[VTIME] = 0;
  4022.       if (!( ioctl(stdout_handle,TCSETAF,&newtermio) ==0))
  4023.         { if (!(errno==ENOTTY)) { OS_error(); } }
  4024.     }}
  4025.   local void term_unraw()
  4026.     { if (oldterm_initialized)
  4027.         { if (!( ioctl(stdout_handle,TCSETAF,&oldtermio) ==0))
  4028.             { if (!(errno==ENOTTY)) { OS_error(); } }
  4029.     }   }
  4030.   # Manche machen's so:
  4031.     # define crmode()    (_tty.c_lflag &=~ICANON,_tty.c_cc[VMIN] = 1,ioctl(_tty_ch,TCSETAF,&_tty))
  4032.     # define nocrmode()  (_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,stty(_tty_ch,&_tty))
  4033.     # define echo()      (_tty.c_lflag |= ECHO, ioctl(_tty_ch, TCSETA, &_tty))
  4034.     # define noecho()    (_tty.c_lflag &=~ECHO, ioctl(_tty_ch, TCSETA, &_tty))
  4035.     # define nl()        (_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,ioctl(_tty_ch, TCSETAW, &_tty))
  4036.     # define nonl()      (_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,ioctl(_tty_ch, TCSETAW, &_tty))
  4037. #elif defined(UNIX_TERM_SGTTY)
  4038.   local struct sgttyb oldsgttyb; # ursprⁿnglicher TTY-Modus
  4039.   local struct tchars oldtchars; # ursprⁿngliche Steuerzeichen
  4040.   #ifdef TIOCSLTC
  4041.   local struct ltchars oldltchars; # ursprⁿngliche Editierzeichen
  4042.   #endif
  4043.   local void term_raw()
  4044.     { if (!oldterm_initialized)
  4045.         { if (!( ioctl(stdout_handle,TIOCGETP,&oldsgttyb) ==0))
  4046.             { if (!(errno==ENOTTY)) { OS_error(); } }
  4047.           if (!( ioctl(stdout_handle,TIOCGETC,&oldtchars) ==0))
  4048.             { if (!(errno==ENOTTY)) { OS_error(); } }
  4049.           #ifdef TIOCSLTC
  4050.           if (!( ioctl(stdout_handle,TIOCGLTC,&oldltchars) ==0))
  4051.             { if (!(errno==ENOTTY)) { OS_error(); } }
  4052.           #endif
  4053.           oldterm_initialized = TRUE;
  4054.         }
  4055.      {var struct sgttyb newsgttyb;
  4056.       newsgttyb = oldsgttyb;
  4057.       newsgttyb.sg_flags |= CBREAK;
  4058.       newsgttyb.sg_flags &= ~(CRMOD|ECHO|XTABS);
  4059.       if (!( ioctl(stdout_handle,TIOCSETP,&newsgttyb) ==0))
  4060.         { if (!(errno==ENOTTY)) { OS_error(); } }
  4061.      }
  4062.      {var struct tchars newtchars;
  4063.       local var union { char a [sizeof(struct tchars)];
  4064.                         struct tchars b;
  4065.                       }
  4066.                 zero_tchars = {{0,}};
  4067.       newtchars = zero_tchars.b;
  4068.       if (!( ioctl(stdout_handle,TIOCSETC,&newtchars) ==0))
  4069.         { if (!(errno==ENOTTY)) { OS_error(); } }
  4070.      }
  4071.      #ifdef TIOCSLTC
  4072.      {var struct ltchars newltchars;
  4073.       local var union { char a [sizeof(struct ltchars)];
  4074.                         struct ltchars b;
  4075.                       }
  4076.                 zero_ltchars = {{0,}};
  4077.       newltchars = zero_ltchars.b;
  4078.       if (!( ioctl(stdout_handle,TIOCSLTC,&newltchars) ==0))
  4079.         { if (!(errno==ENOTTY)) { OS_error(); } }
  4080.      }
  4081.      #endif
  4082.     }
  4083.   local void term_unraw()
  4084.     { if (oldterm_initialized)
  4085.         { if (!( ioctl(stdout_handle,TIOCSETP,&oldsgttyb) ==0))
  4086.             { if (!(errno==ENOTTY)) { OS_error(); } }
  4087.           if (!( ioctl(stdout_handle,TIOCSETC,&oldtchars) ==0))
  4088.             { if (!(errno==ENOTTY)) { OS_error(); } }
  4089.           #ifdef TIOCSLTC
  4090.           if (!( ioctl(stdout_handle,TIOCSLTC,&oldltchars) ==0))
  4091.             { if (!(errno==ENOTTY)) { OS_error(); } }
  4092.           #endif
  4093.     }   }
  4094.   # Manche machen's so:
  4095.     # define raw()       (_tty.sg_flags|=RAW, stty(_tty_ch,&_tty))
  4096.     # define noraw()     (_tty.sg_flags&=~RAW,stty(_tty_ch,&_tty))
  4097.     # define crmode()    (_tty.sg_flags |= CBREAK, stty(_tty_ch,&_tty))
  4098.     # define nocrmode()  (_tty.sg_flags &= ~CBREAK,stty(_tty_ch,&_tty))
  4099.     # define echo()      (_tty.sg_flags |= ECHO, stty(_tty_ch, &_tty))
  4100.     # define noecho()    (_tty.sg_flags &= ~ECHO, stty(_tty_ch, &_tty))
  4101.     # define nl()        (_tty.sg_flags |= CRMOD,stty(_tty_ch, &_tty))
  4102.     # define nonl()      (_tty.sg_flags &= ~CRMOD, stty(_tty_ch, &_tty))
  4103.     # define savetty()   (gtty(_tty_ch, &_tty), _res_flg = _tty.sg_flags)
  4104.     # define resetty()   (_tty.sg_flags = _res_flg, stty(_tty_ch, &_tty))
  4105. #endif
  4106.  
  4107. # Wir speichern, ob zuletzt term_raw() oder term_unraw() ausgefⁿhrt wurde,
  4108. # damit wir bei Programm-Ausstieg zurⁿckschalten k÷nnen.
  4109. local boolean terminal_raw = FALSE;
  4110.  
  4111. global void terminal_sane (void);
  4112. global void terminal_sane()
  4113.   { if (terminal_raw) { term_unraw(); terminal_raw = FALSE; } }
  4114.  
  4115. LISPFUNN(terminal_raw,2)
  4116.   { var reg2 object flag = popSTACK();
  4117.     var reg1 object stream = popSTACK();
  4118.     if (!streamp(stream)) { fehler_stream(stream); }
  4119.     if (TheStream(stream)->strmtype == strmtype_terminal)
  4120.       # der Terminal-Stream
  4121.       { if (!nullp(TheStream(stream)->strm_terminal_isatty))
  4122.           # Terminal
  4123.           { begin_system_call();
  4124.             if (!nullp(flag))
  4125.               # Umschalten in cbreak/noecho-Modus:
  4126.               { term_raw(); terminal_raw = TRUE; }
  4127.               else
  4128.               # Umschalten in nocbreak/echo-Modus:
  4129.               { term_unraw(); terminal_raw = FALSE; }
  4130.             end_system_call();
  4131.       }   }
  4132.     value1 = T; mv_count=1;
  4133.   }
  4134.  
  4135. #endif # UNIX || RISCOS
  4136.  
  4137. #ifdef AMIGAOS
  4138.  
  4139. # include <exec/types.h>
  4140. # include <dos/dosextens.h>
  4141. # include <inline/exec.h>
  4142. # include <inline/dos.h>
  4143.  
  4144. # From Ralph Babel, The Amiga GURU book, p. 278
  4145. # SetMode() for pre-2.0 systems
  4146.  
  4147. local LONG setmode (BPTR fh, LONG mode);
  4148. local LONG setmode(fh,mode)
  4149.   var BPTR fh;
  4150.   var LONG mode;
  4151.   { if (DOSBase->dl_lib.lib_Version > 35)
  4152.       { return SetMode(fh,mode); }
  4153.       else
  4154.       # pre-2.0, no SetMode in the library
  4155.       { register struct MsgPort *fh_type = ((struct FileHandle *)BADDR(fh))->fh_Type;
  4156.         if (fh_type==NULL)
  4157.           return DOSFALSE; /* NIL: has no message port */
  4158.        {var LONGALIGNTYPE(struct StandardPacket) spb;
  4159.         var struct StandardPacket * sp = LONGALIGN(&spb);
  4160.         var struct MsgPort * mp = &((struct Process *)FindTask(NULL))->pr_MsgPort;
  4161.         sp->sp_Msg.mn_Node.ln_Name = (char *)&sp->sp_Pkt;
  4162.         sp->sp_Pkt.dp_Link         = &sp->sp_Msg;
  4163.         sp->sp_Pkt.dp_Port         = mp;
  4164.         sp->sp_Pkt.dp_Type         = ACTION_SCREEN_MODE;
  4165.         sp->sp_Pkt.dp_Arg1         = mode; /* 0 for CON */
  4166.         PutMsg(fh_type, &sp->sp_Msg);
  4167.         WaitPort(mp);
  4168.         GetMsg(mp);         /* assumes that no other packets are pending */
  4169.         return sp->sp_Pkt.dp_Res1;
  4170.   }   }}
  4171.  
  4172. # Genauso wie den Terminal-Stream k÷nnen wir auch beliebige interaktive
  4173. # Handle-Streams (andere Text-Fenster) in den Raw-Modus schalten.
  4174.  
  4175. # Beim Terminal-Stream speichern wir den momentanen Zustand (um so wenig wie
  4176. # m÷glich umschalten zu mⁿssen), bei den Handle-Streams wird das von screen.lsp
  4177. # ⁿbernommen.
  4178. local LONG terminal_mode = 0; # 0 = CON, 1 = RAW
  4179.  
  4180. global void terminal_sane (void);
  4181. global void terminal_sane()
  4182.   { if (!(terminal_mode == 0))
  4183.       { begin_system_call(); setmode(Input_handle,0); end_system_call();
  4184.         terminal_mode = 0;
  4185.   }   }
  4186.  
  4187. LISPFUNN(terminal_raw,2)
  4188.   { var reg4 object flag = popSTACK();
  4189.     var reg1 object stream = popSTACK();
  4190.     if (!streamp(stream)) { fehler_stream(stream); }
  4191.     if (!(TheStream(stream)->strmflags & strmflags_open_B)) # Stream geschlossen?
  4192.       { fehler_illegal_streamop(S(terminal_raw),stream); }
  4193.    {var reg3 LONG new_mode = (nullp(flag) ? 0 : 1);
  4194.     if ((TheStream(stream)->strmtype == strmtype_terminal) # der Terminal-Stream
  4195.         || (TheStream(stream)->strmtype == strmtype_handle) # ein File-Handle-Stream
  4196.        )
  4197.       { if (!nullp(TheStream(stream)->strm_isatty))
  4198.           { var reg2 LONG result;
  4199.             if (TheStream(stream)->strmtype == strmtype_terminal)
  4200.               # Terminal
  4201.               { if (new_mode == terminal_mode)
  4202.                   { result = TRUE; }
  4203.                   else
  4204.                   { begin_system_call();
  4205.                     result = setmode(Input_handle,new_mode);
  4206.                     end_system_call();
  4207.                     terminal_mode = new_mode;
  4208.               }   }
  4209.               else
  4210.               # Handle-Stream
  4211.               { begin_system_call();
  4212.                 result = setmode(TheHandle(TheStream(stream)->strm_ihandle),new_mode);
  4213.                 end_system_call();
  4214.               }
  4215.             value1 = (result ? T : NIL);
  4216.           }
  4217.           else
  4218.           { value1 = T; }
  4219.       }
  4220.       else
  4221.       { value1 = NIL; }
  4222.     mv_count=1;
  4223.   }}
  4224.  
  4225. #endif # AMIGAOS
  4226.  
  4227. #endif # UNIX || AMIGAOS || RISCOS
  4228.  
  4229. #endif # UNIX || (MSDOS && !WINDOWS) || AMIGAOS || RISCOS
  4230.  
  4231. # Liefert einen interaktiven Terminal-Stream.
  4232. # kann GC ausl÷sen
  4233.   local object make_terminal_stream (void);
  4234.   local object make_terminal_stream()
  4235.     { var reg2 object stream = make_terminal_stream_();
  4236.       # Liste der offenen Streams um stream erweitern:
  4237.       pushSTACK(stream);
  4238.       {var reg1 object new_cons = allocate_cons();
  4239.        Car(new_cons) = stream = popSTACK();
  4240.        Cdr(new_cons) = O(open_files);
  4241.        O(open_files) = new_cons;
  4242.       }
  4243.       return stream;
  4244.     }
  4245.  
  4246.  
  4247. # Window-Stream
  4248. # =============
  4249.  
  4250. #ifdef SCREEN
  4251.  
  4252. # Editor-Unterstⁿtzung:
  4253. # ATARI: Direkt mit dem BIOS-VT52-Emulator.
  4254. # MSDOS: ▄bers BIOS.
  4255. # OS/2: Mit der Video-Library von Eberhard Mattes.
  4256. # CURSES: Ein Window-Stream ist im wesentlichen ein Curses-WINDOW.
  4257. #
  4258. # (SCREEN:MAKE-WINDOW)
  4259. #   liefert einen Window-Stream. Solange bis dieser wieder geschlossen wird,
  4260. #   ist das Terminal im cbreak-noecho-Modus; weitere Ein-/Ausgabe ⁿber
  4261. #   *terminal-io* sollte in dieser Zeit nicht erfolgen.
  4262. #
  4263. # (SCREEN:WINDOW-SIZE window-stream)
  4264. #   liefert die Gr÷▀e des Windows,
  4265. #   als 2 Werte: H÷he (= Ymax+1), Breite (= Xmax+1).
  4266. #
  4267. # (SCREEN:WINDOW-CURSOR-POSITION window-stream)
  4268. #   liefert die Position des Cursors im Window
  4269. #   als 2 Werte: Zeile (>=0, <=Ymax, 0=oben), Spalte (>=0, <=Xmax, 0=links).
  4270. #
  4271. # (SCREEN:SET-WINDOW-CURSOR-POSITION window-stream line column)
  4272. #   setzt die Position des Cursors im Window.
  4273. #
  4274. # (SCREEN:CLEAR-WINDOW window-stream)
  4275. #   l÷scht den Inhalt des Window und setzt den Cursor an die linke obere Ecke
  4276. #
  4277. # (SCREEN:CLEAR-WINDOW-TO-EOT window-stream)
  4278. #   l÷scht den Inhalt des Window ab Cursor-Position bis Bildschirmende
  4279. #
  4280. # (SCREEN:CLEAR-WINDOW-TO-EOL window-stream)
  4281. #   l÷scht den Inhalt des Window ab Cursor-Position bis Zeilenende
  4282. #
  4283. # (SCREEN:DELETE-WINDOW-LINE window-stream)
  4284. #   l÷scht die Cursorzeile, schiebt die Zeilen drunter um 1 nach oben
  4285. #   und l÷scht die letzte Bildschirmzeile.
  4286. #
  4287. # (SCREEN:INSERT-WINDOW-LINE window-stream)
  4288. #   fⁿgt in der Zeile des Cursors eine neue Zeile ein und schiebt dabei alle
  4289. #   Zeilen ab der Cursorzeile um 1 nach unten.
  4290. #
  4291. # (SCREEN:HIGHLIGHT-ON window-stream)
  4292. #   schaltet "hervorgehobene" Ausgabe ein.
  4293. #
  4294. # (SCREEN:HIGHLIGHT-OFF window-stream)
  4295. #   schaltet "hervorgehobene" Ausgabe wieder aus.
  4296. #
  4297. # (SCREEN:WINDOW-CURSOR-ON window-stream)
  4298. #   macht den Cursor(block) sichtbar.
  4299. #
  4300. # (SCREEN:WINDOW-CURSOR-OFF window-stream)
  4301. #   macht den Cursor(block) wieder unsichtbar.
  4302.  
  4303. # ▄berprⁿft, ob das Argument ein Window-Stream ist.
  4304.   local void check_window_stream (object stream);
  4305.   local void check_window_stream(stream)
  4306.     var reg1 object stream;
  4307.     { if (streamp(stream)
  4308.           && (TheStream(stream)->strmtype == strmtype_window)
  4309.          )
  4310.         return;
  4311.       pushSTACK(stream);
  4312.       pushSTACK(TheSubr(subr_self)->name);
  4313.       fehler(error,
  4314.              DEUTSCH ? "~: Argument ~ sollte ein Window-Stream sein." :
  4315.              ENGLISH ? "~: argument ~ should be a window stream" :
  4316.              FRANCAIS ? "~ : L'argument ~ devrait Ωtre un WINDOW-STREAM." :
  4317.              ""
  4318.             );
  4319.     }
  4320.  
  4321. #ifdef ATARI
  4322.  
  4323. # Terminal-Emulation:
  4324. # VT52 mit einigen Erweiterungen,
  4325. # vgl. TERMCAP-EintrΣge "vt52", "atari st", "heathkit 19"
  4326.  
  4327. # UP: Ein Zeichen auf einen Window-Stream ausgeben.
  4328. # wr_ch_window(&stream,ch);
  4329. # > stream: Window-Stream
  4330. # > ch: auszugebendes Zeichen
  4331.   local void wr_ch_window (object* stream_, object ch);
  4332.   local void wr_ch_window(stream_,ch)
  4333.     var reg2 object* stream_;
  4334.     var reg3 object ch;
  4335.     { if (!string_char_p(ch)) { fehler_wr_string_char(*stream_,ch); } # ch sollte String-Char sein
  4336.      {var reg1 uintB c = char_code(ch); # Code des Zeichens
  4337.       # Code c ⁿbers BIOS auf den Bildschirm ausgeben:
  4338.       # c >= ' ' -> ⁿbers GEMDOS
  4339.       # 0 <= c < 32 -> genau c in {1,..,4}u{14,..,25}u{28,..,31} ⁿbers BIOS
  4340.       #                sonst als Steuerzeichen ⁿbers BIOS
  4341.       if ((c < ' ') &&
  4342.           # Bit c aus der 32-Bit-Zahl %11110011111111111100000000011110 holen:
  4343.           (0xF3FFC01EUL & bit(c))
  4344.          )
  4345.         { BIOS_GrConOut(c); }
  4346.         else
  4347.         { BIOS_ConOut(c); }
  4348.     }}
  4349.  
  4350. LISPFUNN(make_window,0)
  4351.   { finish_output_terminal(var_stream(S(terminal_io))); # evtl. wartendes NL jetzt ausgeben
  4352.    {var reg2 object stream =
  4353.       allocate_stream(strmflags_wr_ch_B,strmtype_window,strm_len+0);
  4354.       # Flags: nur WRITE-CHAR erlaubt
  4355.     # und fⁿllen:
  4356.     var reg1 Stream s = TheStream(stream);
  4357.       s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  4358.       s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  4359.       s->strm_rd_ch = P(rd_ch_dummy); # READ-CHAR unm÷glich
  4360.       s->strm_rd_ch_last = NIL; # Lastchar := NIL
  4361.       s->strm_wr_ch = P(wr_ch_window); # WRITE-CHAR-Pseudofunktion
  4362.       s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  4363.       #ifdef STRM_WR_SS
  4364.       s->strm_wr_ss = P(wr_ss_dummy_nogc);
  4365.       #endif
  4366.     set_break_sem_1();
  4367.     BIOS_ConOut(ESC); BIOS_ConOut('w'); # Wrap off
  4368.     BIOS_ConOut(ESC); BIOS_ConOut('q'); # Reverse off
  4369.     clr_break_sem_1();
  4370.     value1 = stream; mv_count=1;
  4371.   }}
  4372.  
  4373. # Schlie▀t einen Window-Stream.
  4374.   local void close_window (object stream);
  4375.   local void close_window(stream)
  4376.     var reg1 object stream;
  4377.     { set_break_sem_1();
  4378.       BIOS_ConOut(ESC); BIOS_ConOut('v'); # Wrap on
  4379.       BIOS_ConOut(ESC); BIOS_ConOut('q'); # Reverse off
  4380.       clr_break_sem_1();
  4381.     }
  4382.  
  4383. LISPFUNN(window_size,1)
  4384.   { check_window_stream(popSTACK());
  4385.     value1 = fixnum(window_size.y);
  4386.     value2 = fixnum(window_size.x);
  4387.     mv_count=2;
  4388.   }
  4389.  
  4390. LISPFUNN(window_cursor_position,1)
  4391.   { check_window_stream(popSTACK());
  4392.     value1 = fixnum((UWORD)(vdiesc.v_cur_y));
  4393.     value2 = fixnum((UWORD)(vdiesc.v_cur_x));
  4394.     mv_count=2;
  4395.   }
  4396.  
  4397. LISPFUNN(set_window_cursor_position,3)
  4398.   { check_window_stream(STACK_2);
  4399.    {var reg2 uintL line = posfixnum_to_L(STACK_1);
  4400.     var reg3 uintL column = posfixnum_to_L(STACK_0);
  4401.     if ((line < (uintL)window_size.y) && (column < (uintL)window_size.x))
  4402.       { set_break_sem_1();
  4403.         BIOS_ConOut(ESC); BIOS_ConOut('Y'); BIOS_ConOut(32+line); BIOS_ConOut(32+column);
  4404.         clr_break_sem_1();
  4405.       }
  4406.     value1 = STACK_1; value2 = STACK_0; mv_count=2; skipSTACK(3);
  4407.   }}
  4408.  
  4409. LISPFUNN(clear_window,1)
  4410.   { check_window_stream(popSTACK());
  4411.     set_break_sem_1();
  4412.     BIOS_ConOut(ESC); BIOS_ConOut('E');
  4413.     clr_break_sem_1();
  4414.     value1 = NIL; mv_count=0;
  4415.   }
  4416.  
  4417. LISPFUNN(clear_window_to_eot,1)
  4418.   { check_window_stream(popSTACK());
  4419.     set_break_sem_1();
  4420.     BIOS_ConOut(ESC); BIOS_ConOut('J');
  4421.     clr_break_sem_1();
  4422.     value1 = NIL; mv_count=0;
  4423.   }
  4424.  
  4425. LISPFUNN(clear_window_to_eol,1)
  4426.   { check_window_stream(popSTACK());
  4427.     set_break_sem_1();
  4428.     BIOS_ConOut(ESC); BIOS_ConOut('K');
  4429.     clr_break_sem_1();
  4430.     value1 = NIL; mv_count=0;
  4431.   }
  4432.  
  4433. LISPFUNN(delete_window_line,1)
  4434.   { check_window_stream(popSTACK());
  4435.     set_break_sem_1();
  4436.     BIOS_ConOut(ESC); BIOS_ConOut('M');
  4437.     clr_break_sem_1();
  4438.     value1 = NIL; mv_count=0;
  4439.   }
  4440.  
  4441. LISPFUNN(insert_window_line,1)
  4442.   { check_window_stream(popSTACK());
  4443.     set_break_sem_1();
  4444.     BIOS_ConOut(ESC); BIOS_ConOut('L');
  4445.     clr_break_sem_1();
  4446.     value1 = NIL; mv_count=0;
  4447.   }
  4448.  
  4449. LISPFUNN(highlight_on,1)
  4450.   { check_window_stream(popSTACK());
  4451.     set_break_sem_1();
  4452.     BIOS_ConOut(ESC); BIOS_ConOut('p'); # Reverse on
  4453.     clr_break_sem_1();
  4454.     value1 = NIL; mv_count=0;
  4455.   }
  4456.  
  4457. LISPFUNN(highlight_off,1)
  4458.   { check_window_stream(popSTACK());
  4459.     set_break_sem_1();
  4460.     BIOS_ConOut(ESC); BIOS_ConOut('q'); # Reverse off
  4461.     clr_break_sem_1();
  4462.     value1 = NIL; mv_count=0;
  4463.   }
  4464.  
  4465. LISPFUNN(window_cursor_on,1)
  4466.   { check_window_stream(popSTACK());
  4467.     set_break_sem_1();
  4468.     BIOS_ConOut(ESC); BIOS_ConOut('e'); # Cursor on
  4469.     clr_break_sem_1();
  4470.     value1 = NIL; mv_count=0;
  4471.   }
  4472.  
  4473. LISPFUNN(window_cursor_off,1)
  4474.   { check_window_stream(popSTACK());
  4475.     set_break_sem_1();
  4476.     BIOS_ConOut(ESC); BIOS_ConOut('f'); # Cursor off
  4477.     clr_break_sem_1();
  4478.     value1 = NIL; mv_count=0;
  4479.   }
  4480.  
  4481. #endif # ATARI
  4482.  
  4483. #if defined(MSDOS) && !defined(EMUNIX_PORTABEL) && !defined(WINDOWS)
  4484.  
  4485. # Aus der Distribution von ELVIS 1.4, File PC.C :
  4486.  
  4487. # Author:
  4488. #      Guntram Blohm
  4489. #      Buchenstra▀e 19
  4490. #      W 7904 Erbach
  4491. #      Germany
  4492. #      Tel. ++49-7305-6997
  4493. #      sorry - no regular network connection
  4494.  
  4495. # This file implements the ibm pc bios interface. See IBM documentation
  4496. # for details.
  4497. # If TERM is set upon invocation of CLISP, this code is ignored completely,
  4498. # and the standard termcap functions are used, thus, even not-so-close
  4499. # compatibles can run CLISP. For close compatibles however, bios output
  4500. # is much faster (and permits reverse scrolling, adding and deleting lines,
  4501. # and much more ansi.sys isn't capable of). GB.
  4502.  
  4503. local uintL screentype; # 0 = monochrome, 1 = color
  4504.  
  4505. local uintW screenattr; # screen attribute index
  4506.  
  4507. # Documentation of attributes:
  4508. # bit 7    : foreground character blinking,
  4509. # bit 6..4 : background color,
  4510. # bit 3    : foreground intensity,
  4511. # bit 2..0 : foreground color,
  4512. # color table:
  4513. #   0 black, 1 blue, 2 green, 3 cyan, 4 red, 5 magenta, 6 brown, 7 lightgray,
  4514. # and as foreground color with intensity bit set, it is light:
  4515. #   8 darkgray, ..., E yelloe, F white.
  4516.   #define col_black    0  # schwarz
  4517.   #define col_blue     1  # blau
  4518.   #define col_green    2  # grⁿn
  4519.   #define col_cyan     3  # blaugrⁿn
  4520.   #define col_red      4  # rot
  4521.   #define col_magenta  5  # lila
  4522.   #define col_brown    6  # braun
  4523.   #define col_white    7  # wei▀
  4524.   #define col_light(x)  (8 | x)  # hell
  4525.   #define FG(x)  x         # foreground color
  4526.   #define BG(x)  (x << 4)  # background color
  4527. local uintB attr_table[2][5] =
  4528.   { # monochrome:
  4529.     { /* no standout   */  BG(col_black) | FG(col_white),
  4530.       /* standout      */  BG(col_white) | FG(col_black),
  4531.       /* visible bell  */  BG(col_black) | FG(col_light(col_white)),
  4532.       /* underline     */  BG(col_black) | FG(1), # underline
  4533.       /* alt. char set */  BG(col_black) | FG(col_light(col_white)),
  4534.     },
  4535.     # color:
  4536.     { /* no standout   */  BG(col_blue) | FG(col_light(col_white)),
  4537.       /* standout      */  BG(col_blue) | FG(col_light(col_magenta)),
  4538.       /* visible bell  */  BG(col_blue) | FG(col_light(col_brown)),
  4539.       /* underline     */  BG(col_blue) | FG(col_light(col_green)),
  4540.       /* alt. char set */  BG(col_blue) | FG(col_light(col_red)),
  4541.     },
  4542.   };
  4543. local uintB attr; # = attr_table[screentype][screenattr];
  4544.  
  4545. # INT 10 documentation:
  4546. #   INT 10,01 - Set cursor type
  4547. #   INT 10,02 - Set cursor position
  4548. #   INT 10,03 - Read cursor position
  4549. #   INT 10,06 - Scroll active page up
  4550. #   INT 10,07 - Scroll active page down
  4551. #   INT 10,09 - Write character and attribute at cursor
  4552. #   INT 10,0E - Write text in teletype mode
  4553. #   INT 10,0F - Get current video state
  4554. #
  4555. # INT 10,01 - Set Cursor Type
  4556. #     AH = 01
  4557. #     CH = cursor starting scan line (cursor top) (low order 5 bits)
  4558. #     CL = cursor ending scan line (cursor bottom) (low order 5 bits)
  4559. #     returns nothing
  4560. #     - cursor scan lines are zero based
  4561. #     - the following is a list of the cursor scan lines associated with
  4562. #       most common adapters;  screen sizes over 40 lines may differ
  4563. #       depending on adapters.
  4564. #               Line     Starting     Ending      Character
  4565. #       Video   Count    Scan Line    Scan Line   Point Size
  4566. #       CGA      25         06           07           08
  4567. #       MDA      25         0B           0C           0E
  4568. #       EGA      25         06           07           0E
  4569. #       EGA      43       04/06          07           08
  4570. #       VGA      25         0D           0E           10
  4571. #       VGA      40         08           09           0A
  4572. #       VGA      50         06           07           08
  4573. #     - use CX = 2000h to disable cursor
  4574. #
  4575. # INT 10,02 - Set Cursor Position
  4576. #     AH = 02
  4577. #     BH = page number (0 for graphics modes)
  4578. #     DH = row
  4579. #     DL = column
  4580. #     returns nothing
  4581. #     - positions relative to 0,0 origin
  4582. #     - 80x25 uses coordinates 0,0 to 24,79;  40x25 uses 0,0 to 24,39
  4583. #
  4584. # INT 10,03 - Read Cursor Position and Size
  4585. #     AH = 03
  4586. #     BH = video page
  4587. #     on return:
  4588. #     CH = cursor starting scan line (low order 5 bits)
  4589. #     CL = cursor ending scan line (low order 5 bits)
  4590. #     DH = row
  4591. #     DL = column
  4592. #
  4593. # INT 10,06 - Scroll Window Up
  4594. #     AH = 06
  4595. #     AL = number of lines to scroll, previous lines are
  4596. #          blanked, if 0 or AL > screen size, window is blanked
  4597. #     BH = attribute to be used on blank line
  4598. #     CH = row of upper left corner of scroll window
  4599. #     CL = column of upper left corner of scroll window
  4600. #     DH = row of lower right corner of scroll window
  4601. #     DL = column of lower right corner of scroll window
  4602. #     returns nothing
  4603. #     - in video mode 4 (300x200 4 color) on the EGA, MCGA and VGA
  4604. #       this function scrolls page 0 regardless of the current page
  4605. #
  4606. # INT 10,07 - Scroll Window Down
  4607. #     AH = 07
  4608. #     AL = number of lines to scroll, previous lines are
  4609. #          blanked, if 0 or AL > screen size, window is blanked
  4610. #     BH = attribute to be used on blank line
  4611. #     CH = row of upper left corner of scroll window
  4612. #     CL = column of upper left corner of scroll window
  4613. #     DH = row of lower right corner of scroll window
  4614. #     DL = column of lower right corner of scroll window
  4615. #     returns nothing
  4616. #     - in video mode 4 (300x200 4 color) on the EGA, MCGA and VGA
  4617. #       this function scrolls page 0 regardless of the current page
  4618. #
  4619. # INT 10,09 - Write Character and Attribute at Cursor Position
  4620. #     AH = 09
  4621. #     AL = ASCII character to write
  4622. #     BH = display page  (or mode 13h, background pixel value)
  4623. #     BL = character attribute (text) foreground color (graphics)
  4624. #     CX = count of characters to write (CX >= 1)
  4625. #     returns nothing
  4626. #     - does not move the cursor
  4627. #     - in graphics mode (except mode 13h), if BL bit 7=1 then
  4628. #       value of BL is XOR'ed with the background color
  4629. #
  4630. # INT 10,0E - Write Text in Teletype Mode
  4631. #     AH = 0E
  4632. #     AL = ASCII character to write
  4633. #     BH = page number (text modes)
  4634. #     BL = foreground pixel color (graphics modes)
  4635. #     returns nothing
  4636. #     - cursor advances after write
  4637. #     - characters BEL (7), BS (8), LF (A), and CR (D) are
  4638. #       treated as control codes
  4639. #     - for some older BIOS (10/19/81), the BH register must point
  4640. #       to the currently displayed page
  4641. #     - on CGA adapters this function can disable the video signal while
  4642. #       performing the output which causes flitter.
  4643. #
  4644. # INT 10,0F - Get Video State
  4645. #     AH = 0F
  4646. #     on return:
  4647. #     AH = number of screen columns
  4648. #     AL = mode currently set (see ~VIDEO MODES~)
  4649. #     BH = current display page
  4650. #     - video modes greater than 13h on EGA, MCGA and VGA indicate
  4651. #       ~INT 10,0~ was called with the high bit of the mode (AL) set
  4652. #       to 1, meaning the display does not need cleared
  4653.  
  4654. # low-level BIOS interface
  4655.  
  4656. #if defined(DJUNIX) || defined(WATCOM)
  4657.   #define intvideo(in_ptr,out_ptr)  int86(0x10,in_ptr,out_ptr)
  4658. #endif
  4659. #ifdef EMUNIX
  4660.   local void intvideo (union REGS * in_regs, union REGS * out_regs);
  4661.   local void intvideo(in_regs,out_regs)
  4662.     var register union REGS * in_regs;
  4663.     var register union REGS * out_regs;
  4664.     { __asm__ __volatile__ ( "movl 0(%%esi),%%eax ; "
  4665.                              "movl 4(%%esi),%%ebx ; "
  4666.                              "movl 8(%%esi),%%ecx ; "
  4667.                              "movl 12(%%esi),%%edx ; "
  4668.                              "pushl %%edi ; "
  4669.                              ".byte 0xcd ; .byte 0x10 ; "
  4670.                              "popl %%edi ; "
  4671.                              "movl %%eax,0(%%edi) ; "
  4672.                              "movl %%ebx,4(%%edi) ; "
  4673.                              "movl %%ecx,8(%%edi) ; "
  4674.                              "movl %%edx,12(%%edi)"
  4675.                              :                                                         # OUT
  4676.                              : "S" /* %esi */ (in_regs), "D" /* %edi */ (out_regs)     # IN
  4677.                              : "ax","bx","cx","si","di" /* %eax,%ebx,%ecx,%esi,%edi */ # CLOBBER
  4678.                            );
  4679.     }
  4680. #endif
  4681.  
  4682. local void video (uintW ax, uintW* cx, uintW* dx);
  4683. local void video(ax,cx,dx)
  4684.   var reg1 uintW ax;
  4685.   var reg1 uintW* cx;
  4686.   var reg1 uintW* dx;
  4687.   { var union REGS in;
  4688.     var union REGS out;
  4689.     in.regW.ax = ax;
  4690.     { var uintB ah = in.regB.ah;
  4691.       if (ah==0x06 || ah==0x07)
  4692.         { in.regB.bh = attr; }
  4693.         else
  4694.         { in.regB.bh = 0; # "active page"
  4695.           if (ah==0x09 || ah==0x0e) { in.regB.bl = attr; }
  4696.     }   }
  4697.     if (cx) { in.regW.cx = *cx; }
  4698.     if (dx) { in.regW.dx = *dx; }
  4699.     begin_system_call();
  4700.     intvideo(&in,&out);
  4701.     end_system_call();
  4702.     if (dx) { *dx = out.regW.dx; }
  4703.     if (cx) { *cx = out.regW.cx; }
  4704.   }
  4705.  
  4706. global uintW v_cols()
  4707.   { # determine number of screen columns. Also set screentype according
  4708.     # to monochrome/color screen.
  4709.     var union REGS in;
  4710.     var union REGS out;
  4711.     in.regB.ah=0x0f;
  4712.     intvideo(&in,&out); # INT 10,0F : get current video state
  4713.    {var reg1 uintB videomode = out.regB.al & 0x7f;
  4714.     # Text modes are 0,1,2,3,7, and others (depending on the graphics card).
  4715.     # Only modes 0 and 7 are mono. (Well, mode 2 is gray shaded.)
  4716.     screentype = (((videomode==0) || (videomode==7))
  4717.                   ? 0 # monochrome
  4718.                   : 1 # color
  4719.                  );
  4720.     return out.regB.ah;
  4721.   }}
  4722.  
  4723. local uintW v_rows()
  4724.   { # Getting the number of rows is hard. Most screens support 25 only,
  4725.     # EGA/VGA also support 43/50 lines, and some OEM's even more.
  4726.     # Unfortunately, there is no standard bios variable for the number
  4727.     # of lines, and the bios screen memory size is always rounded up
  4728.     # to 0x1000. So, we'll really have to cheat.
  4729.     # When using the screen memory size, keep in mind that each character
  4730.     # byte has an associated attribute byte.
  4731.     # uses:        word at 40:4c contains  memory size
  4732.     #              byte at 40:84           # of rows-1 (sometimes)
  4733.     #              byte at 40:4a           # of columns
  4734.     #if 0 # cannot execute 8086 code!
  4735.     # screen size less then 4K? then we have 25 lines only
  4736.     if (*(uintW far *)(0x0040004CL)<=4096)
  4737.       return 25;
  4738.     # VEGA vga uses the bios byte at 0x40:0x84 for # of rows.
  4739.     # Use that byte, if it makes sense together with memory size.
  4740.     if ((((*(uintB far *)(0x0040004AL)*2*(*(uintB far *)(0x00400084L)+1))
  4741.           +0xfff
  4742.          )
  4743.          &(~0xfff)
  4744.         )
  4745.         == *(uintW far *)(0x0040004CL)
  4746.        )
  4747.       return *(uintB far *)(0x00400084L)+1;
  4748.     #endif
  4749.     # uh oh. Emit LFs until screen starts scrolling.
  4750.     { var uintW line;
  4751.       var uintW oldline = 0;
  4752.       video(0x0200,NULL,&oldline); # INT 10,02 : set cursor position to (0,0)
  4753.       loop
  4754.         { video(0x0e0a,NULL,NULL); # INT 10,0E : write LF in teletype mode
  4755.           video(0x0300,NULL,&line); # INT 10,03 : read cursor position
  4756.           line>>=8;
  4757.           if (oldline==line) { return line+1; }
  4758.           oldline = line;
  4759.   } }   }
  4760.  
  4761. # High-level BIOS interface
  4762.  
  4763. local uintW LINES;
  4764. local uintW COLS;
  4765.  
  4766. void v_up()
  4767.   { # cursor up: determine current position, decrement row, set position
  4768.     var uintW dx;
  4769.     video(0x0300,NULL,&dx); # INT 10,03 : read cursor position
  4770.     dx -= 0x100;
  4771.     video(0x0200,NULL,&dx); # INT 10,02 : set cursor position
  4772.   }
  4773.  
  4774. #if 1
  4775.  
  4776. void v_cb()
  4777.   { # cursor big: set begin scan to end scan - 4
  4778.     var uintW cx;
  4779.     video(0x0300,&cx,NULL); # INT 10,03 : read cursor position
  4780.     cx=((cx&0xff)|(((cx&0xff)-4)<<8));
  4781.     video(0x0100,&cx,NULL); # INT 10,01 : set cursor type
  4782.   }
  4783.  
  4784. void v_cs()
  4785.   { # cursor small: set begin scan to end scan - 1
  4786.     var uintW cx;
  4787.     video(0x0300,&cx,NULL); # INT 10,03 : read cursor position
  4788.     cx=((cx&0xff)|(((cx&0xff)-1)<<8));
  4789.     video(0x0100,&cx,NULL); # INT 10,01 : set cursor type
  4790.   }
  4791.  
  4792. #endif
  4793.  
  4794. void v_ce()
  4795.   { # clear to end: get cursor position and emit the aproppriate number
  4796.     # of spaces, without moving cursor.
  4797.     var uintW cx;
  4798.     var uintW dx;
  4799.     video(0x0300,NULL,&dx); # INT 10,03 : read cursor position
  4800.     cx = COLS - (dx&0xff);
  4801.     video(0x0920,&cx,NULL); # INT 10,09 : write character at cursor, cx times 0x20
  4802.   }
  4803.  
  4804. void v_cl()
  4805.   { # clear screen: clear all and set cursor home
  4806.     var uintW cx = 0;
  4807.     var uintW dx = ((LINES-1)<<8)+(COLS-1);
  4808.     video(0x0600,&cx,&dx); # INT 10,06 : scroll active page up
  4809.     dx = 0;
  4810.     video(0x0200,&cx,&dx); # INT 10,02 : set cursor position
  4811.   }
  4812.  
  4813. void v_cd()
  4814.   { # clear to bottom: get position, clear to eol, clear next line to end
  4815.     var uintW cx;
  4816.     var uintW dx;
  4817.     var uintW dxtmp;
  4818.     video(0x0300,NULL,&dx); # INT 10,03 : read cursor position
  4819.     dxtmp = (dx&0xff00)|(COLS-1);
  4820.     cx = dx;
  4821.     video(0x0600,&cx,&dxtmp); # INT 10,06 : scroll active page up
  4822.     cx = (dx&0xff00)+0x100;
  4823.     dx = ((LINES-1)<<8)+(COLS-1);
  4824.     video(0x0600,&cx,&dx); # INT 10,06 : scroll active page up
  4825.   }
  4826.  
  4827. void v_al()
  4828.   { # add line: scroll rest of screen down
  4829.     var uintW cx;
  4830.     var uintW dx;
  4831.     video(0x0300,NULL,&dx); # INT 10,03 : read cursor position
  4832.     cx = (dx&0xff00);
  4833.     dx = ((LINES-1)<<8)+(COLS-1);
  4834.     video(0x0701,&cx,&dx); # INT 10,06 : scroll active page down
  4835.   }
  4836.  
  4837. void v_dl()
  4838.   { # delete line: scroll rest up
  4839.     var uintW cx;
  4840.     var uintW dx;
  4841.     video(0x0300,NULL,&dx); # INT 10,03 : read cursor position
  4842.     cx = (dx&0xff00) /* + 0x100 */ ;
  4843.     dx = ((LINES-1)<<8)+(COLS-1);
  4844.     video(0x0601,&cx,&dx); # INT 10,06 : scroll active page up
  4845.   }
  4846.  
  4847. void v_sr()
  4848.   { # scroll reverse: scroll whole screen
  4849.     var uintW cx = 0;
  4850.     var uintW dx = ((LINES-1)<<8)+(COLS-1);
  4851.     video(0x0701,&cx,&dx); # INT 10,06 : scroll active page down
  4852.   }
  4853.  
  4854. void v_move(y,x)
  4855.   var uintW y;
  4856.   var uintW x;
  4857.   { # set cursor
  4858.     var uintW dx = (y<<8)+x;
  4859.     video(0x0200,NULL,&dx); # INT 10,02 : set cursor position
  4860.   }
  4861.  
  4862. uintW v_put(ch)
  4863.   var uintW ch;
  4864.   { # put character:
  4865.     # put attribute and char (no scroll!), then update cursor position.
  4866.     var uintW cx=1;
  4867.     ch &= 0xff;
  4868.     if (ch==NL)
  4869.       { video(0x0e00|CR,NULL,NULL); # INT 10,0E : write in teletype mode
  4870.         video(0x0e00|LF,NULL,NULL); # INT 10,0E : write in teletype mode
  4871.       }
  4872.       else
  4873.       { video(0x0900|ch,&cx,NULL); # INT 10,09 : write character at cursor
  4874.        {# cursor right: determine current position, increment column, set position
  4875.         var uintW dx;
  4876.         video(0x0300,NULL,&dx); # INT 10,03 : read cursor position
  4877.         dx += 0x1; # increment column
  4878.         if ((dx & 0xff) == COLS) # at right margin?
  4879.           { dx &= 0xff00; # set column to 0
  4880.             dx += 0x100; # increment row
  4881.             if ((dx >> 8) == LINES) # at bottom margin?
  4882.               goto no_scroll; # do not scroll at right bottom corner!!
  4883.           }
  4884.         video(0x0200,NULL,&dx); # INT 10,02 : set cursor position
  4885.         no_scroll: ;
  4886.       }}
  4887.     return ch;
  4888.   }
  4889.  
  4890. # Lisp-Funktionen:
  4891.  
  4892. # UP: Ein Zeichen auf einen Window-Stream ausgeben.
  4893. # wr_ch_window(&stream,ch);
  4894. # > stream: Window-Stream
  4895. # > ch: auszugebendes Zeichen
  4896.   local void wr_ch_window (object* stream_, object ch);
  4897.   local void wr_ch_window(stream_,ch)
  4898.     var reg2 object* stream_;
  4899.     var reg3 object ch;
  4900.     { if (!string_char_p(ch)) { fehler_wr_string_char(*stream_,ch); } # ch sollte String-Char sein
  4901.      {var reg1 uintB c = char_code(ch); # Code des Zeichens
  4902.       # Code c ⁿbers BIOS auf den Bildschirm ausgeben:
  4903.       v_put(c);
  4904.     }}
  4905.  
  4906. LISPFUNN(make_window,0)
  4907.   { var reg2 object stream =
  4908.       allocate_stream(strmflags_wr_ch_B,strmtype_window,strm_len+0);
  4909.       # Flags: nur WRITE-CHAR erlaubt
  4910.     # und fⁿllen:
  4911.     var reg1 Stream s = TheStream(stream);
  4912.       s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  4913.       s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  4914.       s->strm_rd_ch = P(rd_ch_dummy); # READ-CHAR unm÷glich
  4915.       s->strm_rd_ch_last = NIL; # Lastchar := NIL
  4916.       s->strm_wr_ch = P(wr_ch_window); # WRITE-CHAR-Pseudofunktion
  4917.       s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  4918.       #ifdef STRM_WR_SS
  4919.       s->strm_wr_ss = P(wr_ss_dummy_nogc);
  4920.       #endif
  4921.     LINES = v_rows(); COLS = v_cols();
  4922.     screenattr = 0; attr = attr_table[screentype][screenattr];
  4923.     v_cs();
  4924.     value1 = stream; mv_count=1;
  4925.   }
  4926.  
  4927. # Schlie▀t einen Window-Stream.
  4928.   local void close_window (object stream);
  4929.   local void close_window(stream)
  4930.     var reg1 object stream;
  4931.     { v_cs();
  4932.       attr = BG(col_black) | FG(col_white); v_cl(); # clear screen black
  4933.     }
  4934.  
  4935. LISPFUNN(window_size,1)
  4936.   { check_window_stream(popSTACK());
  4937.     value1 = fixnum(LINES);
  4938.     value2 = fixnum(COLS);
  4939.     mv_count=2;
  4940.   }
  4941.  
  4942. LISPFUNN(window_cursor_position,1)
  4943.   { check_window_stream(popSTACK());
  4944.    {var uintW dx;
  4945.     video(0x0300,NULL,&dx); # INT 10,03 : read cursor position
  4946.     value1 = fixnum(dx>>8);
  4947.     value2 = fixnum(dx&0xff);
  4948.     mv_count=2;
  4949.   }}
  4950.  
  4951. LISPFUNN(set_window_cursor_position,3)
  4952.   { check_window_stream(STACK_2);
  4953.    {var reg2 uintL line = posfixnum_to_L(STACK_1);
  4954.     var reg3 uintL column = posfixnum_to_L(STACK_0);
  4955.     if ((line < (uintL)LINES) && (column < (uintL)COLS))
  4956.       { v_move(line,column); }
  4957.     value1 = STACK_1; value2 = STACK_0; mv_count=2; skipSTACK(3);
  4958.   }}
  4959.  
  4960. LISPFUNN(clear_window,1)
  4961.   { check_window_stream(popSTACK());
  4962.     v_cl();
  4963.     value1 = NIL; mv_count=0;
  4964.   }
  4965.  
  4966. LISPFUNN(clear_window_to_eot,1)
  4967.   { check_window_stream(popSTACK());
  4968.     v_cd();
  4969.     value1 = NIL; mv_count=0;
  4970.   }
  4971.  
  4972. LISPFUNN(clear_window_to_eol,1)
  4973.   { check_window_stream(popSTACK());
  4974.     v_ce();
  4975.     value1 = NIL; mv_count=0;
  4976.   }
  4977.  
  4978. LISPFUNN(delete_window_line,1)
  4979.   { check_window_stream(popSTACK());
  4980.     v_dl();
  4981.     value1 = NIL; mv_count=0;
  4982.   }
  4983.  
  4984. LISPFUNN(insert_window_line,1)
  4985.   { check_window_stream(popSTACK());
  4986.     v_al();
  4987.     value1 = NIL; mv_count=0;
  4988.   }
  4989.  
  4990. LISPFUNN(highlight_on,1)
  4991.   { check_window_stream(popSTACK());
  4992.     screenattr = 1; attr = attr_table[screentype][screenattr];
  4993.     value1 = NIL; mv_count=0;
  4994.   }
  4995.  
  4996. LISPFUNN(highlight_off,1)
  4997.   { check_window_stream(popSTACK());
  4998.     screenattr = 0; attr = attr_table[screentype][screenattr];
  4999.     value1 = NIL; mv_count=0;
  5000.   }
  5001.  
  5002. LISPFUNN(window_cursor_on,1)
  5003.   { check_window_stream(popSTACK());
  5004.     v_cb();
  5005.     value1 = NIL; mv_count=0;
  5006.   }
  5007.  
  5008. LISPFUNN(window_cursor_off,1)
  5009.   { check_window_stream(popSTACK());
  5010.     v_cs();
  5011.     value1 = NIL; mv_count=0;
  5012.   }
  5013.  
  5014. #endif # MSDOS && !EMUNIX_PORTABEL && !WINDOWS
  5015.  
  5016. #if defined(MSDOS) && (defined(EMUNIX_PORTABEL) && defined(EMUNIX_NEW_8f))
  5017.  
  5018. # Benutze die Video-Library von Eberhard Mattes.
  5019. # Vorzⁿge:
  5020. # - einfaches Interface,
  5021. # - ruft unter OS/2 die Vio-Funktionen auf, unter DOS wird der Bildschirm-
  5022. #   speicher direkt angesprochen (schnell!), falls einer der Standard-Textmodi
  5023. #   vorliegt, sonst wird das BIOS bemⁿht (portabel!).
  5024.  
  5025. local uintL screentype; # 0 = monochrome, 1 = color
  5026.  
  5027. local uintB attr_table[2][5] =
  5028.   { # monochrome:
  5029.     { /* no standout   */  BW_NORMAL,
  5030.       /* standout      */  BW_REVERSE,
  5031.       /* visible bell  */  BW_NORMAL | INTENSITY,
  5032.       /* underline     */  BW_UNDERLINE,
  5033.       /* alt. char set */  BW_NORMAL | INTENSITY,
  5034.     },
  5035.     # color:
  5036.     { /* no standout   */  B_BLUE | F_WHITE | INTENSITY,
  5037.       /* standout      */  B_BLUE | F_MAGENTA | INTENSITY,
  5038.       /* visible bell  */  B_BLUE | F_BROWN | INTENSITY,
  5039.       /* underline     */  B_BLUE | F_GREEN | INTENSITY,
  5040.       /* alt. char set */  B_BLUE | F_RED | INTENSITY,
  5041.     },
  5042.   };
  5043.  
  5044. local int cursor_scanlines_start;
  5045. local int cursor_scanlines_end;
  5046.  
  5047. local int LINES; # Anzahl Zeilen
  5048. local int COLS;  # Anzahl Spalten, Anzahl Zeichen pro Zeile
  5049.  
  5050. # UP: Ein Zeichen auf einen Window-Stream ausgeben.
  5051. # wr_ch_window(&stream,ch);
  5052. # > stream: Window-Stream
  5053. # > ch: auszugebendes Zeichen
  5054.   local void wr_ch_window (object* stream_, object ch);
  5055.   local void wr_ch_window(stream_,ch)
  5056.     var reg2 object* stream_;
  5057.     var reg3 object ch;
  5058.     { if (!string_char_p(ch)) { fehler_wr_string_char(*stream_,ch); } # ch sollte String-Char sein
  5059.      {var reg1 uintB c = char_code(ch); # Code des Zeichens
  5060.       # Code c ⁿber die Video-Library auf den Bildschirm ausgeben:
  5061.       if (c==NL)
  5062.         { v_putc(c); }
  5063.         else
  5064.         { var int current_x;
  5065.           var int current_y;
  5066.           v_getxy(¤t_x,¤t_y); # get current cursor position
  5067.           if ((current_x==COLS-1) && (current_y==LINES-1))
  5068.             { v_putn(c,1); } # do not scroll at right bottom corner!!
  5069.             else
  5070.             { v_putc(c); }
  5071.         }
  5072.     }}
  5073.  
  5074. LISPFUNN(make_window,0)
  5075.   { var reg2 object stream =
  5076.       allocate_stream(strmflags_wr_ch_B,strmtype_window,strm_len+0);
  5077.       # Flags: nur WRITE-CHAR erlaubt
  5078.     # und fⁿllen:
  5079.     var reg1 Stream s = TheStream(stream);
  5080.       s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  5081.       s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  5082.       s->strm_rd_ch = P(rd_ch_dummy); # READ-CHAR unm÷glich
  5083.       s->strm_rd_ch_last = NIL; # Lastchar := NIL
  5084.       s->strm_wr_ch = P(wr_ch_window); # WRITE-CHAR-Pseudofunktion
  5085.       s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  5086.       #ifdef STRM_WR_SS
  5087.       s->strm_wr_ss = P(wr_ss_dummy_nogc);
  5088.       #endif
  5089.     v_init(); # Initialisieren
  5090.     #if 1
  5091.     screentype = (v_hardware()==V_MONOCHROME ? 0 : 1); # Bildschirmtyp abfragen
  5092.     #else
  5093.     videomode abfragen wie in vinit.c, dann
  5094.     screentype = (((videomode==0) || (videomode==7))
  5095.                   ? 0 # monochrome
  5096.                   : 1 # color
  5097.                  );
  5098.     #endif
  5099.     v_dimen(&COLS,&LINES); # Bildschirmgr÷▀e abfragen
  5100.     v_getctype(&cursor_scanlines_start,&cursor_scanlines_end); # Cursorform abfragen
  5101.     v_attrib(attr_table[screentype][0]); # Highlight off
  5102.     v_ctype(cursor_scanlines_end-1,cursor_scanlines_end); # cursor small
  5103.     value1 = stream; mv_count=1;
  5104.   }
  5105.  
  5106. # Schlie▀t einen Window-Stream.
  5107.   local void close_window (object stream);
  5108.   local void close_window(stream)
  5109.     var reg1 object stream;
  5110.     { v_gotoxy(0,0); # Cursor home
  5111.       v_attrib(screentype==0 ? BW_NORMAL : (B_BLACK | F_WHITE));
  5112.       v_putn(' ',LINES*COLS); # Bildschirm l÷schen
  5113.       v_ctype(cursor_scanlines_start,cursor_scanlines_end); # Cursorform zurⁿcksetzen
  5114.     }
  5115.  
  5116. LISPFUNN(window_size,1)
  5117.   { check_window_stream(popSTACK());
  5118.     value1 = fixnum((uintW)LINES);
  5119.     value2 = fixnum((uintW)COLS);
  5120.     mv_count=2;
  5121.   }
  5122.  
  5123. LISPFUNN(window_cursor_position,1)
  5124.   { check_window_stream(popSTACK());
  5125.    {var int current_x;
  5126.     var int current_y;
  5127.     v_getxy(¤t_x,¤t_y); # get current cursor position
  5128.     value1 = fixnum((uintW)current_y);
  5129.     value2 = fixnum((uintW)current_x);
  5130.     mv_count=2;
  5131.   }}
  5132.  
  5133. LISPFUNN(set_window_cursor_position,3)
  5134.   { check_window_stream(STACK_2);
  5135.    {var reg2 uintL line = posfixnum_to_L(STACK_1);
  5136.     var reg3 uintL column = posfixnum_to_L(STACK_0);
  5137.     if ((line < (uintL)LINES) && (column < (uintL)COLS))
  5138.       { v_gotoxy((int)column,(int)line); }
  5139.     value1 = STACK_1; value2 = STACK_0; mv_count=2; skipSTACK(3);
  5140.   }}
  5141.  
  5142. LISPFUNN(clear_window,1)
  5143.   { check_window_stream(popSTACK());
  5144.     v_gotoxy(0,0);
  5145.     #ifdef EMUNIX_NEW_9a
  5146.     v_clear();
  5147.     #else # v_clear() funktioniert bei emx <= 0.8h nicht
  5148.     v_putn(' ',LINES*COLS);
  5149.     #endif
  5150.     value1 = NIL; mv_count=0;
  5151.   }
  5152.  
  5153. LISPFUNN(clear_window_to_eot,1)
  5154.   { check_window_stream(popSTACK());
  5155.    {var int current_x;
  5156.     var int current_y;
  5157.     v_getxy(¤t_x,¤t_y); # get current cursor position
  5158.     v_putn(' ',COLS*(LINES-current_y)-current_x);
  5159.     value1 = NIL; mv_count=0;
  5160.   }}
  5161.  
  5162. LISPFUNN(clear_window_to_eol,1)
  5163.   { check_window_stream(popSTACK());
  5164.     v_clreol();
  5165.     value1 = NIL; mv_count=0;
  5166.   }
  5167.  
  5168. LISPFUNN(delete_window_line,1)
  5169.   { check_window_stream(popSTACK());
  5170.     #ifdef EMUNIX_NEW_8g
  5171.     v_delline(1);
  5172.     #else # Bug in EMX 0.8f umgehen
  5173.     {var int current_x;
  5174.      var int current_y;
  5175.      v_getxy(¤t_x,¤t_y); # get current cursor position
  5176.      v_scroll(0,current_y,COLS-1,LINES-1,1,V_SCROLL_UP);
  5177.     }
  5178.     #endif
  5179.     value1 = NIL; mv_count=0;
  5180.   }
  5181.  
  5182. LISPFUNN(insert_window_line,1)
  5183.   { check_window_stream(popSTACK());
  5184.     v_insline(1);
  5185.     value1 = NIL; mv_count=0;
  5186.   }
  5187.  
  5188. LISPFUNN(highlight_on,1)
  5189.   { check_window_stream(popSTACK());
  5190.     v_attrib(attr_table[screentype][1]);
  5191.     value1 = NIL; mv_count=0;
  5192.   }
  5193.  
  5194. LISPFUNN(highlight_off,1)
  5195.   { check_window_stream(popSTACK());
  5196.     v_attrib(attr_table[screentype][0]);
  5197.     value1 = NIL; mv_count=0;
  5198.   }
  5199.  
  5200. LISPFUNN(window_cursor_on,1)
  5201.   { check_window_stream(popSTACK());
  5202.     # cursor big: set begin scan to end scan - 4
  5203.     v_ctype(cursor_scanlines_end-4,cursor_scanlines_end);
  5204.     value1 = NIL; mv_count=0;
  5205.   }
  5206.  
  5207. LISPFUNN(window_cursor_off,1)
  5208.   { check_window_stream(popSTACK());
  5209.     # cursor small: set begin scan to end scan - 1
  5210.     v_ctype(cursor_scanlines_end-1,cursor_scanlines_end);
  5211.     value1 = NIL; mv_count=0;
  5212.   }
  5213.  
  5214. #endif # MSDOS && (EMUNIX_PORTABEL && EMUNIX_NEW_8f)
  5215.  
  5216. #if defined(MSDOS) && defined(WINDOWS)
  5217.  
  5218. # Benutze ein Text-Fenster, siehe wintext.d.
  5219. extern mywindow text_create (void);
  5220. extern void text_destroy (mywindow w);
  5221. extern void text_cursor_on (mywindow w);
  5222. extern void text_cursor_off (mywindow w);
  5223. extern void get_text_size (mywindow w, int* width, int* height);
  5224. extern void get_text_cursor_position (mywindow w, int* x, int* y);
  5225. extern void set_text_cursor_position (mywindow w, int x, int y);
  5226. extern void text_writechar (mywindow w, char c);
  5227. extern void text_clear (mywindow w);
  5228. extern void text_clear_to_eol (mywindow w);
  5229. extern void text_clear_to_eot (mywindow w);
  5230. extern void text_delete_line (mywindow w);
  5231. extern void text_insert_line (mywindow w);
  5232.  
  5233. #define strm_mywindow  strm_other[0]  # Maschinenpointer auf ein struct mywindow
  5234.  
  5235. # UP: Ein Zeichen auf einen Window-Stream ausgeben.
  5236. # wr_ch_window(&stream,ch);
  5237. # > stream: Window-Stream
  5238. # > ch: auszugebendes Zeichen
  5239.   local void wr_ch_window (object* stream_, object ch);
  5240.   local void wr_ch_window(stream_,ch)
  5241.     var reg2 object* stream_;
  5242.     var reg3 object ch;
  5243.     { if (!string_char_p(ch)) { fehler_wr_string_char(*stream_,ch); } # ch sollte String-Char sein
  5244.      {var reg1 uintB c = char_code(ch); # Code des Zeichens
  5245.       var reg4 mywindow w = (mywindow)TheMachine(TheStream(*stream_)->strm_mywindow);
  5246.       # Code c auf den Bildschirm ausgeben:
  5247.       if (c=='\t')
  5248.         { var int x,y;
  5249.           do { text_writechar(w,' ');
  5250.                get_text_cursor_position(w,&x,&y);
  5251.              }
  5252.              until ((x % 8) == 0);
  5253.         }
  5254.         else
  5255.         { text_writechar(w,c); }
  5256.     }}
  5257.  
  5258. LISPFUNN(make_window,0)
  5259.   { var reg3 mywindow w = text_create();
  5260.     if (!w)
  5261.       { pushSTACK(TheSubr(subr_self)->name);
  5262.         fehler(error,
  5263.                DEUTSCH ? "~: Kann keinen Window-Stream erzeugen." :
  5264.                ENGLISH ? "~: cannot create a window stream" :
  5265.                FRANCAIS ? "~ : Ne peux pas Θtablir un WINDOW-STREAM." :
  5266.                ""
  5267.               );
  5268.       }
  5269.    {var reg2 object stream =
  5270.       allocate_stream(strmflags_wr_ch_B,strmtype_window,strm_len+1);
  5271.       # Flags: nur WRITE-CHAR erlaubt
  5272.     # und fⁿllen:
  5273.     var reg1 Stream s = TheStream(stream);
  5274.       s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  5275.       s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  5276.       s->strm_rd_ch = P(rd_ch_dummy); # READ-CHAR unm÷glich
  5277.       s->strm_rd_ch_last = NIL; # Lastchar := NIL
  5278.       s->strm_wr_ch = P(wr_ch_window); # WRITE-CHAR-Pseudofunktion
  5279.       s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  5280.       #ifdef STRM_WR_SS
  5281.       s->strm_wr_ss = P(wr_ss_dummy);
  5282.       #endif
  5283.       s->strm_mywindow = type_untype_object(machine_type,w);
  5284.     value1 = stream; mv_count=1;
  5285.   }}
  5286.  
  5287. # Schlie▀t einen Window-Stream.
  5288.   local void close_window (object stream);
  5289.   local void close_window(stream)
  5290.     var reg1 object stream;
  5291.     { var reg2 mywindow w = (mywindow)TheMachine(TheStream(stream)->strm_mywindow);
  5292.       text_destroy(w);
  5293.     }
  5294.  
  5295. LISPFUNN(window_size,1)
  5296.   { var reg1 object stream = popSTACK();
  5297.     check_window_stream(stream);
  5298.    {var reg2 mywindow w = (mywindow)TheMachine(TheStream(stream)->strm_mywindow);
  5299.     var int current_width;
  5300.     var int current_height;
  5301.     get_text_size(w,¤t_width,¤t_height);
  5302.     value1 = fixnum((uintW)current_height);
  5303.     value2 = fixnum((uintW)current_width);
  5304.     mv_count=2;
  5305.   }}
  5306.  
  5307. LISPFUNN(window_cursor_position,1)
  5308.   { var reg1 object stream = popSTACK();
  5309.     check_window_stream(stream);
  5310.    {var reg2 mywindow w = (mywindow)TheMachine(TheStream(stream)->strm_mywindow);
  5311.     var int current_x;
  5312.     var int current_y;
  5313.     get_text_cursor_position(w,¤t_x,¤t_y);
  5314.     value1 = fixnum((uintW)current_y);
  5315.     value2 = fixnum((uintW)current_x);
  5316.     mv_count=2;
  5317.   }}
  5318.  
  5319. LISPFUNN(set_window_cursor_position,3)
  5320.   { var reg1 object stream = STACK_2;
  5321.     check_window_stream(stream);
  5322.    {var reg4 mywindow w = (mywindow)TheMachine(TheStream(stream)->strm_mywindow);
  5323.     var reg2 uintL line = posfixnum_to_L(STACK_1);
  5324.     var reg3 uintL column = posfixnum_to_L(STACK_0);
  5325.     var int current_width;
  5326.     var int current_height;
  5327.     get_text_size(w,¤t_width,¤t_height);
  5328.     if ((line < (uintL)current_height) && (column < (uintL)current_width))
  5329.       { set_text_cursor_position(w,column,line); }
  5330.     value1 = STACK_1; value2 = STACK_0; mv_count=2; skipSTACK(3);
  5331.   }}
  5332.  
  5333. LISPFUNN(clear_window,1)
  5334.   { var reg1 object stream = popSTACK();
  5335.     check_window_stream(stream);
  5336.    {var reg2 mywindow w = (mywindow)TheMachine(TheStream(stream)->strm_mywindow);
  5337.     text_clear(w);
  5338.     value1 = NIL; mv_count=0;
  5339.   }}
  5340.  
  5341. LISPFUNN(clear_window_to_eot,1)
  5342.   { var reg1 object stream = popSTACK();
  5343.     check_window_stream(stream);
  5344.    {var reg2 mywindow w = (mywindow)TheMachine(TheStream(stream)->strm_mywindow);
  5345.     text_clear_to_eot(w);
  5346.     value1 = NIL; mv_count=0;
  5347.   }}
  5348.  
  5349. LISPFUNN(clear_window_to_eol,1)
  5350.   { var reg1 object stream = popSTACK();
  5351.     check_window_stream(stream);
  5352.    {var reg2 mywindow w = (mywindow)TheMachine(TheStream(stream)->strm_mywindow);
  5353.     text_clear_to_eol(w);
  5354.     value1 = NIL; mv_count=0;
  5355.   }}
  5356.  
  5357. LISPFUNN(delete_window_line,1)
  5358.   { var reg1 object stream = popSTACK();
  5359.     check_window_stream(stream);
  5360.    {var reg2 mywindow w = (mywindow)TheMachine(TheStream(stream)->strm_mywindow);
  5361.     text_delete_line(w);
  5362.     value1 = NIL; mv_count=0;
  5363.   }}
  5364.  
  5365. LISPFUNN(insert_window_line,1)
  5366.   { var reg1 object stream = popSTACK();
  5367.     check_window_stream(stream);
  5368.    {var reg2 mywindow w = (mywindow)TheMachine(TheStream(stream)->strm_mywindow);
  5369.     text_insert_line(w);
  5370.     value1 = NIL; mv_count=0;
  5371.   }}
  5372.  
  5373. LISPFUNN(highlight_on,1)
  5374.   { check_window_stream(popSTACK());
  5375.     # noch nicht implementiert
  5376.     value1 = NIL; mv_count=0;
  5377.   }
  5378.  
  5379. LISPFUNN(highlight_off,1)
  5380.   { check_window_stream(popSTACK());
  5381.     # noch nicht implementiert
  5382.     value1 = NIL; mv_count=0;
  5383.   }
  5384.  
  5385. LISPFUNN(window_cursor_on,1)
  5386.   { var reg1 object stream = popSTACK();
  5387.     check_window_stream(stream);
  5388.    {var reg2 mywindow w = (mywindow)TheMachine(TheStream(stream)->strm_mywindow);
  5389.     text_cursor_on(w);
  5390.     value1 = NIL; mv_count=0;
  5391.   }}
  5392.  
  5393. LISPFUNN(window_cursor_off,1)
  5394.   { var reg1 object stream = popSTACK();
  5395.     check_window_stream(stream);
  5396.    {var reg2 mywindow w = (mywindow)TheMachine(TheStream(stream)->strm_mywindow);
  5397.     text_cursor_off(w);
  5398.     value1 = NIL; mv_count=0;
  5399.   }}
  5400.  
  5401. #endif # MSDOS && WINDOWS
  5402.  
  5403. #if defined(UNIX) || (defined(EMUNIX_PORTABEL) && defined(EMUNIX_OLD_8e)) || defined(RISCOS)
  5404.  
  5405. # ------------------------------------------------------------------------------
  5406.  
  5407. # Routinen zur Emulation aller VT100-Features auf normalen Terminals.
  5408. # Idee: Oliver Laumann 1987
  5409.  
  5410. # Benutzt die TERMCAP-Library:
  5411.   # Besorgt die Capability-Informationen zu Terminal-Type name.
  5412.   # Ergebnis: 1 falls OK, 0 falls name unbekannt, -1 bei sonstigem Fehler.
  5413.     extern int tgetent (char* bp, char* name);
  5414.   # Besorgt den Wert einer numerischen Capability (-1 falls nicht vorhanden).
  5415.     extern int tgetnum (char* id);
  5416.   # Besorgt den Wert einer booleschen Capability (1 falls vorhanden, 0 sonst).
  5417.     extern int tgetflag (char* id);
  5418.   # Besorgt den Wert einer String-wertigen Capability und (falls area/=NULL)
  5419.   # kopiert es nach *area und rⁿckt dabei *area weiter.
  5420.     extern char* tgetstr (char* id, char** area);
  5421.   # Besorgt den String, der eine Cursor-Positionierung an Stelle (destcol,destline)
  5422.   # bewirkt. (N÷tig, da tgetstr("cm") ein spezielles Format hat!)
  5423.     extern char* tgoto (char* cm, int destcol, int destline);
  5424.   # Fⁿhrt eine String-Capability aus. Dazu wird fⁿr jedes Character die
  5425.   # Ausgabefunktion *outcharfun aufgerufen. (N÷tig, da String-Capabilities
  5426.   # Padding-Befehle enthalten k÷nnen!)
  5427.     extern char* tputs (char* cp, int affcnt, void (*outcharfun)());
  5428.  
  5429. # Einstellbare Wⁿnsche:
  5430.   #define WANT_INSERT  FALSE  # Insert-Modus
  5431.   #define WANT_SAVE    FALSE  # Save/Restore fⁿr die Cursor-Position
  5432.   #define WANT_ATTR    TRUE   # Attribute (fett, reverse etc.)
  5433.   #define WANT_CHARSET FALSE  # Fonts = Charsets
  5434.   # zu definierende Funktionen:
  5435.   #define WANT_CURSOR_MOVE         FALSE
  5436.   #define WANT_CURSOR_BACKSPACE    FALSE
  5437.   #define WANT_CURSOR_RETURN       TRUE
  5438.   #define WANT_CURSOR_LINEFEED     TRUE
  5439.   #define WANT_CURSOR_REVLINEFEED  FALSE
  5440.   #define WANT_CLEAR_SCREEN        TRUE
  5441.   #define WANT_CLEAR_FROM_BOS      FALSE
  5442.   #define WANT_CLEAR_TO_EOS        TRUE
  5443.   #define WANT_CLEAR_LINE          FALSE
  5444.   #define WANT_CLEAR_FROM_BOL      FALSE
  5445.   #define WANT_CLEAR_TO_EOL        TRUE
  5446.   #define WANT_INSERT_1CHAR        FALSE
  5447.   #define WANT_INSERT_CHAR         FALSE
  5448.   #define WANT_INSERT_LINE         TRUE
  5449.   #define WANT_DELETE_CHAR         FALSE
  5450.   #define WANT_DELETE_LINE         TRUE
  5451.   #define WANT_OUTPUT_1CHAR        TRUE
  5452.   # kleine Korrekturen:
  5453.   #define WANT_CLEAR_SCREEN        TRUE
  5454.   #if WANT_OUTPUT_1CHAR && WANT_INSERT
  5455.   #define WANT_INSERT_1CHAR        TRUE
  5456.   #endif
  5457.  
  5458. # Ausgabe eines Zeichens, direkt.
  5459.   local void out_char (uintB c);
  5460.   local void out_char(c)
  5461.     var uintB c;
  5462.     {
  5463.       #ifdef GRAPHICS_SWITCH
  5464.       switch_text_mode();
  5465.       #endif
  5466.       begin_system_call();
  5467.       restart_it:
  5468.      {var reg1 int ergebnis = write(stdout_handle,&c,1); # Zeichen auszugeben versuchen
  5469.       if (ergebnis<0)
  5470.         { if (errno==EINTR) goto restart_it;
  5471.           OS_error(); # Error melden
  5472.         }
  5473.       if (ergebnis==0) # nicht erfolgreich?
  5474.         { pushSTACK(var_stream(S(terminal_io))); # Wert fⁿr Slot PATHNAME von FILE-ERROR
  5475.           fehler(file_error,
  5476.                  DEUTSCH ? "Kann nichts auf Standard-Output ausgeben." :
  5477.                  ENGLISH ? "cannot output to standard output" :
  5478.                  FRANCAIS ? "Ne peut rien Θcrire sur la sortie principale." :
  5479.                  ""
  5480.                 );
  5481.         }
  5482.       end_system_call();
  5483.     }}
  5484.  
  5485. # Ausgabe eines Capability-Strings.
  5486.   local void out_capstring (char* s);
  5487.   local void out_capstring(s)
  5488.     var reg1 char* s;
  5489.     { if (!(s==NULL)) # Absichern gegen nicht vorhandene Capability
  5490.         { tputs(s,1,(void (*)()) &out_char); }
  5491.     }
  5492.  
  5493. # Ausgabe eines Capability-Strings mit einem Argument.
  5494.   local void out_cap1string (char* s, int arg);
  5495.   local void out_cap1string(s,arg)
  5496.     var reg1 char* s;
  5497.     var reg2 int arg;
  5498.     { if (!(s==NULL)) # Absichern gegen nicht vorhandene Capability
  5499.         { tputs(tgoto(s,0,arg),1,(void (*)()) &out_char); }
  5500.     }
  5501.  
  5502. # Kosten der Ausfⁿhrung einer Capability:
  5503.   #define EXPENSIVE 1000
  5504.   local uintC cost_counter; # ZΣhler
  5505.   # Funktion, die nicht ausgibt, sondern nur zΣhlt:
  5506.   local void count_char (char c);
  5507.   local void count_char(c)
  5508.     var reg1 char c;
  5509.     { cost_counter++; }
  5510.   # Berechnet die Kosten der Ausgabe einer Capability:
  5511.   local uintC cap_cost (char* s);
  5512.   local uintC cap_cost(s)
  5513.     var reg1 char* s;
  5514.     { if (s==NULL)
  5515.         { return EXPENSIVE; } # Capability nicht vorhanden
  5516.         else
  5517.         { cost_counter = 0;
  5518.           tputs(s,1,(void (*)()) &count_char);
  5519.           return cost_counter;
  5520.         }
  5521.     }
  5522.  
  5523. # Buffer fⁿr von mir ben÷tigte Capabilities und Pointer da hinein:
  5524.   local char tentry[4096];
  5525.   local char* tp = &tentry[0];
  5526. # Einige ausgewΣhlte Capabilities (NULL oder Pointer in tentry hinein):
  5527.   # Insert-Modus:
  5528.   local char* IMcap; # Enter Insert Mode
  5529.   local uintC IMcost;
  5530.   local char* EIcap; # End Insert Mode
  5531.   local uintC EIcost;
  5532.   #if WANT_ATTR
  5533.   # Attribute:
  5534.   local char* SOcap; # Enter standout mode
  5535.   local char* SEcap; # End standout mode
  5536.   local char* UScap; # Enter underline mode
  5537.   local char* UEcap; # End underline mode
  5538.   local char* MBcap; # Turn on blinking
  5539.   local char* MDcap; # Turn on bold (extra-bright) mode
  5540.   local char* MHcap; # Turn on half-bright mode
  5541.   local char* MRcap; # Turn on reverse mode
  5542.   local char* MEcap; # Turn off all attributes
  5543.   #endif
  5544.   #if WANT_CHARSET
  5545.   # ZeichensΣtze:
  5546.   local boolean ISO2022; # ob Zeichensatzwechsel nach ISO2022 unterstⁿtzt wird
  5547.   #endif
  5548.   # Cursor-Bewegung:
  5549.   local char* CMcap; # Cursor motion, allgemeine Cursor-Positionierung
  5550.   local char* TIcap; # Initialize mode where CM is usable
  5551.   local char* TEcap; # Exit mode where CM is usable
  5552.   local char* BCcap; # Backspace Cursor
  5553.   local uintC BCcost;
  5554.   local char* NDcap; # cursor right
  5555.   local uintC NDcost;
  5556.   local char* DOcap; # cursor down
  5557.   local uintC DOcost;
  5558.   local char* UPcap; # cursor up
  5559.   local uintC UPcost;
  5560.   local char* NLcap; # Newline
  5561.   local char* CRcap; # Carriage Return
  5562.   local uintC CRcost;
  5563.   # Scrolling:
  5564.   local char* CScap; # change scroll region
  5565.   #if WANT_DELETE_LINE
  5566.   local char* SFcap; # Scroll (text up)
  5567.   #endif
  5568.   #if WANT_CURSOR_REVLINEFEED || WANT_INSERT_LINE
  5569.   local char* SRcap; # Scroll reverse (text down)
  5570.   #endif
  5571.   # Sonstige:
  5572.   local char* IScap; # Terminal Initialization 2
  5573. #  local char* BLcap; # Bell
  5574. #  local char* VBcap; # Visible Bell (Flash)
  5575.   local char* CLcap; # clear screen, cursor home
  5576.   #if WANT_CLEAR_FROM_BOS || WANT_CLEAR_TO_EOS || WANT_CLEAR_LINE || WANT_CLEAR_FROM_BOL || WANT_CLEAR_TO_EOL
  5577.   local char* CEcap; # clear to end of line
  5578.   #endif
  5579.   #if WANT_CLEAR_TO_EOS
  5580.   local char* CDcap; # clear to end of screen
  5581.   #endif
  5582.   #if WANT_CURSOR_REVLINEFEED || WANT_INSERT_LINE
  5583.   local char* ALcap; # add new blank line
  5584.   #endif
  5585.   #if WANT_DELETE_LINE
  5586.   local char* DLcap; # delete line
  5587.   #endif
  5588.   #if WANT_DELETE_CHAR
  5589.   local char* DCcap; # delete character
  5590.   #endif
  5591.   #if WANT_INSERT_1CHAR || WANT_INSERT_CHAR
  5592.   local char* ICcap; # insert character
  5593.   #endif
  5594.   #if WANT_INSERT_CHAR
  5595.   local char* CICcap; # insert count characters
  5596.   #endif
  5597.   #if WANT_INSERT_LINE
  5598.   local char* CALcap; # add count blank lines
  5599.   #endif
  5600.   #if WANT_DELETE_CHAR
  5601.   local char* CDCcap; # delete count chars
  5602.   #endif
  5603.   #if WANT_DELETE_LINE
  5604.   local char* CDLcap; # delete count lines
  5605.   #endif
  5606.   local boolean AM; # automatic margins, ob an rechterer unterer Ecke scrollt
  5607.   local int rows; # Anzahl der Zeilen des Bildschirms, >0
  5608.   local int cols; # Anzahl der Spalten des Bildschirms, >0
  5609.   # Obere Zeile ist Zeile 0, untere Zeile ist Zeile rows-1.
  5610.   # Linke Spalte ist Spalte 0, rechte Spalte ist Spalte cols-1.
  5611.   #if WANT_ATTR || WANT_CHARSET
  5612.   local uintB* null; # Pointer auf cols Nullen
  5613.   #endif
  5614.   local uintB* blank; # Pointer auf cols Blanks
  5615.  
  5616. # Beschreibung einer Terminal-Ausgabe-Einheit:
  5617. typedef struct { uintB** image; # image[y][x] ist das Zeichen an Position (x,y)
  5618.                  #if WANT_ATTR
  5619.                  uintB** attr;  # attr[y][x] ist sein Attribut
  5620.                  uintB curr_attr; # welches Attribut gerade aktuell ist
  5621.                  #endif
  5622.                  #if WANT_CHARSET
  5623.                  uintB** font;  # font[y][x] ist sein Font (Charset)
  5624.                  #define charset_count 4
  5625.                  uintB charsets[charset_count]; # Tabelle von ZeichensΣtzen
  5626.                  uintC curr_charset; # welcher der ZeichensΣtze gerade aktuell ist
  5627.                                      # (>=0, <charset_count)
  5628.                  #endif
  5629.                  int x; # Cursorposition (>=0, <=cols)
  5630.                  int y; # Cursorposition (>=0, <rows)
  5631.                         # (Bei x=cols wird der Cursor in Spalte cols-1 dargestellt.)
  5632.                  int top, bot; # Scroll-Region = Zeilen y mit top <= y <= bot,
  5633.                                # es ist 0 <= top <= bot <= rows-1.
  5634.                  #if WANT_INSERT
  5635.                  boolean insert; # ob die Ausgabe-Einheit im Insert-Modus arbeitet
  5636.                                  # (dann ist das Terminal meist im Insert-Modus)
  5637.                  #endif
  5638.                  #if WANT_SAVE
  5639.                  boolean saved;
  5640.                  #if WANT_ATTR
  5641.                  uintB saved_curr_attr;
  5642.                  #endif
  5643.                  #if WANT_CHARSET
  5644.                  uintB saved_charsets[charset_count];
  5645.                  uintC saved_curr_charset;
  5646.                  #endif
  5647.                  int saved_x, saved_y;
  5648.                  #endif
  5649.                }
  5650.         win;
  5651.  
  5652. # aktuelle Ausgabe-Einheit:
  5653.   local win currwin; # es gibt nur eine!
  5654.   #define curr (&currwin)
  5655.  
  5656. #if WANT_INSERT
  5657.  
  5658. # Insert-Modus ein- bzw. ausschalten:
  5659.   # Flag, ob das Terminal im Insert-Modus ist (falls es einen solchen gibt):
  5660.   local boolean insert;
  5661.   local void set_insert_mode (boolean flag);
  5662.   local void set_insert_mode(flag)
  5663.     var reg1 boolean flag;
  5664.     { if (flag)
  5665.         # Einschalten
  5666.         { if (!insert) { out_capstring(IMcap); } }
  5667.         else
  5668.         # Ausschalten
  5669.         { if (insert) { out_capstring(EIcap); } }
  5670.       insert = flag;
  5671.     }
  5672.  
  5673. #endif
  5674.  
  5675. #if WANT_ATTR
  5676.  
  5677. # Ausgabe-Attribute des Terminals umschalten:
  5678.   local uintB term_attr; # aktuelle Attribute des Terminals
  5679.   # m÷gliche Attribute sind ein ODER aus:
  5680.     #define A_SO    bit(0)  # Standout mode
  5681.     #define A_US    bit(1)  # Underscore mode
  5682.     #define A_BL    bit(2)  # Blinking
  5683.     #define A_BD    bit(3)  # Bold mode
  5684.     #define A_DI    bit(4)  # Dim mode
  5685.     #define A_RV    bit(5)  # Reverse mode
  5686.   local void change_attr (uintB new_attr);
  5687.   local void change_attr(new_attr)
  5688.     var reg1 uintB new_attr;
  5689.     { var reg2 uintB old_attr = term_attr;
  5690.       if (old_attr == new_attr) { return; }
  5691.       if (   ((old_attr & A_SO) && !(new_attr & A_SO))
  5692.           || ((old_attr & A_US) && !(new_attr & A_US))
  5693.           || ((old_attr & A_BL) && !(new_attr & A_BL))
  5694.           || ((old_attr & A_BD) && !(new_attr & A_BD))
  5695.           || ((old_attr & A_DI) && !(new_attr & A_DI))
  5696.           || ((old_attr & A_RV) && !(new_attr & A_RV))
  5697.          )
  5698.         # Mu▀ Attribute ausschalten.
  5699.         { out_capstring(UEcap); # alle aus
  5700.           out_capstring(SEcap);
  5701.           out_capstring(MEcap);
  5702.           if (new_attr & A_SO) out_capstring(SOcap); # und selektiv wieder an
  5703.           if (new_attr & A_US) out_capstring(UScap);
  5704.           if (new_attr & A_BL) out_capstring(MBcap);
  5705.           if (new_attr & A_BD) out_capstring(MDcap);
  5706.           if (new_attr & A_DI) out_capstring(MHcap);
  5707.           if (new_attr & A_RV) out_capstring(MRcap);
  5708.         }
  5709.         else
  5710.         { # selektiv einschalten:
  5711.           if ((new_attr & A_SO) && !(old_attr & A_SO)) out_capstring(SOcap);
  5712.           if ((new_attr & A_US) && !(old_attr & A_US)) out_capstring(UScap);
  5713.           if ((new_attr & A_BL) && !(old_attr & A_BL)) out_capstring(MBcap);
  5714.           if ((new_attr & A_BD) && !(old_attr & A_BD)) out_capstring(MDcap);
  5715.           if ((new_attr & A_DI) && !(old_attr & A_DI)) out_capstring(MHcap);
  5716.           if ((new_attr & A_RV) && !(old_attr & A_RV)) out_capstring(MRcap);
  5717.         }
  5718.       term_attr = new_attr;
  5719.     }
  5720.  
  5721. #endif
  5722.  
  5723. #if WANT_CHARSET
  5724.  
  5725. # Ausgabe-Zeichensatz des Terminals umschalten:
  5726.   local uintB term_charset; # aktueller Zeichensatz des Terminals
  5727.                             # = curr->charsets[curr->curr_charset]
  5728.   #define ASCII 0  # Abkⁿrzung fⁿr den Zeichensatz 'B'
  5729.   local void change_charset (uintB new);
  5730.   local void change_charset(new)
  5731.     var reg1 uintB new;
  5732.     { if (term_charset==new) { return; }
  5733.       if (ISO2022)
  5734.         { out_char(ESC); out_char('('); out_char(new==ASCII ? 'B' : new); } /*)*/
  5735.       term_charset = new;
  5736.     }
  5737.   # Charset Nr. n auf c schalten:
  5738.   local void choose_charset (uintB c, uintC n);
  5739.   local void choose_charset(c,n)
  5740.     var reg1 uintB c;
  5741.     var reg2 uintC n;
  5742.     { if (c=='B') { c = ASCII; }
  5743.       if (curr->charsets[n] == c) return;
  5744.       curr->charsets[n] = c;
  5745.       if (curr->curr_charset == n) # der aktuelle?
  5746.         { change_charset(c); }
  5747.     }
  5748.   # Charset Nr. n aktuell machen:
  5749.   local void set_curr_charset (uintC n);
  5750.   local void set_curr_charset(n)
  5751.     var reg1 uintC n;
  5752.     { if (curr->curr_charset == n) return;
  5753.       curr->curr_charset = n;
  5754.       change_charset(curr->charsets[n]);
  5755.     }
  5756.  
  5757. #endif
  5758.  
  5759. # Kosten des Neu-Anzeigens von Zeile y, Zeichen x1..x2-1 berechnen:
  5760. # (0 <= y < rows, 0 <= x1 <= x2 <= cols)
  5761.   local uintC rewrite_cost (int y, int x1, int x2);
  5762.   local uintC rewrite_cost(y,x1,x2)
  5763.     var reg4 int y;
  5764.     var reg6 int x1;
  5765.     var reg5 int x2;
  5766.     { if (AM && (y==rows-1) && (x2==cols)) # rechte untere Ecke kann scrollen?
  5767.         { return EXPENSIVE; }
  5768.      {var reg1 int dx = x2-x1;
  5769.       if (dx==0) { return 0; }
  5770.       #if WANT_ATTR
  5771.       {var reg2 uintB* p = &curr->attr[y][x1];
  5772.        var reg3 uintC count;
  5773.        dotimespC(count,dx,
  5774.          { if (!(*p++ == term_attr)) # Attribut-Wechsel n÷tig?
  5775.              { return EXPENSIVE; }
  5776.          });
  5777.       }
  5778.       #endif
  5779.       #if WANT_CHARSET
  5780.       {var reg2 uintB* p = &curr->font[y][x1];
  5781.        var reg3 uintC count;
  5782.        dotimespC(count,dx,
  5783.          { if (!(*p++ == term_charset)) # Font-Wechsel n÷tig?
  5784.              { return EXPENSIVE; }
  5785.          });
  5786.       }
  5787.       #endif
  5788.       {var reg2 uintC cost = dx;
  5789.        #if WANT_INSERT
  5790.        if (curr->insert) { cost += EIcost + IMcost; }
  5791.        #endif
  5792.        return cost;
  5793.     }}}
  5794.  
  5795. # Bewegt den Cursor von Position (y1,x1) an Position (y2,x2).
  5796. # (x1,y1) = (-1,-1) falls aktuelle Position unbekannt.
  5797.   local void gofromto (int y1, int x1, int y2, int x2);
  5798.   local void gofromto(y1,x1,y2,x2)
  5799.     var reg10 int y1;
  5800.     var reg10 int x1;
  5801.     var reg10 int y2;
  5802.     var reg9 int x2;
  5803.     { if (x2==cols) # Cursor an den rechten Rand?
  5804.         { x2--; out_capstring(tgoto(CMcap,x2,y2)); return; } # Bleibt in der letzten Spalte
  5805.       if (x1==cols) # Cursor ist am rechten Rand?
  5806.         { out_capstring(tgoto(CMcap,x2,y2)); return; } # absolut adressieren
  5807.      {var reg4 int dy = y2-y1;
  5808.       var reg5 int dx = x2-x1;
  5809.       if ((dy==0) && (dx==0)) { return; }
  5810.       if ((y1==-1) || (x1==-1) || (y2 > curr->bot) || (y2 < curr->top))
  5811.         { out_capstring(tgoto(CMcap,x2,y2)); return; }
  5812.       { var reg7 enum { MX_NONE, MX_LE, MX_RI, MX_RW, MX_CR } mx = MX_NONE;
  5813.         var reg8 enum { MY_NONE, MY_UP, MY_DO } my = MY_NONE;
  5814.         # M÷glichkeit 1: mit CMcap
  5815.         var reg6 uintC CMcost = cap_cost(tgoto(CMcap,x2,y2));
  5816.         # M÷glichkeit 2: mit getrennten x- und y-Bewegungen:
  5817.         var reg1 uintC xycost = 0;
  5818.         if (dx > 0)
  5819.           { var reg2 uintC cost1 = rewrite_cost(y1,x1,x2);
  5820.             var reg3 uintC cost2 = dx * NDcost;
  5821.             if (cost1 < cost2)
  5822.               { mx = MX_RW; xycost += cost1; }
  5823.               else
  5824.               { mx = MX_RI; xycost += cost2; }
  5825.           }
  5826.         elif (dx < 0)
  5827.           { mx = MX_LE; xycost += (-dx) * BCcost; }
  5828.         if (!(dx==0))
  5829.           { var reg2 uintC cost1 = CRcost + rewrite_cost(y1,0,x2);
  5830.             if (cost1 < xycost) { mx = MX_CR; xycost = cost1; }
  5831.           }
  5832.         if (dy > 0)
  5833.           { my = MY_DO; xycost += dy * DOcost; }
  5834.         elif (dy < 0)
  5835.           { my = MY_UP; xycost += (-dy) * UPcost; }
  5836.         if (xycost >= CMcost)
  5837.           { out_capstring(tgoto(CMcap,x2,y2)); return; }
  5838.         if (!(mx==MX_NONE))
  5839.           { if ((mx==MX_LE) || (mx==MX_RI))
  5840.               { var reg2 char* s;
  5841.                 if (mx==MX_LE) { dx = -dx; s = BCcap; } else { s = NDcap; }
  5842.                 do { out_capstring(s); } until (--dx == 0);
  5843.               }
  5844.               else
  5845.               { if (mx==MX_CR) { out_capstring(CRcap); x1=0; }
  5846.                 # Hiervon wurden die Kosten mit rewrite_cost berechnet:
  5847.                 if (x1<x2)
  5848.                   {
  5849.                     #if WANT_INSERT
  5850.                     if (curr->insert) { set_insert_mode(FALSE); }
  5851.                     #endif
  5852.                     {var reg2 uintB* ptr = &curr->image[y1][x1];
  5853.                      var reg3 uintC count;
  5854.                      dotimespC(count,x2-x1, { out_char(*ptr++); });
  5855.                     }
  5856.                     #if WANT_INSERT
  5857.                     if (curr->insert) { set_insert_mode(TRUE); }
  5858.                     #endif
  5859.               }   }
  5860.           }
  5861.         if (!(my==MY_NONE))
  5862.           { var reg2 char* s;
  5863.             if (my==MY_UP) { dy = -dy; s = UPcap; } else { s = DOcap; }
  5864.             do { out_capstring(s); } until (--dy == 0);
  5865.           }
  5866.     }}}
  5867.  
  5868. # Redisplay
  5869.   # lokale Variablen:
  5870.   local int last_x;
  5871.   local int last_y;
  5872.   # Eine Zeile neu anzeigen, die sich verΣndert haben kann:
  5873.     # nur ben÷tigte Parameter wirklich ⁿbergeben:
  5874.     #if WANT_ATTR && WANT_CHARSET
  5875.       #define RHargs(osp,oap,ofp,nsp,nap,nfp,y,x1,x2) (osp,oap,ofp,nsp,nap,nfp,y,x1,x2)
  5876.       #define RHparms(osp,oap,ofp,nsp,nap,nfp,y,x1,x2) (osp,oap,ofp,nsp,nap,nfp,y,x1,x2)
  5877.     #endif
  5878.     #if !WANT_ATTR && WANT_CHARSET
  5879.       #define RHargs(osp,oap,ofp,nsp,nap,nfp,y,x1,x2) (osp,ofp,nsp,nfp,y,x1,x2)
  5880.       #define RHparms(osp,oap,ofp,nsp,nap,nfp,y,x1,x2) (osp,ofp,nsp,nfp,y,x1,x2,oap,nap)
  5881.     #endif
  5882.     #if WANT_ATTR && !WANT_CHARSET
  5883.       #define RHargs(osp,oap,ofp,nsp,nap,nfp,y,x1,x2) (osp,oap,nsp,nap,y,x1,x2)
  5884.       #define RHparms(osp,oap,ofp,nsp,nap,nfp,y,x1,x2) (osp,oap,nsp,nap,y,x1,x2,ofp,nfp)
  5885.     #endif
  5886.     #if !WANT_ATTR && !WANT_CHARSET
  5887.       #define RHargs(osp,oap,ofp,nsp,nap,nfp,y,x1,x2) (osp,nsp,y,x1,x2)
  5888.       #define RHparms(osp,oap,ofp,nsp,nap,nfp,y,x1,x2) (osp,nsp,y,x1,x2,oap,ofp,nap,nfp)
  5889.     #endif
  5890.     #ifdef ANSI
  5891.     #undef RHparms
  5892.     #define RHparms  RHargs  # korrekt deklarieren
  5893.     local void redisplay_help RHparms (uintB* osp, uintB* oap, uintB* ofp, # old
  5894.                                        uintB* nsp, uintB* nap, uintB* nfp, # new
  5895.                                        int y, int x1, int x2); # Zeile y, von x1 bis x2-1
  5896.     #endif
  5897.     local void redisplay_help RHparms(osp,oap,ofp,nsp,nap,nfp,y,x1,x2)
  5898.       var reg6 uintB* osp;
  5899.       var reg4 uintB* oap;
  5900.       var reg5 uintB* ofp;
  5901.       var reg3 uintB* nsp;
  5902.       var reg1 uintB* nap;
  5903.       var reg2 uintB* nfp;
  5904.       var reg9 int y;
  5905.       var reg10 int x1;
  5906.       var reg10 int x2;
  5907.       { if (AM && (y == rows-1) && (x2 == cols)) { x2--; }
  5908.        {
  5909.         #if WANT_ATTR
  5910.         var reg8 uintB a = term_attr; # letztes Attribut
  5911.         #endif
  5912.         #if WANT_CHARSET
  5913.         var reg8 uintB f = term_charset; # letzter Font
  5914.         #endif
  5915.         var reg7 int x = x1;
  5916.         osp = &osp[x1]; nsp = &nsp[x1];
  5917.         #if WANT_ATTR
  5918.         oap = &oap[x1]; nap = &nap[x1];
  5919.         #endif
  5920.         #if WANT_CHARSET
  5921.         ofp = &ofp[x1]; nfp = &nfp[x1];
  5922.         #endif
  5923.         while (x < x2)
  5924.           { if (!((*nsp==*osp)
  5925.                   #if WANT_ATTR
  5926.                   && (*nap==*oap) && (*nap==a)
  5927.                   #endif
  5928.                   #if WANT_CHARSET
  5929.                   && (*nfp==*nap) && (*nfp==f)
  5930.                   #endif
  5931.                ) )
  5932.               { gofromto(last_y,last_x,y,x);
  5933.                 #if WANT_ATTR
  5934.                 a = *nap; if (!(a==term_attr)) { change_attr(a); }
  5935.                 #endif
  5936.                 #if WANT_CHARSET
  5937.                 f = *nfp; if (!(f==term_charset)) { change_charset(f); }
  5938.                 #endif
  5939.                 out_char(*nsp);
  5940.                 last_y = y; last_x = x+1;
  5941.               }
  5942.             x++;
  5943.             osp++; nsp++;
  5944.             #if WANT_ATTR
  5945.             oap++; nap++;
  5946.             #endif
  5947.             #if WANT_CHARSET
  5948.             ofp++; nfp++;
  5949.             #endif
  5950.           }
  5951.       }}
  5952.   #if WANT_INSERT_1CHAR || WANT_INSERT_CHAR || WANT_DELETE_CHAR
  5953.   # Eine Zeile neu anzeigen:
  5954.     # nur ben÷tigte Parameter wirklich ⁿbergeben:
  5955.     #if WANT_ATTR && WANT_CHARSET
  5956.       #define RLargs(osp,oap,ofp,y,x1,x2) (osp,oap,ofp,y,x1,x2)
  5957.       #define RLparms(osp,oap,ofp,y,x1,x2) (osp,oap,ofp,y,x1,x2)
  5958.     #endif
  5959.     #if !WANT_ATTR && WANT_CHARSET
  5960.       #define RLargs(osp,oap,ofp,y,x1,x2) (osp,ofp,y,x1,x2)
  5961.       #define RLparms(osp,oap,ofp,y,x1,x2) (osp,ofp,y,x1,x2,oap)
  5962.     #endif
  5963.     #if WANT_ATTR && !WANT_CHARSET
  5964.       #define RLargs(osp,oap,ofp,y,x1,x2) (osp,oap,y,x1,x2)
  5965.       #define RLparms(osp,oap,ofp,y,x1,x2) (osp,oap,y,x1,x2,ofp)
  5966.     #endif
  5967.     #if !WANT_ATTR && !WANT_CHARSET
  5968.       #define RLargs(osp,oap,ofp,y,x1,x2) (osp,y,x1,x2)
  5969.       #define RLparms(osp,oap,ofp,y,x1,x2) (osp,y,x1,x2,oap,ofp)
  5970.     #endif
  5971.     #ifdef ANSI
  5972.     #undef RHparms
  5973.     #define RHparms  RHargs  # korrekt deklarieren
  5974.     local void redisplay_line RLparms (uintB* osp, uintB* oap, uintB* ofp, # old
  5975.                                        int y, int x1, int x2); # Zeile y, von x1 bis x2-1
  5976.     #endif
  5977.     local void redisplay_line RLparms(osp,oap,ofp,y,x1,x2)
  5978.       var reg4 uintB* osp;
  5979.       var reg5 uintB* oap;
  5980.       var reg6 uintB* ofp;
  5981.       var reg1 int y;
  5982.       var reg2 int x1;
  5983.       var reg3 int x2;
  5984.       {
  5985.         #if WANT_INSERT
  5986.         if (curr->insert) { set_insert_mode(FALSE); }
  5987.         #endif
  5988.         #if WANT_ATTR
  5989.         {var reg4 uintB saved_attr = term_attr; change_attr(0);
  5990.         #endif
  5991.         #if WANT_CHARSET
  5992.         {var reg4 uintB saved_charset = term_charset; change_charset(ASCII);
  5993.         #endif
  5994.         last_y = y; last_x = x1;
  5995.         redisplay_help RHargs(osp,           oap,          ofp,
  5996.                               curr->image[y],curr->attr[y],curr->font[y],
  5997.                               y, x1,x2
  5998.                              );
  5999.         #if WANT_CHARSET
  6000.         change_charset(saved_charset); }
  6001.         #endif
  6002.         #if WANT_ATTR
  6003.         change_attr(saved_attr); }
  6004.         #endif
  6005.         #if WANT_INSERT
  6006.         if (curr->insert) { set_insert_mode(TRUE); }
  6007.         #endif
  6008.       }
  6009.   #endif
  6010.   # Den ganzen Schirm neu anzeigen:
  6011.     local void redisplay (void);
  6012.     local void redisplay()
  6013.       {
  6014.         #if WANT_INSERT
  6015.         set_insert_mode(FALSE);
  6016.         #endif
  6017.         #if WANT_ATTR
  6018.         {var reg2 uintB saved_attr = term_attr; change_attr(0);
  6019.         #endif
  6020.         #if WANT_CHARSET
  6021.         {var reg2 uintB saved_charset = term_charset; change_charset(ASCII);
  6022.         #endif
  6023.         out_capstring(CLcap); last_x = 0; last_y = 0;
  6024.         {var reg1 uintC y = 0;
  6025.          while (y<rows)
  6026.            { redisplay_help RHargs(blank,         null,         null,          # old
  6027.                                    curr->image[y],curr->attr[y],curr->font[y], # new
  6028.                                    y,                                          # Zeile y
  6029.                                    0,cols                                      # alle Spalten
  6030.                                   );
  6031.              y++;
  6032.         }  }
  6033.         #if WANT_CHARSET
  6034.         change_charset(saved_charset); }
  6035.         #endif
  6036.         #if WANT_ATTR
  6037.         change_attr(saved_attr); }
  6038.         #endif
  6039.         #if WANT_INSERT
  6040.         if (curr->insert) { set_insert_mode(TRUE); }
  6041.         #endif
  6042.         gofromto(last_y,last_x,curr->y,curr->x);
  6043.       }
  6044.  
  6045. # Weitere Cursor-Bewegungen:
  6046. #if WANT_CURSOR_MOVE
  6047.  
  6048.   local void cursor_right (int n);
  6049.   local void cursor_right(n)
  6050.     var reg3 int n;
  6051.     { var reg2 int x = curr->x;
  6052.       if (x==cols) { return; }
  6053.      {var reg1 int new_x = x + n;
  6054.       if (new_x > cols) { new_x = cols; }
  6055.       gofromto(curr->y,x,curr->y,curr->x = new_x);
  6056.     }}
  6057.  
  6058.   local void cursor_left (int n);
  6059.   local void cursor_left(n)
  6060.     var reg3 int n;
  6061.     { var reg2 int x = curr->x;
  6062.       var reg1 int new_x = x - n;
  6063.       if (new_x < 0) { new_x = 0; }
  6064.       gofromto(curr->y,x,curr->y,curr->x = new_x);
  6065.     }
  6066.  
  6067.   local void cursor_up (int n);
  6068.   local void cursor_up(n)
  6069.     var reg3 int n;
  6070.     { var reg2 int y = curr->y;
  6071.       var reg1 int new_y = y - n;
  6072.       if (new_y < 0) { new_y = 0; }
  6073.       gofromto(y,curr->x,curr->y = new_y,curr->x);
  6074.     }
  6075.  
  6076.   local void cursor_down (int n);
  6077.   local void cursor_down(n)
  6078.     var reg3 int n;
  6079.     { var reg2 int y = curr->y;
  6080.       var reg1 int new_y = y + n;
  6081.       if (new_y >= rows) { new_y = rows-1; }
  6082.       gofromto(y,curr->x,curr->y = new_y,curr->x);
  6083.     }
  6084.  
  6085. #endif
  6086.  
  6087. # Backspace (Cursor um 1 nach links, innerhalb einer Zeile)
  6088. #if WANT_CURSOR_BACKSPACE
  6089.   local void cursor_backspace (void);
  6090.   local void cursor_backspace()
  6091.     { if (curr->x > 0)
  6092.         { if (curr->x < cols)
  6093.             { if (BCcap)
  6094.                 { out_capstring(BCcap); }
  6095.                 else
  6096.                 { gofromto(curr->y,curr->x,curr->y,curr->x - 1); }
  6097.             }
  6098.           curr->x = curr->x - 1;
  6099.     }   }
  6100. #endif
  6101.  
  6102. # Return (Cursor an den Anfang der Zeile)
  6103. #if WANT_CURSOR_RETURN
  6104.   local void cursor_return (void);
  6105.   local void cursor_return()
  6106.     { if (curr->x > 0) { out_capstring(CRcap); curr->x = 0; } }
  6107. #endif
  6108.  
  6109. # Hilfroutinen zum Scrollen:
  6110. #if WANT_CURSOR_LINEFEED || WANT_DELETE_LINE
  6111.   local void scroll_up_help (uintB** pp, uintB filler);
  6112.   local void scroll_up_help(pp,filler)
  6113.     var reg1 uintB** pp;
  6114.     var reg3 uintB filler;
  6115.     { # pp[top..bot] um eins nach links verschieben,
  6116.       # pp[top] herausnehmen, l÷schen und als pp[bot] wieder einhΣngen:
  6117.       pp = &pp[curr->top];
  6118.      {var reg2 uintC count;
  6119.       var reg4 uintB* tmp = *pp;
  6120.       dotimesC(count,curr->bot - curr->top, { pp[0] = pp[1]; pp++; } );
  6121.       {var reg1 uintB* p = tmp;
  6122.        dotimesC(count,cols, { *p++ = filler; } );
  6123.       }
  6124.       *pp = tmp;
  6125.     }}
  6126.   local void scroll_up (void);
  6127.   local void scroll_up()
  6128.     { scroll_up_help(curr->image,' ');
  6129.       #if WANT_ATTR
  6130.       scroll_up_help(curr->attr,0);
  6131.       #endif
  6132.       #if WANT_CHARSET
  6133.       scroll_up_help(curr->font,0);
  6134.       #endif
  6135.     }
  6136. #endif
  6137. #if WANT_CURSOR_REVLINEFEED || WANT_INSERT_LINE
  6138.   local void scroll_down_help (uintB** pp, uintB filler);
  6139.   local void scroll_down_help(pp,filler)
  6140.     var reg1 uintB** pp;
  6141.     var reg3 uintB filler;
  6142.     { # pp[top..bot] um eins nach rechts verschieben,
  6143.       # pp[top] herausnehmen, l÷schen und als pp[bot] wieder einhΣngen:
  6144.       pp = &pp[curr->bot];
  6145.      {var reg2 uintC count;
  6146.       var reg4 uintB* tmp = *pp;
  6147.       dotimesC(count,curr->bot - curr->top, { pp[0] = pp[-1]; pp--; } );
  6148.       {var reg1 uintB* p = tmp;
  6149.        dotimesC(count,cols, { *p++ = filler; } );
  6150.       }
  6151.       *pp = tmp;
  6152.     }}
  6153.   local void scroll_down (void);
  6154.   local void scroll_down()
  6155.     { scroll_down_help(curr->image,' ');
  6156.       #if WANT_ATTR
  6157.       scroll_down_help(curr->attr,0);
  6158.       #endif
  6159.       #if WANT_CHARSET
  6160.       scroll_down_help(curr->font,0);
  6161.       #endif
  6162.     }
  6163. #endif
  6164.  
  6165. # Linefeed (Cursor um 1 nach unten):
  6166. #if WANT_CURSOR_LINEFEED
  6167.   local void cursor_linefeed (void);
  6168.   local void cursor_linefeed()
  6169.     { if (curr->y == curr->bot) { scroll_up(); }
  6170.       elif (curr->y < rows-1) { curr->y++; }
  6171.       out_capstring(NLcap);
  6172.     }
  6173. #endif
  6174.  
  6175. # Reverse Linefeed (Cursor um 1 nach oben):
  6176. #if WANT_CURSOR_REVLINEFEED
  6177.   local void cursor_revlinefeed (void);
  6178.   local void cursor_revlinefeed()
  6179.     { if (curr->y == curr->top)
  6180.         { scroll_down();
  6181.           if (SRcap)
  6182.             { out_capstring(SRcap); }
  6183.           elif (ALcap)
  6184.             { gofromto(curr->top,curr->x,curr->top,0); # Cursor nach links
  6185.               out_capstring(ALcap);
  6186.               gofromto(curr->top,0,curr->top,curr->x); # Cursor wieder zurⁿck
  6187.             }
  6188.           else
  6189.             { redisplay(); }
  6190.         }
  6191.       elif (curr->y > 0)
  6192.         { cursor_up(1); }
  6193.     }
  6194. #endif
  6195.  
  6196. # L÷sch-Operationen:
  6197.  
  6198. # Stⁿck einer Zeile l÷schen:
  6199. #if WANT_CLEAR_SCREEN || WANT_CLEAR_FROM_BOS
  6200.   local void cleared_linepart (int y, int x1, int x2);
  6201.   local void cleared_linepart(y,x1,x2)
  6202.     var reg5 int y;
  6203.     var reg4 int x1;
  6204.     var reg6 int x2;
  6205.     { var reg3 int n = x2-x1;
  6206.       if (n>0)
  6207.         { {var reg1 uintB* sp = &curr->image[y][x1];
  6208.            var reg2 uintC count;
  6209.            dotimespC(count,n, { *sp++ = ' '; } );
  6210.           }
  6211.           #if WANT_ATTR
  6212.           {var reg1 uintB* ap = &curr->attr[y][x1];
  6213.            var reg2 uintC count;
  6214.            dotimespC(count,n, { *ap++ = 0; } );
  6215.           }
  6216.           #endif
  6217.           #if WANT_CHARSET
  6218.           {var reg1 uintB* fp = &curr->font[y][x1];
  6219.            var reg2 uintC count;
  6220.            dotimespC(count,n, { *fp++ = 0; } );
  6221.           }
  6222.           #endif
  6223.     }   }
  6224. #endif
  6225.  
  6226. # Bildschirm l÷schen:
  6227. #if WANT_CLEAR_SCREEN
  6228.   local void clear_screen (void);
  6229.   local void clear_screen()
  6230.     { out_capstring(CLcap);
  6231.      {var reg3 uintC y = 0;
  6232.       while (y<rows) { cleared_linepart(y,0,cols); y++; }
  6233.     }}
  6234. #endif
  6235.  
  6236. # Stⁿck einer Zeile l÷schen:
  6237. #if WANT_CLEAR_FROM_BOS || WANT_CLEAR_TO_EOS || WANT_CLEAR_LINE || WANT_CLEAR_FROM_BOL || WANT_CLEAR_TO_EOL
  6238.   local void clear_linepart (int y, int x1, int x2);
  6239.   local void clear_linepart(y,x1,x2)
  6240.     var reg5 int y;
  6241.     var reg4 int x1;
  6242.     var reg6 int x2;
  6243.     { var reg3 int n = x2-x1;
  6244.       if (n>0)
  6245.         { {var reg1 uintB* sp = &curr->image[y][x1];
  6246.            var reg2 uintC count;
  6247.            dotimesC(count,n, { *sp++ = ' '; } );
  6248.           }
  6249.           #if WANT_ATTR
  6250.           {var reg1 uintB* ap = &curr->attr[y][x1];
  6251.            var reg2 uintC count;
  6252.            dotimesC(count,n, { *ap++ = 0; } );
  6253.           }
  6254.           #endif
  6255.           #if WANT_CHARSET
  6256.           {var reg1 uintB* fp = &curr->font[y][x1];
  6257.            var reg2 uintC count;
  6258.            dotimesC(count,n, { *fp++ = 0; } );
  6259.           }
  6260.           #endif
  6261.           if ((x2==cols) && CEcap)
  6262.             { gofromto(curr->y,curr->x,y,x1); curr->y = y; curr->x = x1;
  6263.               out_capstring(CEcap);
  6264.             }
  6265.             else
  6266.             { if ((x2==cols) && (y==rows-1) && AM) { n--; }
  6267.               if (n>0)
  6268.                 {
  6269.                   #if WANT_ATTR
  6270.                   {var reg7 uintB saved_attr = term_attr; change_attr(0);
  6271.                   #endif
  6272.                   #if WANT_CHARSET
  6273.                   {var reg7 uintB saved_charset = term_charset; change_charset(ASCII);
  6274.                   #endif
  6275.                   #if WANT_INSERT
  6276.                   if (curr->insert) { set_insert_mode(FALSE); }
  6277.                   #endif
  6278.                   gofromto(curr->y,curr->x,y,x1);
  6279.                   {var reg1 uintC count;
  6280.                    dotimespC(count,n, { out_char(' '); } );
  6281.                   }
  6282.                   curr->y = y; curr->x = x1+n;
  6283.                   #if WANT_CHARSET
  6284.                   change_charset(saved_charset); }
  6285.                   #endif
  6286.                   #if WANT_ATTR
  6287.                   change_attr(saved_attr); }
  6288.                   #endif
  6289.                   #if WANT_INSERT
  6290.                   if (curr->insert) { set_insert_mode(TRUE); }
  6291.                   #endif
  6292.     }   }   }   }
  6293. #endif
  6294.  
  6295. # Bildschirm bis zum Cursor (ausschlie▀lich) l÷schen:
  6296. #if WANT_CLEAR_FROM_BOS
  6297.   local void clear_from_BOS (void);
  6298.   local void clear_from_BOS()
  6299.     { var reg2 int y0 = curr->y;
  6300.       var reg3 int x0 = curr->x;
  6301.       var reg1 int y = 0;
  6302.       while (y<y0) { clear_linepart(y,0,cols); y++; }
  6303.       clear_linepart(y0,0,x0);
  6304.       gofromto(curr->y,curr->x,y0,x0); curr->y = y0; curr->x = x0;
  6305.     }
  6306. #endif
  6307.  
  6308. # Bildschirm ab Cursor (einschlie▀lich) l÷schen:
  6309. #if WANT_CLEAR_TO_EOS
  6310.   local void clear_to_EOS (void);
  6311.   local void clear_to_EOS()
  6312.     { var reg2 int y0 = curr->y;
  6313.       var reg3 int x0 = curr->x;
  6314.       if (CDcap)
  6315.         { out_capstring(CDcap);
  6316.           cleared_linepart(y0,x0,cols);
  6317.          {var reg1 int y = y0;
  6318.           while (++y < rows) { cleared_linepart(y,0,cols); }
  6319.         }}
  6320.         else
  6321.         { clear_linepart(y0,x0,cols);
  6322.          {var reg1 int y = y0;
  6323.           while (++y < rows) { clear_linepart(y,0,cols); }
  6324.         }}
  6325.       gofromto(curr->y,curr->x,y0,x0); curr->y = y0; curr->x = x0;
  6326.     }
  6327. #endif
  6328.  
  6329. # Cursorzeile l÷schen:
  6330. #if WANT_CLEAR_LINE
  6331.   local void clear_line (void);
  6332.   local void clear_line()
  6333.     { var reg1 int y0 = curr->y;
  6334.       var reg2 int x0 = curr->x;
  6335.       clear_linepart(y0,0,cols);
  6336.       gofromto(curr->y,curr->x,y0,x0); curr->y = y0; curr->x = x0;
  6337.     }
  6338. #endif
  6339.  
  6340. # Cursorzeile bis Cursor (ausschlie▀lich) l÷schen:
  6341. #if WANT_CLEAR_FROM_BOL
  6342.   local void clear_from_BOL (void);
  6343.   local void clear_from_BOL()
  6344.     { var reg1 int y0 = curr->y;
  6345.       var reg2 int x0 = curr->x;
  6346.       clear_linepart(y0,0,x0);
  6347.       gofromto(curr->y,curr->x,y0,x0); curr->y = y0; curr->x = x0;
  6348.     }
  6349. #endif
  6350.  
  6351. # Cursorzeile ab Cursor (einschlie▀lich) l÷schen:
  6352. #if WANT_CLEAR_TO_EOL
  6353.   local void clear_to_EOL (void);
  6354.   local void clear_to_EOL()
  6355.     { var reg1 int y0 = curr->y;
  6356.       var reg2 int x0 = curr->x;
  6357.       clear_linepart(y0,x0,cols);
  6358.       gofromto(curr->y,curr->x,y0,x0); curr->y = y0; curr->x = x0;
  6359.     }
  6360. #endif
  6361.  
  6362. # Einfⁿge-Operationen:
  6363.  
  6364. # alter Zeileninhalt:
  6365. #if WANT_INSERT_1CHAR || WANT_INSERT_CHAR || WANT_DELETE_CHAR
  6366.   local uintB* old_image_y;
  6367.   #if WANT_ATTR
  6368.   local uintB* old_attr_y;
  6369.   #endif
  6370.   #if WANT_CHARSET
  6371.   local uintB* old_font_y;
  6372.   #endif
  6373.   local void save_line_old (int y);
  6374.   local void save_line_old(y)
  6375.     var reg4 int y;
  6376.     { {var reg1 uintB* p1 = &curr->image[y][0];
  6377.        var reg2 uintB* p2 = &old_image_y[0];
  6378.        var reg3 uintC count;
  6379.        dotimesC(count,cols, { *p2++ = *p1++; } );
  6380.       }
  6381.       #if WANT_ATTR
  6382.       {var reg1 uintB* p1 = &curr->attr[y][0];
  6383.        var reg2 uintB* p2 = &old_attr_y[0];
  6384.        var reg3 uintC count;
  6385.        dotimesC(count,cols, { *p2++ = *p1++; } );
  6386.       }
  6387.       #endif
  6388.       #if WANT_CHARSET
  6389.       {var reg1 uintB* p1 = &curr->font[y][0];
  6390.        var reg2 uintB* p2 = &old_font_y[0];
  6391.        var reg3 uintC count;
  6392.        dotimesC(count,cols, { *p2++ = *p1++; } );
  6393.       }
  6394.       #endif
  6395.     }
  6396. #endif
  6397.  
  6398. # Ein Zeichen einfⁿgen:
  6399. #if WANT_INSERT_1CHAR
  6400.   local void insert_1char (uintB c);
  6401.   local void insert_1char(c)
  6402.     var reg6 uintB c;
  6403.     { var reg4 int y = curr->y;
  6404.       var reg5 int x = curr->x;
  6405.       if (x==cols) { x--; } # nicht ⁿber den rechten Rand schreiben!
  6406.       if (ICcap || IMcap)
  6407.         { curr->image[y][x] = c;
  6408.           #if WANT_ATTR
  6409.           curr->attr[y][x] = curr->curr_attr;
  6410.           #endif
  6411.           #if WANT_CHARSET
  6412.           curr->font[y][x] = curr->charsets[curr->curr_charset]; # = term_charset
  6413.           #endif
  6414.           #if WANT_INSERT
  6415.           if (!curr->insert)
  6416.           #endif
  6417.             { set_insert_mode(TRUE); }
  6418.           out_capstring(ICcap); out_char(c);
  6419.           #if WANT_INSERT
  6420.           if (!curr->insert)
  6421.           #endif
  6422.             { set_insert_mode(FALSE); }
  6423.           curr->x = x+1;
  6424.         }
  6425.         else
  6426.         { # alten Zeileninhalt retten:
  6427.           save_line_old(y);
  6428.           # neuen Zeileninhalt bilden:
  6429.           {var reg1 uintB* p1 = &curr->image[y][x];
  6430.            var reg2 uintB* p2 = &old_image[x];
  6431.            var reg3 uintC count;
  6432.            *p1++ = c;
  6433.            dotimesC(count,cols-1-x, { *p1++ = *p2++; } );
  6434.           }
  6435.           #if WANT_ATTR
  6436.           {var reg1 uintB* p1 = &curr->attr[y][x];
  6437.            var reg2 uintB* p2 = &old_attr[x];
  6438.            var reg3 uintC count;
  6439.            *p1++ = curr->curr_attr;
  6440.            dotimesC(count,cols-1-x, { *p1++ = *p2++; } );
  6441.           }
  6442.           #endif
  6443.           #if WANT_CHARSET
  6444.           {var reg1 uintB* p1 = &curr->font[y][x];
  6445.            var reg2 uintB* p2 = &old_font[x];
  6446.            var reg3 uintC count;
  6447.            *p1++ = term_charset; # = curr->charsets[curr->curr_charset]
  6448.            dotimesC(count,cols-1-x, { *p1++ = *p2++; } );
  6449.           }
  6450.           #endif
  6451.           # Zeile anzeigen:
  6452.           redisplay_line RLargs(old_image,old_attr,old_font,y,x,cols);
  6453.           x++;
  6454.           gofromto(last_y,last_x,y,x); curr->x = x;
  6455.     }   }
  6456. #endif
  6457.  
  6458. # Platz fⁿr n Zeichen machen:
  6459. #if WANT_INSERT_CHAR
  6460.   local void insert_char (uintC n);
  6461.   local void insert_char(n)
  6462.     var reg6 uintC n;
  6463.     { var reg5 int y = curr->y;
  6464.       var reg4 int x = curr->x;
  6465.       if (n > cols-x) { n = cols-x; }
  6466.       if (n==0) return;
  6467.       # alten Zeileninhalt retten:
  6468.       save_line_old(y);
  6469.       # neuen Zeileninhalt bilden:
  6470.       {var reg1 uintB* p1 = &curr->image[y][x];
  6471.        var reg2 uintB* p2 = &old_image[x];
  6472.        var reg3 uintC count;
  6473.        dotimespC(count,n, { *p1++ = ' '; } );
  6474.        dotimesC(count,cols-x-n, { *p1++ = *p2++; } );
  6475.       }
  6476.       #if WANT_ATTR
  6477.       {var reg1 uintB* p1 = &curr->attr[y][x];
  6478.        var reg2 uintB* p2 = &old_attr[x];
  6479.        var reg3 uintC count;
  6480.        dotimespC(count,n, { *p1++ = 0; } );
  6481.        dotimesC(count,cols-x-n, { *p1++ = *p2++; } );
  6482.       }
  6483.       #endif
  6484.       #if WANT_CHARSET
  6485.       {var reg1 uintB* p1 = &curr->font[y][x];
  6486.        var reg2 uintB* p2 = &old_font[x];
  6487.        var reg3 uintC count;
  6488.        dotimespC(count,n, { *p1++ = 0; } );
  6489.        dotimesC(count,cols-x-n, { *p1++ = *p2++; } );
  6490.       }
  6491.       #endif
  6492.       if (CICcap && (n > 1))
  6493.         {
  6494.           #if WANT_INSERT
  6495.           if (curr->insert) { set_insert_mode(FALSE); }
  6496.           #endif
  6497.           out_cap1string(CICcap,n);
  6498.           {var reg1 uintC count;
  6499.            dotimespC(count,n, { out_char(' '); } );
  6500.           }
  6501.           #if WANT_INSERT
  6502.           if (curr->insert) { set_insert_mode(TRUE); }
  6503.           #endif
  6504.           gofromto(y,x+n,y,x);
  6505.         }
  6506.       elif (ICcap || IMcap)
  6507.         {
  6508.           #if WANT_INSERT
  6509.           if (!curr->insert)
  6510.           #endif
  6511.             { set_insert_mode(TRUE); }
  6512.           {var reg1 uintC count;
  6513.            dotimespC(count,n, { out_capstring(ICcap); out_char(' '); } );
  6514.           }
  6515.           #if WANT_INSERT
  6516.           if (!curr->insert)
  6517.           #endif
  6518.             { set_insert_mode(FALSE); }
  6519.           gofromto(y,x+n,y,x);
  6520.         }
  6521.       else
  6522.         { redisplay_line RLargs(old_image,old_attr,old_font,y,x,cols);
  6523.           gofromto(last_y,last_x,y,x);
  6524.         }
  6525.     }
  6526. #endif
  6527.  
  6528. # Zeilen einfⁿgen:
  6529. #if WANT_INSERT_LINE
  6530.   local void insert_line (uintC n);
  6531.   local void insert_line(n)
  6532.     var reg2 uintC n;
  6533.     { if (n > curr->bot - curr->y + 1) { n = curr->bot - curr->y + 1; }
  6534.       if (n==0) return;
  6535.      {var reg3 int oldtop = curr->top;
  6536.       curr->top = curr->y;
  6537.       {var reg1 uintC count;
  6538.        dotimespC(count,n, { scroll_down(); } );
  6539.       }
  6540.       if (ALcap || CALcap)
  6541.         { gofromto(curr->y,curr->x,curr->y,0); # an den Zeilenanfang
  6542.           if ((CALcap && (n>1)) || !ALcap)
  6543.             { out_cap1string(CALcap,n); }
  6544.             else
  6545.             { var reg1 uintC count;
  6546.               dotimespC(count,n, { out_capstring(ALcap); } );
  6547.             }
  6548.           gofromto(curr->y,0,curr->y,curr->x);
  6549.         }
  6550.       elif (CScap && SRcap)
  6551.         { out_capstring(tgoto(CScap,curr->bot,curr->top));
  6552.           gofromto(-1,-1,curr->top,0);
  6553.           {var reg1 uintC count;
  6554.            dotimespC(count,n, { out_capstring(SRcap); } );
  6555.           }
  6556.           out_capstring(tgoto(CScap,curr->bot,oldtop));
  6557.           gofromto(-1,-1,curr->y,curr->x);
  6558.         }
  6559.       else
  6560.         { redisplay(); }
  6561.       curr->top = oldtop;
  6562.     }}
  6563. #endif
  6564.  
  6565. # L÷sch-Operationen:
  6566.  
  6567. # Characters l÷schen:
  6568. #if WANT_DELETE_CHAR
  6569.   local void delete_char (uintC n);
  6570.   local void delete_char(n)
  6571.     var reg6 uintC n;
  6572.     { var reg5 int y = curr->y;
  6573.       var reg4 int x = curr->x;
  6574.       if (n > cols-x) { n = cols-x; }
  6575.       if (n==0) return;
  6576.       # alten Zeileninhalt retten:
  6577.       save_line_old(y);
  6578.       # neuen Zeileninhalt bilden:
  6579.       {var reg1 uintB* p1 = &curr->image[y][x];
  6580.        var reg2 uintB* p2 = &old_image[x];
  6581.        var reg3 uintC count;
  6582.        dotimesC(count,cols-x-n, { *p1++ = *p2++; } );
  6583.        dotimespC(count,n, { *p1++ = ' '; } );
  6584.       }
  6585.       #if WANT_ATTR
  6586.       {var reg1 uintB* p1 = &curr->attr[y][x];
  6587.        var reg2 uintB* p2 = &old_attr[x];
  6588.        var reg3 uintC count;
  6589.        dotimesC(count,cols-x-n, { *p1++ = *p2++; } );
  6590.        dotimespC(count,n, { *p1++ = 0; } );
  6591.       }
  6592.       #endif
  6593.       #if WANT_CHARSET
  6594.       {var reg1 uintB* p1 = &curr->font[y][x];
  6595.        var reg2 uintB* p2 = &old_font[x];
  6596.        var reg3 uintC count;
  6597.        dotimesC(count,cols-x-n, { *p1++ = *p2++; } );
  6598.        dotimespC(count,n, { *p1++ = 0; } );
  6599.       }
  6600.       #endif
  6601.       if (CDCcap && ((n>1) || !DCcap))
  6602.         { out_cap1string(CDCcap,n); }
  6603.       elif (DCcap)
  6604.         { var reg1 uintC count;
  6605.           dotimespC(count,n, { out_capstring(DCcap); } );
  6606.         }
  6607.       else
  6608.         { redisplay_line RLargs(old_image,old_attr,old_font,y,x,cols);
  6609.           gofromto(last_y,last_x,y,x);
  6610.         }
  6611.     }
  6612. #endif
  6613.  
  6614. # Zeilen l÷schen:
  6615. #if WANT_DELETE_LINE
  6616.   local void delete_line (uintC n);
  6617.   local void delete_line(n)
  6618.     var reg2 uintC n;
  6619.     { if (n > curr->bot - curr->y + 1) { n = curr->bot - curr->y + 1; }
  6620.       if (n==0) return;
  6621.      {var reg3 int oldtop = curr->top;
  6622.       curr->top = curr->y;
  6623.       {var reg1 uintC count;
  6624.        dotimespC(count,n, { scroll_up(); } );
  6625.       }
  6626.       if (DLcap || CDLcap)
  6627.         { gofromto(curr->y,curr->x,curr->y,0); # an den Zeilenanfang
  6628.           if ((CDLcap && (n>1)) || !DLcap)
  6629.             { out_cap1string(CDLcap,n); }
  6630.             else
  6631.             { var reg1 uintC count;
  6632.               dotimespC(count,n, { out_capstring(DLcap); } );
  6633.             }
  6634.           gofromto(curr->y,0,curr->y,curr->x);
  6635.         }
  6636.       elif (CScap)
  6637.         { out_capstring(tgoto(CScap,curr->bot,curr->top));
  6638.           gofromto(-1,-1,curr->bot,0);
  6639.           {var reg1 uintC count;
  6640.            dotimespC(count,n, { out_capstring(SFcap); } );
  6641.           }
  6642.           out_capstring(tgoto(CScap,curr->bot,oldtop));
  6643.           gofromto(-1,-1,curr->y,curr->x);
  6644.         }
  6645.       else
  6646.         { redisplay(); }
  6647.       curr->top = oldtop;
  6648.     }}
  6649. #endif
  6650.  
  6651. # Ein Zeichen ausgeben:
  6652. #if WANT_OUTPUT_1CHAR
  6653.   local void output_1char (uintB c);
  6654.   local void output_1char(c)
  6655.     var reg3 uintB c;
  6656.     {
  6657.       #if WANT_INSERT
  6658.       if (curr->insert)
  6659.         { insert_1char(c); }
  6660.         else
  6661.       #endif
  6662.         { var reg1 int y = curr->y;
  6663.           var reg2 int x = curr->x;
  6664.           if (x==cols) { x--; } # nicht ⁿber den rechten Rand schreiben!
  6665.           curr->image[y][x] = c;
  6666.           #if WANT_ATTR
  6667.           curr->attr[y][x] = curr->curr_attr;
  6668.           #endif
  6669.           #if WANT_CHARSET
  6670.           curr->font[y][x] = curr->charsets[curr->curr_charset]; # = term_charset
  6671.           #endif
  6672.           x++;
  6673.           if (!(AM && (x==cols) && (curr->y==curr->bot))) # rechte untere Ecke evtl. freilassen
  6674.             { out_char(c); } # Zeichen ausgeben
  6675.           curr->x = x; # Cursor rⁿckt um eins weiter
  6676.           if (x==cols) # au▀er wenn er schon ganz rechts war
  6677.             { gofromto(-1,-1,curr->y,curr->x); }
  6678.     }   }
  6679. #endif
  6680.  
  6681. #if WANT_SAVE
  6682.  
  6683. # gespeicherte Cursor-Position:
  6684.   local void save_cursor (void);
  6685.   local void save_cursor()
  6686.     { curr->saved_x = curr->x;
  6687.       curr->saved_y = curr->y;
  6688.       #if WANT_ATTR
  6689.       curr->saved_curr_attr = curr->curr_attr;
  6690.       #endif
  6691.       #if WANT_CHARSET
  6692.       curr->saved_curr_charset = curr->curr_charset;
  6693.       {var reg1 uintC i = 0;
  6694.        while (i<charset_count) { curr->saved_charsets[i] = curr->charsets[i]; i++; }
  6695.       }
  6696.       #endif
  6697.       curr->saved = TRUE;
  6698.     }
  6699.   local void restore_cursor (void);
  6700.   local void restore_cursor()
  6701.     { if (curr->saved)
  6702.         { gofromto(curr->y,curr->x,curr->saved_y,curr->saved_x);
  6703.           curr->y = curr->saved_y; curr->x = curr->saved_x;
  6704.           #if WANT_ATTR
  6705.           curr->curr_attr = curr->saved_curr_attr;
  6706.           change_attr(curr->curr_attr);
  6707.           #endif
  6708.           #if WANT_CHARSET
  6709.           curr->curr_charset = curr->saved_curr_charset;
  6710.           {var reg1 uintC i = 0;
  6711.            while (i<charset_count) { curr->charsets[i] = curr->saved_charsets[i]; i++; }
  6712.           }
  6713.           change_charset(curr->charsets[curr->curr_charset]);
  6714.           #endif
  6715.     }   }
  6716.  
  6717. #endif
  6718.  
  6719. # Initialisiert das Terminal.
  6720. # Liefert NULL falls OK, einen Fehlerstring sonst.
  6721.   local boolean term_initialized = FALSE;
  6722.   local char* init_term (void);
  6723.   local char* init_term()
  6724.     { var char tbuf[4096]; # interner Buffer fⁿr die Termcap-Routinen
  6725.       if (term_initialized) { return NULL; } # schon initialisiert -> OK
  6726.       # Terminal-Typ abfragen:
  6727.       begin_system_call();
  6728.       { var reg1 char* s = getenv("TERM");
  6729.         if (s==NULL)
  6730.           { end_system_call();
  6731.             return (DEUTSCH ? "Environment enthΣlt keine TERM-Variable." :
  6732.                     ENGLISH ? "environment has no TERM variable" :
  6733.                     FRANCAIS ? "L'environnment ne contient pas de variable TERM." :
  6734.                     ""
  6735.                    );
  6736.           }
  6737.         if (!(tgetent(tbuf,s)==1))
  6738.           { end_system_call();
  6739.             pushSTACK(asciz_to_string(s));
  6740.             return (DEUTSCH ? "TERMCAP kennt Terminal-Typ ~ nicht." :
  6741.                     ENGLISH ? "terminal type ~ unknown to termcap" :
  6742.                     FRANCAIS ? "TERMCAP ne connait pas le type d'Θcran ~." :
  6743.                     ""
  6744.                    );
  6745.           }
  6746.       }
  6747.       { var reg1 int i = tgetnum("co");
  6748.         cols = (i>0 ? i : 80);
  6749.       }
  6750.       { var reg1 int i = tgetnum("li");
  6751.         rows = (i>0 ? i : 24);
  6752.       }
  6753.       #ifdef EMUNIX
  6754.       # Obwohl das eigentlich unsauber ist, holen wir uns die aktuelle Bildschirm-
  6755.       # gr÷▀e mit _scrsize().
  6756.       #ifdef EMUNIX_OLD_8d
  6757.       if (!(_osmode == DOS_MODE))
  6758.       #endif
  6759.       { var int scrsize[2];
  6760.         _scrsize(&!scrsize);
  6761.         if (scrsize[0] > 0) { cols = scrsize[0]; }
  6762.         if (scrsize[1] > 0) { rows = scrsize[1]; }
  6763.       }
  6764.       #endif
  6765.       if (tgetflag("hc"))
  6766.         { end_system_call();
  6767.           return (DEUTSCH ? "Unzureichendes Terminal: Hardcopy-Terminal." :
  6768.                   ENGLISH ? "insufficient terminal: hardcopy terminal" :
  6769.                   FRANCAIS ? "Terminal insuffisant : imprimante au lieu d'Θcran." :
  6770.                   ""
  6771.                  );
  6772.         }
  6773.       if (tgetflag("os"))
  6774.         { end_system_call();
  6775.           return (DEUTSCH ? "Unzureichendes Terminal: Kann Ausgegebenes nicht mehr l÷schen." :
  6776.                   ENGLISH ? "insufficient terminal: overstrikes, cannot clear output" :
  6777.                   FRANCAIS ? "Terminal insuffisant : ne peut rien effacer." :
  6778.                   ""
  6779.                  );
  6780.         }
  6781.       if (tgetflag("ns"))
  6782.         { end_system_call();
  6783.           return (DEUTSCH ? "Unzureichendes Terminal: Kann nicht scrollen." :
  6784.                   ENGLISH ? "insufficient terminal: cannot scroll" :
  6785.                   FRANCAIS ? "Terminal insuffisant : pas de dΘfilement." :
  6786.                   ""
  6787.                  );
  6788.         }
  6789.       if (!(CLcap = tgetstr("cl",&tp)))
  6790.         { # K÷nnte CLcap = "\n\n\n\n"; als Default nehmen ('weird HPs')
  6791.           end_system_call();
  6792.           return (DEUTSCH ? "Unzureichendes Terminal: Kann Bildschirm nicht l÷schen." :
  6793.                   ENGLISH ? "insufficient terminal: cannot clear screen" :
  6794.                   FRANCAIS ? "Terminal insuffisant : ne peut pas effacer l'Θcran." :
  6795.                   ""
  6796.                  );
  6797.         }
  6798.       if (!(CMcap = tgetstr("cm",&tp)))
  6799.         { end_system_call();
  6800.           return (DEUTSCH ? "Unzureichendes Terminal: Kann Cursor nicht willkⁿrlich positionieren." :
  6801.                   ENGLISH ? "insufficient terminal: cannot position cursor randomly" :
  6802.                   FRANCAIS ? "Terminal insuffisant : ne peut pas placer le curseur n'importe o∙." :
  6803.                   ""
  6804.                  );
  6805.         }
  6806.       # Capabilities initialisieren:
  6807.       AM = tgetflag("am"); if (tgetflag("LP")) { AM = FALSE; }
  6808.       TIcap = tgetstr("ti",&tp);
  6809.       TEcap = tgetstr("te",&tp);
  6810.       # BLcap = tgetstr("bl",&tp); if (!BLcap) BLcap = "\007";
  6811.       # VBcap = tgetstr("vb",&tp);
  6812.       BCcap = tgetstr("bc",&tp); if (!BCcap) BCcap = (tgetflag("bs") ? "\b" : tgetstr("le",&tp));
  6813.       CRcap = tgetstr("cr",&tp); if (!CRcap) CRcap = "\r";
  6814.       NLcap = tgetstr("nl",&tp); if (!NLcap) NLcap = "\n";
  6815.       DOcap = tgetstr("do",&tp); if (!DOcap) DOcap = NLcap;
  6816.       UPcap = tgetstr("up",&tp);
  6817.       NDcap = tgetstr("nd",&tp);
  6818.       IScap = tgetstr("is",&tp);
  6819.       #if WANT_ATTR
  6820.       if ((tgetnum("sg") > 0) || (tgetnum("ug") > 0))
  6821.         # Beim Umschalten in Standout-Mode oder beim Umschalten in den
  6822.         # Underline-Mode gibt's Leerstellen -> unbrauchbar!
  6823.         { SOcap = NULL; SEcap = NULL; UScap = NULL; UEcap = NULL;
  6824.           MBcap = NULL; MDcap = NULL; MHcap = NULL; MRcap = NULL; MEcap = NULL;
  6825.         }
  6826.         else
  6827.         { SOcap = tgetstr("so",&tp);
  6828.           SEcap = tgetstr("se",&tp);
  6829.           UScap = tgetstr("us",&tp);
  6830.           UEcap = tgetstr("ue",&tp);
  6831.           if (!UScap && !UEcap) # kein Underline?
  6832.             { UScap = SOcap; UEcap = SEcap; } # nimm Standout als Ersatz
  6833.           MBcap = tgetstr("mb",&tp);
  6834.           MDcap = tgetstr("md",&tp);
  6835.           MHcap = tgetstr("mh",&tp);
  6836.           MRcap = tgetstr("mr",&tp);
  6837.           MEcap = tgetstr("me",&tp);
  6838.           # Does ME also reverse the effect of SO and/or US?  This is not
  6839.           # clearly specified by the termcap manual.
  6840.           # Anyway, we should at least look whether ME/SE/UE are equal:
  6841.           if (UEcap && SEcap && asciz_equal(UEcap,SEcap)) { UEcap = NULL; }
  6842.           if (UEcap && MEcap && asciz_equal(UEcap,MEcap)) { UEcap = NULL; }
  6843.           if (SEcap && MEcap && asciz_equal(SEcap,MEcap)) { SEcap = NULL; }
  6844.           # tgetstr("uc",&tp) liefert ein underline-character. Dann jeweils
  6845.           # in redisplay_help() und output_1char() nach dem out_char() noch
  6846.           # backspace() und out_capstring(UCcap) durchfⁿhren.
  6847.           # Fⁿr welche Terminals lohnt sich das??
  6848.         }
  6849.       #endif
  6850.       #if WANT_CHARSET
  6851.       ISO2022 = tgetflag("G0");
  6852.       #endif
  6853.       CScap = tgetstr("cs",&tp);
  6854.       #if WANT_DELETE_LINE
  6855.       SFcap = tgetstr("sf",&tp); if (!SFcap) SFcap = NLcap;
  6856.       #endif
  6857.       #if WANT_CURSOR_REVLINEFEED || WANT_INSERT_LINE
  6858.       SRcap = tgetstr("sr",&tp);
  6859.       #endif
  6860.       #if WANT_CLEAR_FROM_BOS || WANT_CLEAR_TO_EOS || WANT_CLEAR_LINE || WANT_CLEAR_FROM_BOL || WANT_CLEAR_TO_EOL
  6861.       CEcap = tgetstr("ce",&tp);
  6862.       #endif
  6863.       #if WANT_CLEAR_TO_EOS
  6864.       CDcap = tgetstr("cd",&tp);
  6865.       #endif
  6866.       #if WANT_CURSOR_REVLINEFEED || WANT_INSERT_LINE
  6867.       ALcap = tgetstr("al",&tp);
  6868.       #endif
  6869.       #if WANT_DELETE_LINE
  6870.       DLcap = tgetstr("dl",&tp);
  6871.       #endif
  6872.       #if WANT_DELETE_CHAR
  6873.       DCcap = tgetstr("dc",&tp);
  6874.       #endif
  6875.       #if WANT_INSERT_1CHAR || WANT_INSERT_CHAR
  6876.       ICcap = tgetstr("ic",&tp);
  6877.       #endif
  6878.       #if WANT_INSERT_CHAR
  6879.       CICcap = tgetstr("IC",&tp);
  6880.       #endif
  6881.       #if WANT_INSERT_LINE
  6882.       CALcap = tgetstr("AL",&tp);
  6883.       #endif
  6884.       #if WANT_DELETE_CHAR
  6885.       CDCcap = tgetstr("DC",&tp);
  6886.       #endif
  6887.       #if WANT_DELETE_LINE
  6888.       CDLcap = tgetstr("DL",&tp);
  6889.       #endif
  6890.       IMcap = tgetstr("im",&tp);
  6891.       EIcap = tgetstr("ei",&tp);
  6892.       if (tgetflag ("in")) # Insert-Modus unbrauchbar?
  6893.         { IMcap = NULL; EIcap = NULL;
  6894.           #if WANT_INSERT_1CHAR || WANT_INSERT_CHAR
  6895.           ICcap = NULL;
  6896.           #endif
  6897.           #if WANT_INSERT_CHAR
  6898.           CICcap = NULL;
  6899.           #endif
  6900.         }
  6901.       if (IMcap && (IMcap[0]==0)) { IMcap = NULL; } # IMcap leer?
  6902.       if (EIcap && (EIcap[0]==0)) { EIcap = NULL; } # EIcap leer?
  6903.       #if WANT_INSERT_1CHAR || WANT_INSERT_CHAR
  6904.       if (ICcap && (ICcap[0]==0)) { ICcap = NULL; } # ICcap leer?
  6905.       #endif
  6906.       # Kosten der Capabilities berechnen:
  6907.       IMcost = cap_cost(IMcap);
  6908.       EIcost = cap_cost(EIcap);
  6909.       BCcost = cap_cost(BCcap);
  6910.       NDcost = cap_cost(NDcap);
  6911.       DOcost = cap_cost(DOcap);
  6912.       #ifndef NL_HACK
  6913.       # Falls DOcap ein LF ausgibt, ist nicht sicher, ob dies auch als solches
  6914.       # (und nicht als CR/LF) beim Terminal ankommt. In diesem Fall erklΣren
  6915.       # wir DOcap fⁿr unbrauchbar. Das erspart uns den NL_HACK.
  6916.       if (DOcap[0]=='\n') { DOcost = EXPENSIVE; }
  6917.       #endif
  6918.       UPcost = cap_cost(UPcap);
  6919.       CRcost = cap_cost(CRcap);
  6920.       # Hilfs-Datenstrukturen bereitstellen:
  6921.       {var reg1 uintB* ptr = malloc(cols*sizeof(uintB));
  6922.        var reg2 uintC count;
  6923.        blank = ptr;
  6924.        dotimespC(count,cols, { *ptr++ = ' '; } );
  6925.       }
  6926.       #if WANT_ATTR || WANT_CHARSET
  6927.       {var reg1 uintB* ptr = malloc(cols*sizeof(uintB));
  6928.        var reg2 uintC count;
  6929.        null = ptr;
  6930.        dotimespC(count,cols, { *ptr++ = 0; } );
  6931.       }
  6932.       #endif
  6933.       #if WANT_INSERT_1CHAR || WANT_INSERT_CHAR || WANT_DELETE_CHAR
  6934.       old_image_y = malloc(cols*sizeof(uintB));
  6935.       #if WANT_ATTR
  6936.       old_attr_y = malloc(cols*sizeof(uintB));
  6937.       #endif
  6938.       #if WANT_CHARSET
  6939.       old_font_y = malloc(cols*sizeof(uintB));
  6940.       #endif
  6941.       #endif
  6942.       end_system_call();
  6943.       term_initialized = TRUE;
  6944.       return NULL;
  6945.     }
  6946.  
  6947. #ifdef NL_HACK
  6948.  
  6949. # Wenn NLcap = "\n" ist, mⁿssen wir ein "stty -onlcr" durchfⁿhren, weil sonst
  6950. # das NL vom Terminal-Driver in ein CR umgewandelt wird, bevor es beim
  6951. # Terminal ankommt.
  6952.   local void term_nlraw (void);
  6953.   local void term_nlunraw (void);
  6954. #if defined(UNIX_TERM_TERMIOS)
  6955.   static unsigned long old_c_oflag = 0;
  6956.   local void term_nlraw()
  6957.     { var struct termios oldtermio;
  6958.       if (!( tcgetattr(stdout_handle,&oldtermio) ==0))
  6959.         { if (!(errno==ENOTTY)) { OS_error(); } }
  6960.       old_c_oflag = oldtermio.c_oflag;
  6961.       oldtermio.c_oflag &= ~ONLCR;
  6962.       if (!( TCSETATTR(stdout_handle,TCSAFLUSH,&oldtermio) ==0))
  6963.         { if (!(errno==ENOTTY)) { OS_error(); } }
  6964.     }
  6965.   local void term_nlunraw()
  6966.     { if (old_c_oflag & ONLCR)
  6967.         { var struct termios oldtermio;
  6968.           if (!( tcgetattr(stdout_handle,&oldtermio) ==0))
  6969.             { if (!(errno==ENOTTY)) { OS_error(); } }
  6970.           oldtermio.c_oflag |= ONLCR;
  6971.           if (!( TCSETATTR(stdout_handle,TCSAFLUSH,&oldtermio) ==0))
  6972.             { if (!(errno==ENOTTY)) { OS_error(); } }
  6973.     }   }
  6974. #elif defined(UNIX_TERM_TERMIO) || defined(EMUNIX)
  6975.   static unsigned long old_c_oflag = 0;
  6976.   local void term_nlraw()
  6977.     { var struct termio oldtermio;
  6978.       if (!( ioctl(stdout_handle,TCGETA,&oldtermio) ==0))
  6979.         { if (!(errno==ENOTTY)) { OS_error(); } }
  6980.       old_c_oflag = oldtermio.c_oflag;
  6981.       oldtermio.c_oflag &= ~ONLCR;
  6982.       if (!( ioctl(stdout_handle,TCSETAF,&oldtermio) ==0))
  6983.         { if (!(errno==ENOTTY)) { OS_error(); } }
  6984.     }
  6985.   local void term_nlunraw()
  6986.     { if (old_c_oflag & ONLCR)
  6987.         { var struct termio oldtermio;
  6988.           if (!( ioctl(stdout_handle,TCGETA,&oldtermio) ==0))
  6989.             { if (!(errno==ENOTTY)) { OS_error(); } }
  6990.           oldtermio.c_oflag |= ONLCR;
  6991.           if (!( ioctl(stdout_handle,TCSETAF,&oldtermio) ==0))
  6992.             { if (!(errno==ENOTTY)) { OS_error(); } }
  6993.     }   }
  6994. #elif defined(UNIX_TERM_SGTTY)
  6995.   static unsigned long old_sg_flags = 0;
  6996.   local void term_nlraw()
  6997.     { var struct sgttyb oldsgttyb;
  6998.       if (!( ioctl(stdout_handle,TIOCGETP,&oldsgttyb) ==0))
  6999.         { if (!(errno==ENOTTY)) { OS_error(); } }
  7000.       old_sg_flags = oldsgttyb.sg_flags;
  7001.       oldsgttyb.sg_flags &= ~CRMOD;
  7002.       if (!( ioctl(stdout_handle,TIOCSETP,&oldsgttyb) ==0))
  7003.         { if (!(errno==ENOTTY)) { OS_error(); } }
  7004.     }
  7005.   local void term_nlunraw()
  7006.     { if (old_sg_flags & CRMOD)
  7007.         { var struct sgttyb oldsgttyb;
  7008.           if (!( ioctl(stdout_handle,TIOCGETP,&oldsgttyb) ==0))
  7009.             { if (!(errno==ENOTTY)) { OS_error(); } }
  7010.           oldsgttyb.sg_flags |= CRMOD;
  7011.           if (!( ioctl(stdout_handle,TIOCSETP,&oldsgttyb) ==0))
  7012.             { if (!(errno==ENOTTY)) { OS_error(); } }
  7013.     }   }
  7014. #endif
  7015.  
  7016. #endif # NL_HACK
  7017.  
  7018. # Beginn des Arbeitens mit diesem Paket:
  7019.   local void start_term (void);
  7020.   local void start_term()
  7021.     {
  7022.       #ifdef NL_HACK
  7023.       if (NLcap[0] == '\n') { term_nlraw(); }
  7024.       #endif
  7025.       out_capstring (IScap);
  7026.       out_capstring (TIcap);
  7027.     }
  7028.  
  7029. # Ende des Arbeitens mit diesem Paket:
  7030.   local void end_term (void);
  7031.   local void end_term()
  7032.     { out_capstring (TEcap);
  7033.       out_capstring (IScap);
  7034.       #ifdef MSDOS # wie testet man auf Farb-ANSI-Terminal??
  7035.       # Auf ANSI-Terminals mit mehreren Farben: TEcap setzt die Farben zurⁿck.
  7036.       out_capstring(CLcap); # Bildschirm l÷schen, diesmal in der normalen Farbe
  7037.       #endif
  7038.       #ifdef NL_HACK
  7039.       if (NLcap[0] == '\n') { term_nlunraw(); }
  7040.       #endif
  7041.     }
  7042.  
  7043. # Initialisiert das Window curr.
  7044.   local void init_curr (void);
  7045.   local void init_curr()
  7046.     { {var reg1 uintB** ptr = malloc(rows*sizeof(uintB*));
  7047.        var reg2 uintC count;
  7048.        curr->image = ptr;
  7049.        dotimespC(count,rows, { *ptr++ = malloc(cols*sizeof(uintB)); } );
  7050.       }
  7051.       #if WANT_ATTR
  7052.       {var reg1 uintB** ptr = malloc(rows*sizeof(uintB*));
  7053.        var reg2 uintC count;
  7054.        curr->attr = ptr;
  7055.        dotimespC(count,rows, { *ptr++ = malloc(cols*sizeof(uintB)); } );
  7056.       }
  7057.       # Attribute ausschalten:
  7058.       out_capstring(UEcap); # alle aus
  7059.       out_capstring(SEcap);
  7060.       out_capstring(MEcap);
  7061.       term_attr = curr->curr_attr = 0;
  7062.       #endif
  7063.       #if WANT_CHARSET
  7064.       {var reg1 uintB** ptr = malloc(rows*sizeof(uintB*));
  7065.        var reg2 uintC count;
  7066.        curr->font = ptr;
  7067.        dotimespC(count,rows, { *ptr++ = malloc(cols*sizeof(uintB)); } );
  7068.       }
  7069.       {var reg1 uintC i = 0;
  7070.        while (i<charset_count) { curr->charsets[i] = ASCII; i++; }
  7071.       }
  7072.       curr->curr_charset = 0;
  7073.       if (ISO2022) { out_char(ESC); out_char('('); out_char('B'); } /*)*/
  7074.       term_charset = ASCII;
  7075.       #endif
  7076.       curr->x = 0; curr->y = 0;
  7077.       curr->top = 0; curr->bot = rows-1;
  7078.       #if WANT_INSERT
  7079.       curr->insert = FALSE;
  7080.       #endif
  7081.       #if WANT_SAVE
  7082.       curr->saved = FALSE;
  7083.       #endif
  7084.       if (CScap) { out_capstring(tgoto(CScap,curr->bot,curr->top)); }
  7085.       clear_screen();
  7086.     }
  7087.  
  7088. # ------------------------------------------------------------------------------
  7089.  
  7090. # UP: Ein Zeichen auf einen Window-Stream ausgeben.
  7091. # wr_ch_window(&stream,ch);
  7092. # > stream: Window-Stream
  7093. # > ch: auszugebendes Zeichen
  7094.   local void wr_ch_window (object* stream_, object ch);
  7095.   local void wr_ch_window(stream_,ch)
  7096.     var reg2 object* stream_;
  7097.     var reg3 object ch;
  7098.     { if (!string_char_p(ch)) { fehler_wr_string_char(*stream_,ch); } # ch sollte String-Char sein
  7099.      {var reg1 uintB c = char_code(ch); # Code des Zeichens
  7100.       if (graphic_char_p(c))
  7101.         { if (curr->x == cols) { cursor_return(); cursor_linefeed(); } # Wrap!
  7102.           output_1char(c);
  7103.         }
  7104.       elif (c == NL)
  7105.         { cursor_return(); cursor_linefeed(); }
  7106.       elif (c == BS)
  7107.         { var reg4 int x0 = curr->x;
  7108.           if (x0>0)
  7109.             { var reg5 int y0 = curr->y;
  7110.               clear_linepart(y0,x0-1,x0);
  7111.               gofromto(curr->y,curr->x,y0,x0-1); curr->y = y0; curr->x = x0-1;
  7112.         }   }
  7113.     }}
  7114.  
  7115. LISPFUNN(make_window,0)
  7116.   { var reg2 object stream =
  7117.       allocate_stream(strmflags_wr_ch_B,strmtype_window,strm_len+1);
  7118.       # Flags: nur WRITE-CHAR erlaubt
  7119.     # und fⁿllen:
  7120.     var reg1 Stream s = TheStream(stream);
  7121.       s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  7122.       s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  7123.       s->strm_rd_ch = P(rd_ch_dummy); # READ-CHAR unm÷glich
  7124.       s->strm_rd_ch_last = NIL; # Lastchar := NIL
  7125.       s->strm_wr_ch = P(wr_ch_window); # WRITE-CHAR-Pseudofunktion
  7126.       s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  7127.       #ifdef STRM_WR_SS
  7128.       s->strm_wr_ss = P(wr_ss_dummy_nogc);
  7129.       #endif
  7130.     # Initialisieren:
  7131.     {var reg3 char* ergebnis = init_term();
  7132.      if (!(ergebnis==NULL)) { fehler(error,ergebnis); }
  7133.     }
  7134.     start_term();
  7135.     init_curr();
  7136.     value1 = stream; mv_count=1;
  7137.   }
  7138.  
  7139. # Schlie▀t einen Window-Stream.
  7140.   local void close_window (object stream);
  7141.   local void close_window(stream)
  7142.     var reg1 object stream;
  7143.     { end_term(); }
  7144.  
  7145. LISPFUNN(window_size,1)
  7146.   { check_window_stream(popSTACK());
  7147.     value1 = fixnum(rows); # Variablen rows,cols abfragen
  7148.     value2 = fixnum(cols);
  7149.     mv_count=2;
  7150.   }
  7151.  
  7152. LISPFUNN(window_cursor_position,1)
  7153.   { check_window_stream(popSTACK());
  7154.     value1 = fixnum(curr->y);
  7155.     value2 = fixnum(curr->x);
  7156.     mv_count=2;
  7157.   }
  7158.  
  7159. LISPFUNN(set_window_cursor_position,3)
  7160.   { check_window_stream(STACK_2);
  7161.    {var reg1 uintL line = posfixnum_to_L(STACK_1);
  7162.     var reg2 uintL column = posfixnum_to_L(STACK_0);
  7163.     if ((line < rows) && (column < cols))
  7164.       { gofromto(curr->y,curr->x,line,column); # Cursor positionieren
  7165.         curr->y = line; curr->x = column;
  7166.       }
  7167.     value1 = STACK_1; value2 = STACK_0; mv_count=2; skipSTACK(3);
  7168.   }}
  7169.  
  7170. LISPFUNN(clear_window,1)
  7171.   { check_window_stream(popSTACK());
  7172.     clear_screen();
  7173.     value1 = NIL; mv_count=0;
  7174.   }
  7175.  
  7176. LISPFUNN(clear_window_to_eot,1)
  7177.   { check_window_stream(popSTACK());
  7178.     clear_to_EOS();
  7179.     value1 = NIL; mv_count=0;
  7180.   }
  7181.  
  7182. LISPFUNN(clear_window_to_eol,1)
  7183.   { check_window_stream(popSTACK());
  7184.     clear_to_EOL();
  7185.     value1 = NIL; mv_count=0;
  7186.   }
  7187.  
  7188. LISPFUNN(delete_window_line,1)
  7189.   { check_window_stream(popSTACK());
  7190.     delete_line(1);
  7191.     value1 = NIL; mv_count=0;
  7192.   }
  7193.  
  7194. LISPFUNN(insert_window_line,1)
  7195.   { check_window_stream(popSTACK());
  7196.     insert_line(1);
  7197.     value1 = NIL; mv_count=0;
  7198.   }
  7199.  
  7200. LISPFUNN(highlight_on,1)
  7201.   { check_window_stream(popSTACK());
  7202.     change_attr(curr->curr_attr |= A_US);
  7203.     value1 = NIL; mv_count=0;
  7204.   }
  7205.  
  7206. LISPFUNN(highlight_off,1)
  7207.   { check_window_stream(popSTACK());
  7208.     change_attr(curr->curr_attr &= ~A_US);
  7209.     value1 = NIL; mv_count=0;
  7210.   }
  7211.  
  7212. LISPFUNN(window_cursor_on,1)
  7213.   { check_window_stream(popSTACK());
  7214.     # Cursor ist permanent an!
  7215.     value1 = NIL; mv_count=0;
  7216.   }
  7217.  
  7218. LISPFUNN(window_cursor_off,1)
  7219.   { check_window_stream(popSTACK());
  7220.     # geht nicht, da Cursor permanent an!
  7221.     value1 = NIL; mv_count=0;
  7222.   }
  7223.  
  7224. #endif # UNIX || EMUNIX_PORTABEL || RISCOS
  7225.  
  7226. #if defined(UNIX) && 0
  7227.  
  7228. # Normales CURSES-Paket, wir benutzen nur stdscr.
  7229.  
  7230. #undef BS
  7231. #undef CR
  7232. #undef NL
  7233. #include <curses.h>
  7234. #undef OK
  7235. #define CR  13
  7236. #define NL  10
  7237.  
  7238. # UP: Ein Zeichen auf einen Window-Stream ausgeben.
  7239. # wr_ch_window(&stream,ch);
  7240. # > stream: Window-Stream
  7241. # > ch: auszugebendes Zeichen
  7242.   local void wr_ch_window (object* stream_, object ch);
  7243.   local void wr_ch_window(stream_,ch)
  7244.     var reg2 object* stream_;
  7245.     var reg1 object ch;
  7246.     { if (!string_char_p(ch)) { fehler_wr_string_char(*stream_,ch); } # ch sollte String-Char sein
  7247.      {var reg1 uintB c = char_code(ch); # Code des Zeichens
  7248.       begin_system_call();
  7249.       if (graphic_char_p(c)) # nur druckbare Zeichen auf den Bildschirm lassen
  7250.         { addch(c); }
  7251.       elif (c == NL) # NL in CR/LF umwandeln
  7252.         { addch(CR); addch(LF); }
  7253.       else # etwas ausgeben, damit die Cursorposition stimmt
  7254.         { addch('?'); }
  7255.       end_system_call();
  7256.     }}
  7257.  
  7258. LISPFUNN(make_window,0)
  7259.   { var reg2 object stream =
  7260.       allocate_stream(strmflags_wr_ch_B,strmtype_window,strm_len+1);
  7261.       # Flags: nur WRITE-CHAR erlaubt
  7262.     # und fⁿllen:
  7263.     var reg1 Stream s = TheStream(stream);
  7264.       s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  7265.       s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  7266.       s->strm_rd_ch = P(rd_ch_dummy); # READ-CHAR unm÷glich
  7267.       s->strm_rd_ch_last = NIL; # Lastchar := NIL
  7268.       s->strm_wr_ch = P(wr_ch_window); # WRITE-CHAR-Pseudofunktion
  7269.       s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  7270.       #ifdef STRM_WR_SS
  7271.       s->strm_wr_ss = P(wr_ss_dummy_nogc);
  7272.       #endif
  7273.     begin_system_call();
  7274.     initscr(); # Curses initialisieren # Was ist, wenn das abstⁿrzt?? newterm() benutzen??
  7275.     cbreak(); noecho(); # Input nicht zeilengebuffert, ohne Echo
  7276.     #if defined(SUN3) || defined(SUN4)
  7277.     keypad(stdscr,TRUE); # Funktionstasten-Erkennung einschalten
  7278.     #endif
  7279.     end_system_call();
  7280.     value1 = stream; mv_count=1;
  7281.   }
  7282.  
  7283. # Schlie▀t einen Window-Stream.
  7284.   local void close_window (object stream);
  7285.   local void close_window(stream)
  7286.     var reg1 object stream;
  7287.     { begin_system_call();
  7288.       nocbreak(); echo(); # Input wieder zeilengebuffert, mit Echo
  7289.       #if defined(SUN3) || defined(SUN4)
  7290.       keypad(stdscr,FALSE); # Funktionstasten-Erkennung wieder ausschalten
  7291.       #endif
  7292.       endwin(); # Curses abschalten
  7293.       end_system_call();
  7294.     }
  7295.  
  7296. LISPFUNN(window_size,1)
  7297.   { check_window_stream(popSTACK());
  7298.     value1 = fixnum(LINES); # Curses-Variablen LINES, COLS abfragen
  7299.     value2 = fixnum(COLS);
  7300.     mv_count=2;
  7301.   }
  7302.  
  7303. LISPFUNN(window_cursor_position,1)
  7304.   { check_window_stream(popSTACK());
  7305.    {var reg1 int y;
  7306.     var reg2 int x;
  7307.     begin_system_call();
  7308.     getyx(stdscr,y,x); # (y,x) := cursor position
  7309.     end_system_call();
  7310.     value1 = fixnum(y);
  7311.     value2 = fixnum(x);
  7312.     mv_count=2;
  7313.   }}
  7314.  
  7315. LISPFUNN(set_window_cursor_position,3)
  7316.   { check_window_stream(STACK_2);
  7317.    {var reg1 uintL line = posfixnum_to_L(STACK_1);
  7318.     var reg2 uintL column = posfixnum_to_L(STACK_0);
  7319.     if ((line < LINES) && (column < COLS))
  7320.       { begin_system_call();
  7321.         move(line,column); refresh(); # Cursor positionieren
  7322.         end_system_call();
  7323.       }
  7324.     value1 = STACK_1; value2 = STACK_0; mv_count=2; skipSTACK(3);
  7325.   }}
  7326.  
  7327. LISPFUNN(clear_window,1)
  7328.   { check_window_stream(popSTACK());
  7329.     begin_system_call();
  7330.     clear(); refresh();
  7331.     end_system_call();
  7332.     value1 = NIL; mv_count=0;
  7333.   }
  7334.  
  7335. LISPFUNN(clear_window_to_eot,1)
  7336.   { check_window_stream(popSTACK());
  7337.     begin_system_call();
  7338.     clrtobot(); refresh();
  7339.     end_system_call();
  7340.     value1 = NIL; mv_count=0;
  7341.   }
  7342.  
  7343. LISPFUNN(clear_window_to_eol,1)
  7344.   { check_window_stream(popSTACK());
  7345.     begin_system_call();
  7346.     clrtoeol(); refresh();
  7347.     end_system_call();
  7348.     value1 = NIL; mv_count=0;
  7349.   }
  7350.  
  7351. LISPFUNN(delete_window_line,1)
  7352.   { check_window_stream(popSTACK());
  7353.     begin_system_call();
  7354.     deleteln(); refresh();
  7355.     end_system_call();
  7356.     value1 = NIL; mv_count=0;
  7357.   }
  7358.  
  7359. LISPFUNN(insert_window_line,1)
  7360.   { check_window_stream(popSTACK());
  7361.     begin_system_call();
  7362.     insertln(); refresh();
  7363.     end_system_call();
  7364.     value1 = NIL; mv_count=0;
  7365.   }
  7366.  
  7367. LISPFUNN(highlight_on,1)
  7368.   { check_window_stream(popSTACK());
  7369.     #ifdef A_STANDOUT # geht nur, wenn Curses Attribute verwaltet
  7370.     begin_system_call();
  7371.     attron(A_STANDOUT); # Attribut A_STANDOUT bei addch() hineinoderieren
  7372.     end_system_call();
  7373.     #endif
  7374.     value1 = NIL; mv_count=0;
  7375.   }
  7376.  
  7377. LISPFUNN(highlight_off,1)
  7378.   { check_window_stream(popSTACK());
  7379.     #ifdef A_STANDOUT # geht nur, wenn Curses Attribute verwaltet
  7380.     begin_system_call();
  7381.     attroff(A_STANDOUT); # kein Attribut mehr bei addch() hineinoderieren
  7382.     end_system_call();
  7383.     #endif
  7384.     value1 = NIL; mv_count=0;
  7385.   }
  7386.  
  7387. LISPFUNN(window_cursor_on,1)
  7388.   { check_window_stream(popSTACK());
  7389.     # Cursor ist permanent an!
  7390.     value1 = NIL; mv_count=0;
  7391.   }
  7392.  
  7393. LISPFUNN(window_cursor_off,1)
  7394.   { check_window_stream(popSTACK());
  7395.     # geht nicht, da Cursor permanent an!
  7396.     value1 = NIL; mv_count=0;
  7397.   }
  7398.  
  7399. #endif # UNIX
  7400.  
  7401. #ifdef AMIGAOS
  7402.  
  7403. # Terminal-Emulation: ANSI-Steuerzeichen, siehe console.doc
  7404.  
  7405. # UP: Ausgabe mehrerer Zeichen auf den Bildschirm
  7406.   local void wr_window (uintB* outbuffer, uintL count);
  7407.   local void wr_window(outbuffer,count)
  7408.     var reg2 uintB* outbuffer;
  7409.     var reg3 uintL count;
  7410.     { set_break_sem_1();
  7411.       begin_system_call();
  7412.      {var reg1 long ergebnis = Write(Output_handle,outbuffer,count);
  7413.       end_system_call();
  7414.       if (ergebnis<0) { OS_error(); } # Error melden
  7415.       if (ergebnis<count) # nicht erfolgreich?
  7416.         { ?? }
  7417.       clr_break_sem_1();
  7418.     }}
  7419.  
  7420. #define WR_WINDOW(characters)  \
  7421.   { local var uintB outbuffer[] = characters; \
  7422.      wr_window(&outbuffer,sizeof(outbuffer)); \
  7423.   }
  7424.  
  7425. # UP: Ein Zeichen auf einen Window-Stream ausgeben.
  7426. # wr_ch_window(&stream,ch);
  7427. # > stream: Window-Stream
  7428. # > ch: auszugebendes Zeichen
  7429.   local void wr_ch_window (object* stream_, object ch);
  7430.   local void wr_ch_window(stream_,ch)
  7431.     var reg2 object* stream_;
  7432.     var reg3 object ch;
  7433.     { if (!string_char_p(ch)) { fehler_wr_string_char(*stream_,ch); } # ch sollte String-Char sein
  7434.      {var reg1 uintB c = char_code(ch); # Code des Zeichens
  7435.       ??
  7436.     }}
  7437.  
  7438. LISPFUNN(make_window,0)
  7439.   { finish_output_terminal(var_stream(S(terminal_io))); # evtl. wartendes NL jetzt ausgeben
  7440.    {var reg2 object stream =
  7441.       allocate_stream(strmflags_wr_ch_B,strmtype_window,strm_len+0);
  7442.       # Flags: nur WRITE-CHAR erlaubt
  7443.     # und fⁿllen:
  7444.     var reg1 Stream s = TheStream(stream);
  7445.       s->strm_rd_by = P(rd_by_dummy); # READ-BYTE unm÷glich
  7446.       s->strm_wr_by = P(wr_by_dummy); # WRITE-BYTE unm÷glich
  7447.       s->strm_rd_ch = P(rd_ch_dummy); # READ-CHAR unm÷glich
  7448.       s->strm_rd_ch_last = NIL; # Lastchar := NIL
  7449.       s->strm_wr_ch = P(wr_ch_window); # WRITE-CHAR-Pseudofunktion
  7450.       s->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  7451.       #ifdef STRM_WR_SS
  7452.       s->strm_wr_ss = P(wr_ss_dummy_nogc);
  7453.       #endif
  7454.     # size: aWSR? aWBR??
  7455.     # Wrap off ?? ASM? AWM?
  7456.     WR_WINDOW({CSI,'0',0x6D}); # Set Graphics Rendition Normal
  7457.     value1 = stream; mv_count=1;
  7458.   }}
  7459.  
  7460. # Schlie▀t einen Window-Stream.
  7461.   local void close_window (object stream);
  7462.   local void close_window(stream)
  7463.     var reg1 object stream;
  7464.     { # Wrap on ?? ASM? AWM?
  7465.       WR_WINDOW({CSI,'0',0x6D}); # Set Graphics Rendition Normal
  7466.     }
  7467.  
  7468. LISPFUNN(window_size,1)
  7469.   { check_window_stream(popSTACK());
  7470.     value1 = fixnum(window_size.y); ??
  7471.     value2 = fixnum(window_size.x); ??
  7472.     mv_count=2;
  7473.   }
  7474.  
  7475. LISPFUNN(window_cursor_position,1)
  7476.   { check_window_stream(popSTACK());
  7477.     # aWSR? CPR??
  7478.     value1 = fixnum(_y); ??
  7479.     value2 = fixnum(_x); ??
  7480.     mv_count=2;
  7481.   }
  7482.  
  7483. LISPFUNN(set_window_cursor_position,3)
  7484.   { check_window_stream(STACK_2);
  7485.    {var reg3 uintL line = posfixnum_to_L(STACK_1);
  7486.     var reg4 uintL column = posfixnum_to_L(STACK_0);
  7487.     if ((line < (uintL)window_size.y) && (column < (uintL)window_size.x))
  7488.       { var uintB outbuffer[23]; # Buffer fⁿr  CSI <line> ; <column> H
  7489.         var reg1 uintB* ptr = &outbuffer[sizeof(outbuffer)];
  7490.         var reg2 uintL count = 0;
  7491.         count++; *--ptr = 'H';
  7492.         do { count++; *--ptr = '0'+(column%10); column = floor(column,10); }
  7493.            until (column==0);
  7494.         count++; *--ptr = ';';
  7495.         do { count++; *--ptr = '0'+(line%10); line = floor(line,10); }
  7496.            until (line==0);
  7497.         count++; *--ptr = CSI;
  7498.         wr_window(ptr,count);
  7499.       }
  7500.     value1 = STACK_1; value2 = STACK_0; mv_count=2; skipSTACK(3);
  7501.   }}
  7502.  
  7503. LISPFUNN(clear_window,1)
  7504.   { check_window_stream(popSTACK());
  7505.     WR_WINDOW({CSI,'0',';','0','H',CSI,'J'});
  7506.     value1 = NIL; mv_count=0;
  7507.   }
  7508.  
  7509. LISPFUNN(clear_window_to_eot,1)
  7510.   { check_window_stream(popSTACK());
  7511.     WR_WINDOW({CSI,'J'});
  7512.     value1 = NIL; mv_count=0;
  7513.   }
  7514.  
  7515. LISPFUNN(clear_window_to_eol,1)
  7516.   { check_window_stream(popSTACK());
  7517.     WR_WINDOW({CSI,'K'});
  7518.     value1 = NIL; mv_count=0;
  7519.   }
  7520.  
  7521. LISPFUNN(delete_window_line,1)
  7522.   { check_window_stream(popSTACK());
  7523.     WR_WINDOW({CSI,'M'});
  7524.     value1 = NIL; mv_count=0;
  7525.   }
  7526.  
  7527. LISPFUNN(insert_window_line,1)
  7528.   { check_window_stream(popSTACK());
  7529.     WR_WINDOW({CSI,'L'});
  7530.     value1 = NIL; mv_count=0;
  7531.   }
  7532.  
  7533. LISPFUNN(highlight_on,1)
  7534.   { check_window_stream(popSTACK());
  7535.     WR_WINDOW({CSI,'1',0x6D}); # Set Graphics Rendition Bold
  7536.     value1 = NIL; mv_count=0;
  7537.   }
  7538.  
  7539. LISPFUNN(highlight_off,1)
  7540.   { check_window_stream(popSTACK());
  7541.     WR_WINDOW({CSI,'0',0x6D}); # Set Graphics Rendition Normal
  7542.     value1 = NIL; mv_count=0;
  7543.   }
  7544.  
  7545. LISPFUNN(window_cursor_on,1)
  7546.   { check_window_stream(popSTACK());
  7547.     # aSCR ??
  7548.     value1 = NIL; mv_count=0;
  7549.   }
  7550.  
  7551. LISPFUNN(window_cursor_off,1)
  7552.   { check_window_stream(popSTACK());
  7553.     # aSCR ??
  7554.     value1 = NIL; mv_count=0;
  7555.   }
  7556.  
  7557. #endif # AMIGAOS
  7558.  
  7559. #endif # SCREEN
  7560.  
  7561.  
  7562. # File-Stream
  7563. # ===========
  7564.  
  7565. # Um nicht fⁿr jedes Character das GEMDOS/UNIX/AMIGADOS bemⁿhen zu mⁿssen,
  7566. # wird ein eigener Buffer gefⁿhrt.
  7567. # (Dies bewirkt z.B. beim Einlesen eines 408 KByte- Files auf dem Atari
  7568. # eine Beschleunigung um einen Faktor 2.7 von 500 sec auf 180 sec.)
  7569.  
  7570. # ZusΣtzliche Komponenten:
  7571.   # define strm_file_name       strm_other[3] # Filename, ein Pathname
  7572.   # define strm_file_truename   strm_other[4] # Truename, ein Pathname
  7573.   # define strm_file_handle     strm_other[2] # Handle, ein Fixnum >=0, <2^16
  7574.   #define strm_file_buffstart   strm_other[0] # Buffer-Startposition, ein Fixnum >=0
  7575.   #define strm_file_bufflen     4096          # BufferlΣnge (Zweierpotenz <2^16)
  7576.   #define strm_file_buffer      strm_other[1] # eigener Buffer,
  7577.                                 # ein Simple-String der LΣnge strm_file_bufflen
  7578.   #define strm_file_eofindex    strm_other[5] # Index darin, bis wo die
  7579.                                 # Daten gⁿltig sind (fⁿr EOF-Erkennung)
  7580.   #define strm_file_index       strm_other[6] # Fixnum mit Index im Buffer
  7581.                                               # (>=0, <=STRM_FILE_BUFFLEN)
  7582.                                               # und Modified-Flag in Bit 16.
  7583.   # eofindex = NIL: Bufferdaten ganz ungⁿltig, index=0.
  7584.   # eofindex Fixnum: 0 <= index <= eofindex <= strm_file_bufflen.
  7585.   # eofindex = T: Bufferdaten ganz gⁿltig, 0 <= index <= strm_file_bufflen.
  7586.   # buffstart = (Nummer des Sectors) * strm_file_bufflen.
  7587.   # Beim Betriebssystem ist das File 'handle' ans Ende des aktuellen Buffers
  7588.   #   positioniert:
  7589.   #   bei eofindex = T: buffstart + strm_file_bufflen,
  7590.   #   bei eofindex Fixnum: buffstart + eofindex,
  7591.   #   bei eofindex = NIL: buffstart.
  7592.   # Modified-Flag abfragen und verΣndern:
  7593.   #define modified_flag(stream)  \
  7594.     (as_oint(TheStream(stream)->strm_file_index) & wbit(16+oint_data_shift))
  7595.   #define set_modified_flag(stream)  \
  7596.     TheStream(stream)->strm_file_index = \
  7597.       as_object(as_oint(TheStream(stream)->strm_file_index) | wbit(16+oint_data_shift))
  7598.   #define reset_modified_flag(stream)  \
  7599.     TheStream(stream)->strm_file_index = \
  7600.       as_object(as_oint(TheStream(stream)->strm_file_index) & ~wbit(16+oint_data_shift))
  7601. # Bis hierher wird ein File aus Bytes α 8 Bits aufgebaut gedacht.
  7602. # Logisch ist es jedoch aus anderen Einheiten aufgebaut:
  7603.   #define strm_file_position    strm_other[7] # Position, ein Fixnum >=0
  7604.   # Bei File-Streams mit element-type = STRING-CHAR (sch_file)
  7605.   #   belegt jedes Character 1 Byte.
  7606.   # define strm_sch_file_lineno strm_other[8] # Zeilennummer beim Lesen, ein Fixnum >0
  7607.   # Bei File-Streams mit element-type = CHARACTER (ch_file)
  7608.   #   belegt jedes Character 2 Bytes.
  7609.   # Bei File-Streams mit element-type = INTEGER ("Byte-Files")
  7610.   #   belegt jeder Integer immer dieselbe Anzahl Bits.
  7611.   #define strm_file_bitsize     strm_other[8] # Anzahl der Bits, ein Fixnum >0 und <intDsize*uintC_max
  7612.   #define strm_file_bitbuffer   strm_other[9] # Buffer, ein Simple-Bit-Vector
  7613.                                               # mit ceiling(bitsize/8)*8 Bits
  7614.   #   Ist diese Anzahl nicht durch 8 teilbar, so ist bitindex der Index
  7615.   #   im aktuellen Byte:
  7616.   #define strm_file_bitindex    strm_other[10] # Index im Byte, ein Fixnum >=0 und <=8
  7617.   #   8*index+bitindex ist die Anzahl der gelesenen Bits des Buffers.
  7618.   #   Die Bits sind in der Reihenfolge Bit0,...,Bit7 angeordnet.
  7619.   #   Ist Bitsize<8, so wird beim Schlie▀en des Files die LΣnge des Files
  7620.   #   (gemessen in Bits) als .L am Anfang des Files abgelegt, die Daten
  7621.   #   fangen also erst beim 5. Byte an.
  7622.   #define strm_file_eofposition  strm_other[11] # Position des logischen EOF, ein Fixnum >=0
  7623. # Bei geschlossenen File-Streams sind nur die Komponenten name und truename
  7624. # relevant.
  7625.  
  7626. # File-Stream allgemein
  7627. # =====================
  7628.  
  7629. #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS)
  7630. # Annahme: Alle von OPEN(2) gelieferten File-Descriptoren (hier Handles
  7631. # genannt) passen in ein uintW.
  7632. # Begrⁿndung: Bekanntlich ist 0 <= fd < getdtablesize() .
  7633. #endif
  7634.  
  7635. # Handle positionieren:
  7636. # file_lseek(stream,offset,mode,ergebnis_zuweisung);
  7637. # > mode: Positionierungsmodus:
  7638. #         SEEK_SET  "absolut"
  7639. #         SEEK_CUR  "relativ"
  7640. #         SEEK_END  "ab Ende"
  7641. # < ergebnis: neue Position
  7642.   #ifdef ATARI
  7643.     #define file_lseek(stream,offset,mode,ergebnis_zuweisung)  \
  7644.       { var reg2 sintL ergebnis =           \
  7645.           GEMDOS_Lseek(offset,              \
  7646.                        TheHandle(TheStream(stream)->strm_file_handle), # Handle \
  7647.                        mode                 \
  7648.                       );                    \
  7649.         if (ergebnis<0) { OS_error(ergebnis); } # Fehler aufgetreten? \
  7650.         nowarn ergebnis_zuweisung ergebnis; \
  7651.       }
  7652.     #define SEEK_SET  0
  7653.     #define SEEK_CUR  1
  7654.     #define SEEK_END  2
  7655.   #endif
  7656.   #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS)
  7657.     #define file_lseek(stream,offset,mode,ergebnis_zuweisung)  \
  7658.       { var reg2 sintL ergebnis =           \
  7659.           lseek(TheHandle(TheStream(stream)->strm_file_handle), # Handle \
  7660.                 offset,                     \
  7661.                 mode                        \
  7662.                );                           \
  7663.         if (ergebnis<0) { OS_error(); } # Fehler aufgetreten? \
  7664.         nowarn ergebnis_zuweisung ergebnis; \
  7665.       }
  7666.   #endif
  7667.   #ifdef AMIGAOS
  7668.     #define file_lseek(stream,offset,mode,ergebnis_zuweisung)  \
  7669.       { var reg3 uintL _offset = (offset);                     \
  7670.         var reg2 sintL ergebnis =                              \
  7671.           Seek(TheHandle(TheStream(stream)->strm_file_handle), \
  7672.                _offset,                                        \
  7673.                mode                                            \
  7674.               );                                               \
  7675.         if (ergebnis<0) { OS_error(); } # Fehler aufgetreten?  \
  7676.         if (mode==SEEK_SET) { nowarn ergebnis_zuweisung _offset; }     \
  7677.         elif (mode==SEEK_CUR) { nowarn ergebnis_zuweisung ergebnis+_offset; } \
  7678.         else /* mode==SEEK_END */                                      \
  7679.           { ergebnis = Seek(TheHandle(TheStream(stream)->strm_file_handle),0,SEEK_CUR); \
  7680.             if (ergebnis<0) { OS_error(); } # Fehler aufgetreten?      \
  7681.             nowarn ergebnis_zuweisung ergebnis;                        \
  7682.       }   }
  7683.     #define SEEK_SET  OFFSET_BEGINNING
  7684.     #define SEEK_CUR  OFFSET_CURRENT
  7685.     #define SEEK_END  OFFSET_END
  7686.   #endif
  7687.  
  7688. # UP: Beendet das Zurⁿckschreiben des Buffers.
  7689. # b_file_finish_flush(stream,bufflen);
  7690. # > stream : (offener) Byte-basierter File-Stream.
  7691. # > bufflen : Anzahl der zu schreibenden Bytes
  7692. # < modified_flag von stream : gel÷scht
  7693. # verΣndert in stream: index
  7694.   local void b_file_finish_flush (object stream, uintL bufflen);
  7695.   local void b_file_finish_flush(stream,bufflen)
  7696.     var reg1 object stream;
  7697.     var reg2 uintL bufflen;
  7698.     { begin_system_call();
  7699.      {var reg3 sintL ergebnis = # Buffer hinausschreiben
  7700.         #ifdef ATARI
  7701.           GEMDOS_write(TheHandle(TheStream(stream)->strm_file_handle), # Handle
  7702.                        bufflen,
  7703.                        untype(&TheSstring(TheStream(stream)->strm_file_buffer)->data[0]) # Bufferadresse ohne Typinfo
  7704.                       )
  7705.         #endif
  7706.         #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS)
  7707.           full_write(TheHandle(TheStream(stream)->strm_file_handle), # Handle
  7708.                      &TheSstring(TheStream(stream)->strm_file_buffer)->data[0], # Bufferadresse
  7709.                      bufflen
  7710.                     )
  7711.         #endif
  7712.         #ifdef AMIGAOS
  7713.           Write(TheHandle(TheStream(stream)->strm_file_handle),
  7714.                 &TheSstring(TheStream(stream)->strm_file_buffer)->data[0], # Bufferadresse
  7715.                 bufflen
  7716.                )
  7717.         #endif
  7718.         ;
  7719.       end_system_call();
  7720.       if (ergebnis==bufflen)
  7721.         # alles korrekt geschrieben
  7722.         { reset_modified_flag(stream); }
  7723.         else
  7724.         # Nicht alles geschrieben
  7725.         {
  7726.           #ifdef ATARI
  7727.           if (ergebnis<0) { OS_error(ergebnis); } # Fehler aufgetreten?
  7728.           #endif
  7729.           #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS)
  7730.           if (ergebnis<0) # Fehler aufgetreten?
  7731.             #ifdef ENOSPC
  7732.             if (!(errno == ENOSPC))
  7733.             #endif
  7734.             #ifdef EDQUOT
  7735.             if (!(errno == EDQUOT))
  7736.             #endif
  7737.               { OS_error(); }
  7738.           #endif
  7739.           #ifdef AMIGAOS
  7740.           if (ergebnis<0) { OS_error(); } # Fehler aufgetreten?
  7741.           #endif
  7742.           # Nicht alles geschrieben, wohl wegen voller Diskette.
  7743.           # Um Inkonsistenzen zu vermeiden, mu▀ man das File schlie▀en.
  7744.           reset_modified_flag(stream); # Hierbei gehen Daten verloren!
  7745.           pushSTACK(stream);
  7746.           stream_close(&STACK_0); # File schlie▀en
  7747.           clr_break_sem_4(); # keine GEMDOS/UNIX-Operation mehr aktiv
  7748.           # Fehler melden.
  7749.           pushSTACK(TheStream(STACK_0)->strm_file_truename); # Wert fⁿr Slot PATHNAME von FILE-ERROR
  7750.           pushSTACK(STACK_(0+1)); # stream
  7751.           fehler(file_error,
  7752.                  DEUTSCH ? "Diskette/Platte voll. Deswegen wurde ~ geschlossen." :
  7753.                  ENGLISH ? "Closed ~ because disk is full." :
  7754.                  FRANCAIS ? "Ai fermΘ ~, parce que le disque est sans doute plein." :
  7755.                  ""
  7756.                 );
  7757.     }}  }
  7758.  
  7759. # UP: Schreibt den vollen, modifizierten Buffer zurⁿck.
  7760. # b_file_full_flush(stream);
  7761. # > stream : (offener) Byte-basierter File-Stream.
  7762. # < modified_flag von stream : gel÷scht
  7763. # verΣndert in stream: index
  7764.   local void b_file_full_flush (object stream);
  7765.   local void b_file_full_flush(stream)
  7766.     var reg1 object stream;
  7767.     { # erst zurⁿckpositionieren, dann schreiben.
  7768.       begin_system_call();
  7769.       file_lseek(stream,-(long)strm_file_bufflen,SEEK_CUR,); # Zurⁿckpositionieren
  7770.       end_system_call();
  7771.       b_file_finish_flush(stream,strm_file_bufflen);
  7772.     }
  7773.  
  7774. # UP: Schreibt den halbvollen, modifizierten Buffer zurⁿck.
  7775. # b_file_full_flush(stream);
  7776. # > stream : (offener) Byte-basierter File-Stream.
  7777. # < modified_flag von stream : gel÷scht
  7778. # verΣndert in stream: index
  7779.   local void b_file_half_flush (object stream);
  7780.   local void b_file_half_flush(stream)
  7781.     var reg1 object stream;
  7782.     { begin_system_call();
  7783.       file_lseek(stream,posfixnum_to_L(TheStream(stream)->strm_file_buffstart),SEEK_SET,); # Zurⁿckpositionieren
  7784.       end_system_call();
  7785.       # eofindex Bytes schreiben:
  7786.       b_file_finish_flush(stream,
  7787.                           posfixnum_to_L(TheStream(stream)->strm_file_eofindex)
  7788.                          );
  7789.     }
  7790.  
  7791. # UP: Schreibt den modifizierten Buffer zurⁿck.
  7792. # b_file_flush(stream);
  7793. # > stream : (offener) Byte-basierter File-Stream.
  7794. # < modified_flag von stream : gel÷scht
  7795. # verΣndert in stream: index
  7796.   local void b_file_flush (object stream);
  7797.   local void b_file_flush(stream)
  7798.     var reg1 object stream;
  7799.     { if (eq(TheStream(stream)->strm_file_eofindex,T)) # Buffer ganz gⁿltig ?
  7800.         { b_file_full_flush(stream); }
  7801.         else
  7802.         { b_file_half_flush(stream); }
  7803.     }
  7804.  
  7805. # UP: Positioniert einen Byte-basierten File-Stream so, da▀ das nΣchste Byte
  7806. # gelesen oder ⁿberschrieben werden kann.
  7807. # b_file_nextbyte(stream)
  7808. # > stream : (offener) Byte-basierter File-Stream.
  7809. # < ergebnis : NULL falls EOF (und dann ist index=eofindex),
  7810. #              sonst: Pointer auf nΣchstes Byte
  7811. # verΣndert in stream: index, eofindex, buffstart
  7812.   local uintB* b_file_nextbyte (object stream);
  7813.   local uintB* b_file_nextbyte(stream)
  7814.     var reg1 object stream;
  7815.     { var reg2 object eofindex = TheStream(stream)->strm_file_eofindex;
  7816.       var reg3 object index = TheStream(stream)->strm_file_index;
  7817.       if (!eq(eofindex,T))
  7818.         # Bufferdaten nur halb gⁿltig
  7819.         { if (eq(eofindex,NIL))
  7820.             # Bufferdaten ganz ungⁿltig
  7821.             { goto reread; }
  7822.             else
  7823.             # EOF tritt in diesem Sector auf
  7824.             { goto eofsector; }
  7825.         }
  7826.       # Bufferdaten ganz gⁿltig
  7827.       if (!((uintW)posfixnum_to_L(index) == strm_file_bufflen)) # index = bufflen ?
  7828.         # nein, also 0 <= index < strm_file_bufflen -> OK
  7829.         { return &TheSstring(TheStream(stream)->strm_file_buffer)->data[(uintW)posfixnum_to_L(index)]; }
  7830.       # Buffer mu▀ neu gefⁿllt werden.
  7831.       if (modified_flag(stream))
  7832.         # Zuvor mu▀ der Buffer hinausgeschrieben werden:
  7833.         { b_file_full_flush(stream); }
  7834.       TheStream(stream)->strm_file_buffstart =
  7835.         fixnum_inc(TheStream(stream)->strm_file_buffstart,strm_file_bufflen);
  7836.       reread: # Ab hier den Buffer neu lesen:
  7837.       { begin_system_call();
  7838.        {var reg4 sintL ergebnis = # Buffer fⁿllen
  7839.           #ifdef ATARI
  7840.             GEMDOS_read(TheHandle(TheStream(stream)->strm_file_handle), # Handle
  7841.                         strm_file_bufflen,
  7842.                         untype(&TheSstring(TheStream(stream)->strm_file_buffer)->data[0]) # Bufferadresse ohne Typinfo
  7843.                        )
  7844.           #endif
  7845.           #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS)
  7846.             full_read(TheHandle(TheStream(stream)->strm_file_handle), # Handle
  7847.                       &TheSstring(TheStream(stream)->strm_file_buffer)->data[0], # Bufferadresse
  7848.                       strm_file_bufflen
  7849.                      )
  7850.           #endif
  7851.           #ifdef AMIGAOS
  7852.             Read(TheHandle(TheStream(stream)->strm_file_handle),
  7853.                  &TheSstring(TheStream(stream)->strm_file_buffer)->data[0], # Bufferadresse
  7854.                  strm_file_bufflen
  7855.                 )
  7856.           #endif
  7857.           ;
  7858.         end_system_call();
  7859.         if (ergebnis==strm_file_bufflen)
  7860.           # der ganze Buffer wurde gefⁿllt
  7861.           { TheStream(stream)->strm_file_index = Fixnum_0; # Index := 0, Buffer unmodifiziert
  7862.             TheStream(stream)->strm_file_eofindex = T; # eofindex := T
  7863.             return &TheSstring(TheStream(stream)->strm_file_buffer)->data[0];
  7864.           }
  7865.         #ifdef ATARI
  7866.         if (ergebnis<0) { OS_error(ergebnis); } # Fehler aufgetreten?
  7867.         #endif
  7868.         #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS)
  7869.         if (ergebnis<0) { OS_error(); } # Fehler aufgetreten?
  7870.         #endif
  7871.         #ifdef AMIGAOS
  7872.         if (ergebnis<0) { OS_error(); } # Fehler aufgetreten?
  7873.         #endif
  7874.         # Es wurden ergebnis (< strm_file_bufflen) Bytes gelesen.
  7875.         # Nicht der ganze Buffer wurde gefⁿllt -> EOF ist erreicht.
  7876.         TheStream(stream)->strm_file_index = index = Fixnum_0; # Index := 0, Buffer unmodifiziert
  7877.         TheStream(stream)->strm_file_eofindex = eofindex = fixnum(ergebnis); # eofindex := ergebnis
  7878.       }}
  7879.       eofsector: # eofindex ist ein Fixnum, d.h. EOF tritt in diesem Sector auf.
  7880.       if ((uintW)posfixnum_to_L(index) == (uintW)posfixnum_to_L(eofindex))
  7881.         { return (uintB*)NULL; } # EOF erreicht
  7882.         else
  7883.         { return &TheSstring(TheStream(stream)->strm_file_buffer)->data[(uintW)posfixnum_to_L(index)]; }
  7884.     }
  7885.  
  7886. # UP: Bereitet das Schreiben eines Bytes am EOF vor.
  7887. # b_file_eofbyte(stream);
  7888. # > stream : (offener) Byte-basierter File-Stream,
  7889. #            bei dem gerade b_file_nextbyte(stream)==NULL ist.
  7890. # < ergebnis : Pointer auf nΣchstes (freies) Byte
  7891. # verΣndert in stream: index, eofindex, buffstart
  7892.   local uintB* b_file_eofbyte (object stream);
  7893.   local uintB* b_file_eofbyte(stream)
  7894.     var reg1 object stream;
  7895.     { # EOF. Es ist eofindex=index.
  7896.       if (eq(TheStream(stream)->strm_file_eofindex,
  7897.              fixnum(strm_file_bufflen)
  7898.          )  )
  7899.         # eofindex = strm_file_bufflen
  7900.         { # Buffer mu▀ neu gefⁿllt werden. Da nach ihm sowieso EOF kommt,
  7901.           # genⁿgt es, ihn hinauszuschreiben:
  7902.           if (modified_flag(stream)) { b_file_half_flush(stream); }
  7903.           TheStream(stream)->strm_file_buffstart =
  7904.             fixnum_inc(TheStream(stream)->strm_file_buffstart,strm_file_bufflen);
  7905.           TheStream(stream)->strm_file_eofindex = Fixnum_0; # eofindex := 0
  7906.           TheStream(stream)->strm_file_index = Fixnum_0; # index := 0, unmodifiziert
  7907.         }
  7908.       # eofindex erh÷hen:
  7909.       TheStream(stream)->strm_file_eofindex = fixnum_inc(TheStream(stream)->strm_file_eofindex,1);
  7910.       return &TheSstring(TheStream(stream)->strm_file_buffer)->data[(uintW)posfixnum_to_L(TheStream(stream)->strm_file_index)];
  7911.     }
  7912.  
  7913. # UP: Schreibt ein Byte auf einen Byte-basierten File-Stream.
  7914. # b_file_writebyte(stream,b);
  7915. # > stream : (offener) Byteblock-basierter File-Stream.
  7916. # > b : zu schreibendes Byte
  7917. # verΣndert in stream: index, eofindex, buffstart
  7918.   local void b_file_writebyte (object stream, uintB b);
  7919.   local void b_file_writebyte(stream,b)
  7920.     var reg1 object stream;
  7921.     var reg3 uintB b;
  7922.     { var reg2 uintB* ptr = b_file_nextbyte(stream);
  7923.       if (!(ptr == (uintB*)NULL))
  7924.         { if (*ptr == b) goto no_modification; } # keine wirkliche Modifikation?
  7925.         else
  7926.         { ptr = b_file_eofbyte(stream); } # EOF -> 1 Byte Platz machen
  7927.       # nΣchstes Byte in den Buffer schreiben:
  7928.       *ptr = b; set_modified_flag(stream);
  7929.       no_modification:
  7930.       # index incrementieren:
  7931.       TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  7932.     }
  7933.  
  7934. # File-Stream, Byte-basiert (b_file)
  7935. # ===========  ============
  7936.  
  7937. # Fehler wegen Positionierung hinter EOF.
  7938. # fehler_position_beyond_EOF(stream);
  7939.   nonreturning_function(local, fehler_position_beyond_EOF, (object stream));
  7940.   local void fehler_position_beyond_EOF(stream)
  7941.     var reg1 object stream;
  7942.     { pushSTACK(TheStream(stream)->strm_file_truename); # Wert fⁿr Slot PATHNAME von FILE-ERROR
  7943.       pushSTACK(stream);
  7944.       fehler(file_error,
  7945.              DEUTSCH ? "Positionierung von ~ hinter EOF unm÷glich." :
  7946.              ENGLISH ? "cannot position ~ beyond EOF" :
  7947.              FRANCAIS ? "Ne peux pas positionner ~ au-delα de la fin du fichier." :
  7948.              ""
  7949.             );
  7950.     }
  7951.  
  7952. # UP: Positioniert einen (offenen) Byte-basierten File-Stream an eine
  7953. # gegebene Position.
  7954. # position_b_file(stream,position);
  7955. # > stream : (offener) Byte-basierter File-Stream.
  7956. # > position : neue Position
  7957. # verΣndert in stream: index, eofindex, buffstart
  7958.   local void position_b_file (object stream, uintL position);
  7959.   local void position_b_file(stream,position)
  7960.     var reg1 object stream;
  7961.     var reg2 uintL position;
  7962.     { # Liegt die neue Position im selben Sector?
  7963.       { var reg3 object eofindex = TheStream(stream)->strm_file_eofindex;
  7964.         var reg4 uintL newindex = position - posfixnum_to_L(TheStream(stream)->strm_file_buffstart);
  7965.         if (newindex
  7966.             <= (eq(eofindex,T) ? strm_file_bufflen :
  7967.                 (!eq(eofindex,NIL)) ? posfixnum_to_L(eofindex) :
  7968.                 0
  7969.            )   )
  7970.           { # ja -> brauche nur index zu verΣndern:
  7971.             # (Dabei aber das modified_flag erhalten!)
  7972.             TheStream(stream)->strm_file_index =
  7973.               (modified_flag(stream)
  7974.                ? fixnum(bit(16)+newindex)
  7975.                : fixnum(newindex)
  7976.               );
  7977.             return;
  7978.       }   }
  7979.       # evtl. Buffer hinausschreiben:
  7980.       if (modified_flag(stream)) { b_file_flush(stream); }
  7981.       # Nun ist modified_flag gel÷scht.
  7982.      {var reg5 uintL oldposition = posfixnum_to_L(TheStream(stream)->strm_file_buffstart)
  7983.                                    + posfixnum_to_L(TheStream(stream)->strm_file_index);
  7984.       # Positionieren:
  7985.       { var reg4 uintL newposition;
  7986.         begin_system_call();
  7987.         file_lseek(stream,floor(position,strm_file_bufflen)*strm_file_bufflen,SEEK_SET,newposition=);
  7988.         end_system_call();
  7989.         TheStream(stream)->strm_file_buffstart = fixnum(newposition);
  7990.       }
  7991.       # Sector lesen:
  7992.       TheStream(stream)->strm_file_eofindex = NIL; # eofindex := NIL
  7993.       TheStream(stream)->strm_file_index = Fixnum_0; # index := 0, unmodifiziert
  7994.       { var reg3 uintL newindex = position % strm_file_bufflen; # gewⁿnschter Index im Sector
  7995.         if (!(newindex==0)) # Position zwischen Sectoren -> brauche nichts zu lesen
  7996.           { b_file_nextbyte(stream);
  7997.             # Jetzt ist index=0.
  7998.             # index auf (position mod bufflen) setzen, vorher ⁿberprⁿfen:
  7999.            {var reg4 object eofindex = TheStream(stream)->strm_file_eofindex;
  8000.             # Es mu▀ entweder eofindex=T oder 0<=newindex<=eofindex sein:
  8001.             if (!(eq(eofindex,T) || (newindex <= posfixnum_to_L(eofindex))))
  8002.               # Fehler. Aber erst an die alte Position zurⁿckpositionieren:
  8003.               { check_SP();
  8004.                 position_b_file(stream,oldposition); # zurⁿckpositionieren
  8005.                 fehler_position_beyond_EOF(stream);
  8006.               }
  8007.             TheStream(stream)->strm_file_index = fixnum(newindex);
  8008.       }   }}
  8009.     }}
  8010.  
  8011. # File-Stream fⁿr String-Chars
  8012. # ============================
  8013.  
  8014. # Funktionsweise:
  8015. # Beim Schreiben: Characters werden unverΣndert durchgereicht, nur NL wird auf
  8016. # ATARI/MSDOS in CR/LF umgewandelt. Beim Lesen: CR/LF wird in NL umgewandelt.
  8017.  
  8018. # READ-CHAR - Pseudofunktion fⁿr File-Streams fⁿr String-Chars
  8019.   local object rd_ch_sch_file (object* stream_);
  8020.   local object rd_ch_sch_file(stream_)
  8021.     var reg3 object* stream_;
  8022.     { var reg1 object stream = *stream_;
  8023.       var reg2 uintB* charptr = b_file_nextbyte(stream);
  8024.       if (charptr == (uintB*)NULL) { return eof_value; } # EOF ?
  8025.       # nΣchstes Zeichen holen:
  8026.      {var reg3 object ch = code_char(*charptr); # Character aus dem Buffer holen
  8027.       # index und position incrementieren:
  8028.       TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  8029.       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  8030.       # ch = nΣchstes Zeichen
  8031.       if (!eq(ch,code_char(CR))) # Ist es CR ?
  8032.         { # nein -> OK
  8033.           if (eq(ch,code_char(NL))) # Ist es NL, dann lineno incrementieren
  8034.             { TheStream(stream)->strm_sch_file_lineno = fixnum_inc(TheStream(stream)->strm_sch_file_lineno,1); }
  8035.           return ch;
  8036.         }
  8037.       # ja -> nΣchstes Zeichen auf LF untersuchen
  8038.       charptr = b_file_nextbyte(stream);
  8039.       if (charptr == (uintB*)NULL) { return ch; } # EOF -> bleibt CR
  8040.       if (!(*charptr == LF)) { return ch; } # kein LF -> bleibt CR
  8041.       # LF ⁿbergehen, index und position incrementieren:
  8042.       TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  8043.       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  8044.       # lineno incrementieren:
  8045.       TheStream(stream)->strm_sch_file_lineno = fixnum_inc(TheStream(stream)->strm_sch_file_lineno,1);
  8046.       # NL als Ergebnis:
  8047.       return code_char(NL);
  8048.     }}
  8049.  
  8050. # Stellt fest, ob ein File-Stream ein Zeichen verfⁿgbar hat.
  8051. # listen_sch_file(stream)
  8052. # > stream: File-Stream fⁿr String-Chars
  8053. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  8054. #             -1 falls bei EOF angelangt,
  8055. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  8056.   local signean listen_sch_file (object stream);
  8057.   local signean listen_sch_file(stream)
  8058.     var reg1 object stream;
  8059.     { if (b_file_nextbyte(stream) == (uintB*)NULL)
  8060.         { return signean_minus; } # EOF
  8061.         else
  8062.         { return signean_null; }
  8063.     }
  8064.  
  8065. # UP: Schreibt ein Byte auf einen Byte-basierten File-Stream.
  8066. # write_b_file(stream,b);
  8067. # > stream : (offener) Byte-basierter File-Stream.
  8068. # > b : zu schreibendes Byte
  8069. # verΣndert in stream: index, eofindex, buffstart, position
  8070.   local void write_b_file (object stream, uintB b);
  8071.   local void write_b_file(stream,b)
  8072.     var reg1 object stream;
  8073.     var reg2 uintB b;
  8074.     { b_file_writebyte(stream,b);
  8075.       # position incrementieren:
  8076.       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  8077.     }
  8078.  
  8079. # WRITE-CHAR - Pseudofunktion fⁿr File-Streams fⁿr String-Chars
  8080.   local void wr_ch_sch_file (object* stream_, object obj);
  8081.   local void wr_ch_sch_file(stream_,obj)
  8082.     var reg4 object* stream_;
  8083.     var reg2 object obj;
  8084.     { var reg1 object stream = *stream_;
  8085.       # obj mu▀ ein String-Char sein:
  8086.       if (!string_char_p(obj)) { fehler_wr_string_char(stream,obj); }
  8087.      {var reg3 uintB ch = char_code(obj);
  8088.       #if defined(ATARI) || defined(MSDOS)
  8089.       if (ch==NL)
  8090.         # Newline als CR/LF ausgeben
  8091.         { write_b_file(stream,CR); write_b_file(stream,LF); }
  8092.         else
  8093.         # alle anderen Zeichen unverΣndert ausgeben
  8094.         { write_b_file(stream,ch); }
  8095.       #else
  8096.       write_b_file(stream,ch); # unverΣndert ausgeben
  8097.       #endif
  8098.     }}
  8099.  
  8100. # WRITE-CHAR-SEQUENCE fⁿr File-Streams fⁿr String-Chars:
  8101.   local uintB* write_schar_array_sch_file (object stream, uintB* strptr, uintL len);
  8102.   #if defined(ATARI) || defined(MSDOS)
  8103.   # Wegen NL->CR/LF-Umwandlung keine Optimierung m÷glich.
  8104.   local inline uintB* write_schar_array_sch_file(stream,strptr,len)
  8105.     var reg3 object stream;
  8106.     var reg4 uintB* strptr;
  8107.     var reg5 uintL len;
  8108.     { var reg1 uintL remaining = len;
  8109.       do { var reg2 uintB ch = *strptr++;
  8110.            if (ch==NL)
  8111.              # Newline als CR/LF ausgeben
  8112.              { write_b_file(stream,CR); write_b_file(stream,LF); }
  8113.              else
  8114.              # alle anderen Zeichen unverΣndert ausgeben
  8115.              { write_b_file(stream,ch); }
  8116.            remaining--;
  8117.          }
  8118.          until (remaining == 0);
  8119.       wr_ss_lpos(stream,strptr,len); # Line-Position aktualisieren
  8120.       return strptr;
  8121.     }
  8122.   #else
  8123.   local uintB* write_schar_array_sch_file(stream,strptr,len)
  8124.     var reg5 object stream;
  8125.     var reg2 uintB* strptr;
  8126.     var reg9 uintL len;
  8127.     { var reg6 uintL remaining = len;
  8128.       var reg1 uintB* ptr;
  8129.       do # Noch remaining>0 Bytes abzulegen.
  8130.         { ptr = b_file_nextbyte(stream);
  8131.           if (ptr == (uintB*)NULL) goto eof_reached;
  8132.          {var reg8 object eofindex = TheStream(stream)->strm_file_eofindex;
  8133.           var reg7 uintL next = # so viel wie noch in den Buffer oder bis EOF pa▀t
  8134.             (eq(eofindex,T) ? strm_file_bufflen : posfixnum_to_L(eofindex))
  8135.             - (uintW)posfixnum_to_L(TheStream(stream)->strm_file_index); # > 0 !
  8136.           if (next > remaining) { next = remaining; }
  8137.           # next Bytes in den Buffer kopieren:
  8138.           {var reg4 uintL count;
  8139.            dotimespL(count,next,
  8140.              { var reg3 uintB b = *strptr++; # nΣchstes Byte
  8141.                if (!(*ptr == b)) { *ptr = b; set_modified_flag(stream); } # in den Buffer
  8142.                ptr++;
  8143.              });
  8144.           }
  8145.           remaining = remaining - next;
  8146.           # index incrementieren:
  8147.           TheStream(stream)->strm_file_index =
  8148.             fixnum_inc(TheStream(stream)->strm_file_index,next);
  8149.         }}
  8150.         until (remaining == 0);
  8151.       if (FALSE)
  8152.         eof_reached: # Schreiben am EOF, eofindex = index
  8153.         do # Noch remaining>0 Bytes abzulegen.
  8154.           { var reg7 uintL next = # so viel wie noch Platz im Buffer ist
  8155.               strm_file_bufflen
  8156.               - (uintW)posfixnum_to_L(TheStream(stream)->strm_file_index);
  8157.             if (next==0)
  8158.               { # Buffer mu▀ neu gefⁿllt werden. Da nach ihm sowieso EOF kommt,
  8159.                 # genⁿgt es, ihn hinauszuschreiben:
  8160.                 if (modified_flag(stream)) { b_file_half_flush(stream); }
  8161.                 TheStream(stream)->strm_file_buffstart =
  8162.                   fixnum_inc(TheStream(stream)->strm_file_buffstart,strm_file_bufflen);
  8163.                 TheStream(stream)->strm_file_eofindex = Fixnum_0; # eofindex := 0
  8164.                 TheStream(stream)->strm_file_index = Fixnum_0; # index := 0, unmodifiziert
  8165.                 # Dann nochmals versuchen:
  8166.                 next = strm_file_bufflen;
  8167.               }
  8168.             if (next > remaining) { next = remaining; }
  8169.             # next Bytes in den Buffer kopieren:
  8170.             {var reg3 uintL count;
  8171.              ptr = &TheSstring(TheStream(stream)->strm_file_buffer)->data[(uintW)posfixnum_to_L(TheStream(stream)->strm_file_index)];
  8172.              dotimespL(count,next, { *ptr++ = *strptr++; } );
  8173.              set_modified_flag(stream);
  8174.             }
  8175.             remaining = remaining - next;
  8176.             # index und eofindex incrementieren:
  8177.             TheStream(stream)->strm_file_index =
  8178.               fixnum_inc(TheStream(stream)->strm_file_index,next);
  8179.             TheStream(stream)->strm_file_eofindex =
  8180.               fixnum_inc(TheStream(stream)->strm_file_eofindex,next);
  8181.           }
  8182.           until (remaining == 0);
  8183.       # position incrementieren:
  8184.       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,len);
  8185.       wr_ss_lpos(stream,strptr,len); # Line-Position aktualisieren
  8186.       return strptr;
  8187.     }
  8188.   #endif
  8189.  
  8190. #ifdef STRM_WR_SS
  8191. # WRITE-SIMPLE-STRING - Pseudofunktion fⁿr File-Streams fⁿr String-Chars
  8192.   local void wr_ss_sch_file (object* stream_, object string, uintL start, uintL len);
  8193.   local void wr_ss_sch_file(stream_,string,start,len)
  8194.     var reg1 object* stream_;
  8195.     var reg2 object string;
  8196.     var reg4 uintL start;
  8197.     var reg3 uintL len;
  8198.     { if (len==0) return;
  8199.       write_schar_array_sch_file(*stream_,&TheSstring(string)->data[start],len);
  8200.     }
  8201. #endif
  8202.  
  8203. # File-Stream fⁿr Characters
  8204. # ==========================
  8205.  
  8206. # Funktionsweise:
  8207. # Characters werden incl. Fonts und Bits durchgereicht.
  8208.   #if (!((char_int_len % 8) == 0)) # char_int_len mu▀ durch 8 teilbar sein
  8209.     #error "Charactergr÷▀e neu einstellen!"
  8210.   #endif
  8211.   #define char_size  (char_int_len / 8)  # Gr÷▀e eines Characters in Bytes
  8212.  
  8213. # READ-CHAR - Pseudofunktion fⁿr File-Streams fⁿr Characters
  8214.   local object rd_ch_ch_file (object* stream_);
  8215.   local object rd_ch_ch_file(stream_)
  8216.     var reg4 object* stream_;
  8217.     { var reg1 object stream = *stream_;
  8218.       var reg3 cint c;
  8219.       var reg2 uintB* ptr = b_file_nextbyte(stream);
  8220.       if (ptr == (uintB*)NULL) goto eof; # EOF ?
  8221.       c = *ptr;
  8222.       # index incrementieren:
  8223.       TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  8224.       doconsttimes(char_size-1,
  8225.         ptr = b_file_nextbyte(stream);
  8226.         if (ptr == (uintB*)NULL) goto eof1; # EOF ?
  8227.         c = (c<<8) | *ptr;
  8228.         # index incrementieren:
  8229.         TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  8230.         );
  8231.       # position incrementieren:
  8232.       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  8233.       return int_char(c);
  8234.       eof1:
  8235.         # Wieder zurⁿckpositionieren:
  8236.         position_b_file(stream,posfixnum_to_L(TheStream(stream)->strm_file_position) * char_size);
  8237.       eof: # EOF erreicht gewesen
  8238.         return eof_value;
  8239.     }
  8240.  
  8241. # Stellt fest, ob ein File-Stream ein Zeichen verfⁿgbar hat.
  8242. # listen_ch_file(stream)
  8243. # > stream: File-Stream fⁿr Characters
  8244. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  8245. #             -1 falls bei EOF angelangt,
  8246. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  8247. # kann GC ausl÷sen
  8248.   local signean listen_ch_file (object stream);
  8249.   local signean listen_ch_file(stream)
  8250.     var reg1 object stream;
  8251.     { var reg2 uintB* ptr = b_file_nextbyte(stream); # erstes Byte da ?
  8252.       if (ptr == (uintB*)NULL) goto eof; # EOF ?
  8253.       doconsttimes(char_size-1,
  8254.         # index incrementieren:
  8255.         TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  8256.         ptr = b_file_nextbyte(stream); # nΣchstes Byte da ?
  8257.         if (ptr == (uintB*)NULL) goto eof1; # EOF ?
  8258.         );
  8259.       # Wieder zurⁿckpositionieren:
  8260.       position_b_file(stream,posfixnum_to_L(TheStream(stream)->strm_file_position) * char_size);
  8261.       return signean_null;
  8262.       eof1:
  8263.         # Wieder zurⁿckpositionieren:
  8264.         position_b_file(stream,posfixnum_to_L(TheStream(stream)->strm_file_position) * char_size);
  8265.       eof: # EOF erreicht gewesen
  8266.         return signean_minus;
  8267.     }
  8268.  
  8269. # WRITE-CHAR - Pseudofunktion fⁿr File-Streams fⁿr Characters
  8270.   local void wr_ch_ch_file (object* stream_, object obj);
  8271.   local void wr_ch_ch_file(stream_,obj)
  8272.     var reg4 object* stream_;
  8273.     var reg2 object obj;
  8274.     { var reg1 object stream = *stream_;
  8275.       # obj mu▀ ein Character sein:
  8276.       if (!charp(obj)) { fehler_wr_char(stream,obj); }
  8277.      {var reg3 cint c = char_int(obj);
  8278.       #define WRITEBYTE(i)  b_file_writebyte(stream,(uintB)(c>>(char_size-1-i)));
  8279.       DOCONSTTIMES(char_size,WRITEBYTE) # WRITEBYTE(0..char_size-1)
  8280.       #undef WRITEBYTE
  8281.       # position incrementieren:
  8282.       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  8283.     }}
  8284.  
  8285. # File-Stream, Bit-basiert
  8286. # ========================
  8287.  
  8288. # Davon gibt es insgesamt 6 Arten:
  8289. # Drei FΣlle
  8290. #   a - bitsize durch 8 teilbar,
  8291. #   b - bitsize < 8,
  8292. #   c - bitsize nicht durch 8 teilbar und >= 8,
  8293. # jeweils unterschieden durch
  8294. #   s - Elementtyp (signed-byte bitsize),
  8295. #       dazu zΣhlt auch signed-byte = (signed-byte 8)
  8296. #   u - Elementtyp (unsigned-byte bitsize),
  8297. #       dazu zΣhlen auch unsigned-byte = (unsigned-byte 8)
  8298. #       und bit = (unsigned-byte 1)
  8299. #       und (mod n) = (unsigned-byte (integer-length n))
  8300.  
  8301. # UP: Positioniert einen (offenen) Bit-basierten File-Stream an eine
  8302. # gegebene Position.
  8303. # position_i_file(stream,position);
  8304. # > stream : (offener) Byte-basierter File-Stream.
  8305. # > position : neue (logische) Position
  8306. # verΣndert in stream: index, eofindex, buffstart, bitindex
  8307.   local void position_i_file (object stream, uintL position);
  8308.   local void position_i_file(stream,position)
  8309.     var reg1 object stream;
  8310.     var reg2 uintL position;
  8311.     { var reg5 uintB flags = TheStream(stream)->strmflags;
  8312.       var reg4 uintL bitsize = posfixnum_to_L(TheStream(stream)->strm_file_bitsize);
  8313.       var reg3 uintL position_bits = position * bitsize;
  8314.       if (flags & strmflags_ib_B)
  8315.         { position_bits += sizeof(uintL)*8; } # Header berⁿcksichtigen
  8316.       # An Bit Nummer position_bits positionieren.
  8317.       position_b_file(stream,floor(position_bits,8)); # Aufs Byte positionieren
  8318.       if (flags & strmflags_ia_B) return; # Bei Art a war's das.
  8319.       if (# Liegt die angesprochene Position im ersten Byte nach EOF ?
  8320.           ((!((position_bits%8)==0))
  8321.            && (b_file_nextbyte(stream) == (uintB*)NULL)
  8322.           )
  8323.           ||
  8324.           # Liegt die angesprochene Position im letzten Byte, aber zu weit?
  8325.           ((flags & strmflags_ib_B)
  8326.            && (position > posfixnum_to_L(TheStream(stream)->strm_file_eofposition))
  8327.          ))
  8328.         # Fehler. Aber erst an die alte Position zurⁿckpositionieren:
  8329.         { var reg6 uintL oldposition = posfixnum_to_L(TheStream(stream)->strm_file_position);
  8330.           check_SP();
  8331.           position_i_file(stream,oldposition); # zurⁿckpositionieren
  8332.           fehler_position_beyond_EOF(stream);
  8333.         }
  8334.       TheStream(stream)->strm_file_bitindex = fixnum(position_bits%8);
  8335.     }
  8336.  
  8337. # UP fⁿr READ-BYTE auf File-Streams fⁿr Integers, Art u :
  8338. # Liefert die im Bitbuffer enthaltenen bytesize Bytes als Integer >=0.
  8339. # kann GC ausl÷sen
  8340.   local object rd_by_iu_I (object stream, uintL bitsize, uintL bytesize);
  8341.   local object rd_by_iu_I(stream,bitsize,bytesize)
  8342.     var reg6 object stream;
  8343.     var reg7 uintL bitsize;
  8344.     var reg5 uintL bytesize;
  8345.     { var reg4 object bitbuffer = TheStream(stream)->strm_file_bitbuffer;
  8346.       # Zahl im bitbuffer normalisieren:
  8347.       var reg1 uintB* bitbufferptr = &TheSbvector(bitbuffer)->data[0];
  8348.       *bitbufferptr &= (bit(((bitsize-1)%8)+1)-1); # High byte maskieren
  8349.      {var reg2 uintL count = bytesize;
  8350.       while ((!(count==0)) && (*bitbufferptr==0)) { count--; bitbufferptr++; }
  8351.       # Zahl bilden:
  8352.       if # h÷chstens oint_data_len Bits ?
  8353.          ((count <= floor(oint_data_len,8))
  8354.           || ((count == floor(oint_data_len,8)+1)
  8355.               && (*bitbufferptr < bit(oint_data_len%8))
  8356.          )   )
  8357.         # ja -> Fixnum >=0 bilden:
  8358.         { var reg3 uintL wert = 0;
  8359.           until (count==0) { wert = (wert<<8) | *bitbufferptr++; count--; }
  8360.           return fixnum(wert);
  8361.         }
  8362.         else
  8363.         # nein -> Bignum >0 bilden:
  8364.         { pushSTACK(bitbuffer);
  8365.          {var reg5 uintL digitcount = floor(count,(intDsize/8));
  8366.           if (((count%(intDsize/8)) > 0) || (*bitbufferptr & bit(7)))
  8367.             { digitcount++; }
  8368.           # Da bitsize < intDsize*uintC_max, ist
  8369.           # digitcount <= ceiling((bitsize+1)/intDsize) <= uintC_max .
  8370.           { var reg4 object big = allocate_bignum(digitcount,0); # neues Bignum >0
  8371.             TheBignum(big)->data[0] = 0; # h÷chstes Digit auf 0 setzen
  8372.             # restliche Digits von rechts fⁿllen, dabei Folge von Bytes in
  8373.             # Folge von uintD ⁿbersetzen:
  8374.             bitbuffer = popSTACK();
  8375.             bitbufferptr = &TheSbvector(bitbuffer)->data[bytesize];
  8376.             #if BIG_ENDIAN_P
  8377.             {var reg1 uintB* bigptr = (uintB*)(&TheBignum(big)->data[digitcount]);
  8378.              dotimespL(count,count, { *--bigptr = *--bitbufferptr; } );
  8379.             }
  8380.             #else
  8381.             {var reg1 uintD* bigptr = &TheBignum(big)->data[digitcount];
  8382.              var reg6 uintL count2;
  8383.              #define GET_NEXT_BYTE(i)  \
  8384.                digit |= ((uintD)(*--bitbufferptr) << (8*i));
  8385.              dotimespL(count2,floor(count,intDsize/8),
  8386.                { var reg3 uintD digit = 0;
  8387.                  DOCONSTTIMES(intDsize/8,GET_NEXT_BYTE); # GET_NEXT_BYTE(0..intDsize/8-1)
  8388.                  *--bigptr = digit;
  8389.                });
  8390.              #undef GET_NEXT_BYTE
  8391.              count2 = count % (intDsize/8);
  8392.              if (count2>0)
  8393.                { var reg7 uintL shiftcount = 0;
  8394.                  var reg3 uintD digit = (uintD)(*--bitbufferptr);
  8395.                  dotimesL(count2,count2-1,
  8396.                    { shiftcount += 8;
  8397.                      digit |= ((uintD)(*--bitbufferptr) << shiftcount);
  8398.                    });
  8399.                  *--bigptr = digit;
  8400.                }
  8401.             }
  8402.             #endif
  8403.             # Wegen (intDsize/8)*(digitcount-1) <= count <= (intDsize/8)*digitcount
  8404.             # ist alles gefⁿllt.
  8405.             return big;
  8406.         }}}
  8407.     }}
  8408.  
  8409. # UP fⁿr READ-BYTE auf File-Streams fⁿr Integers, Art s :
  8410. # Liefert die im Bitbuffer enthaltenen bytesize Bytes als Integer.
  8411. # kann GC ausl÷sen
  8412.   local object rd_by_is_I (object stream, uintL bitsize, uintL bytesize);
  8413.   local object rd_by_is_I(stream,bitsize,bytesize)
  8414.     var reg6 object stream;
  8415.     var reg7 uintL bitsize;
  8416.     var reg5 uintL bytesize;
  8417.     { var reg4 object bitbuffer = TheStream(stream)->strm_file_bitbuffer;
  8418.       # Zahl im bitbuffer normalisieren:
  8419.       var reg1 uintB* bitbufferptr = &TheSbvector(bitbuffer)->data[0];
  8420.       var reg8 sintD sign;
  8421.       var reg3 uintL signbitnr = (bitsize-1)%8;
  8422.       var reg2 uintL count = bytesize;
  8423.       if (!(*bitbufferptr & bit(signbitnr)))
  8424.         { sign = 0;
  8425.           *bitbufferptr &= (bitm(signbitnr+1)-1); # High byte sign-extenden
  8426.           # normalisieren, h÷chstes Bit mu▀ 0 bleiben:
  8427.           while ((count>=2) && (*bitbufferptr==0) && !(*(bitbufferptr+1) & bit(7)))
  8428.             { count--; bitbufferptr++; }
  8429.           # Zahl bilden:
  8430.           if # h÷chstens oint_data_len+1 Bits, Zahl <2^oint_data_len ?
  8431.              ((count <= floor(oint_data_len,8))
  8432.               || ((count == floor(oint_data_len,8)+1)
  8433.                   && (*bitbufferptr < bit(oint_data_len%8))
  8434.              )   )
  8435.             # ja -> Fixnum >=0 bilden:
  8436.             { var reg3 uintL wert = 0;
  8437.               until (count==0) { wert = (wert<<8) | *bitbufferptr++; count--; }
  8438.               return posfixnum(wert);
  8439.             }
  8440.         }
  8441.         else
  8442.         { sign = -1;
  8443.           *bitbufferptr |= minus_bitm(signbitnr+1); # High byte sign-extenden
  8444.           # normalisieren, h÷chstes Bit mu▀ 1 bleiben:
  8445.           while ((count>=2) && (*bitbufferptr==(uintB)(-1)) && (*(bitbufferptr+1) & bit(7)))
  8446.             { count--; bitbufferptr++; }
  8447.           # Zahl bilden:
  8448.           if # h÷chstens oint_data_len+1 Bits, Zahl >=-2^oint_data_len ?
  8449.              ((count <= floor(oint_data_len,8))
  8450.               || ((count == floor(oint_data_len,8)+1)
  8451.                   && (*bitbufferptr >= (uintB)(-bit(oint_data_len%8)))
  8452.              )   )
  8453.             # ja -> Fixnum <0 bilden:
  8454.             { var reg3 uintL wert = -1;
  8455.               until (count==0) { wert = (wert<<8) | *bitbufferptr++; count--; }
  8456.               return negfixnum(wbitm(intLsize)+(oint)wert);
  8457.             }
  8458.         }
  8459.       # Bignum bilden:
  8460.       pushSTACK(bitbuffer);
  8461.       { var reg5 uintL digitcount = ceiling(count,(intDsize/8));
  8462.         # Da bitsize < intDsize*uintC_max, ist
  8463.         # digitcount <= ceiling(bitsize/intDsize) <= uintC_max .
  8464.         var reg4 object big = allocate_bignum(digitcount,sign); # neues Bignum
  8465.         TheBignum(big)->data[0] = sign; # h÷chstes Word auf sign setzen
  8466.         # restliche Digits von rechts fⁿllen, dabei Folge von Bytes in
  8467.         # Folge von uintD ⁿbersetzen:
  8468.         bitbuffer = popSTACK();
  8469.         bitbufferptr = &TheSbvector(bitbuffer)->data[bytesize];
  8470.         #if BIG_ENDIAN_P
  8471.         {var reg1 uintB* bigptr = (uintB*)(&TheBignum(big)->data[digitcount]);
  8472.          dotimespL(count,count, { *--bigptr = *--bitbufferptr; } );
  8473.         }
  8474.         #else
  8475.         {var reg1 uintD* bigptr = &TheBignum(big)->data[digitcount];
  8476.          var reg6 uintL count2;
  8477.          #define GET_NEXT_BYTE(i)  \
  8478.            digit |= ((uintD)(*--bitbufferptr) << (8*i));
  8479.          dotimespL(count2,floor(count,intDsize/8),
  8480.            { var reg3 uintD digit = 0;
  8481.              DOCONSTTIMES(intDsize/8,GET_NEXT_BYTE); # GET_NEXT_BYTE(0..intDsize/8-1)
  8482.              *--bigptr = digit;
  8483.            });
  8484.          #undef GET_NEXT_BYTE
  8485.          count2 = count % (intDsize/8);
  8486.          if (count2>0)
  8487.            { var reg7 uintL shiftcount = 0;
  8488.              var reg3 uintD digit = (uintD)(*--bitbufferptr);
  8489.              dotimesL(count2,count2-1,
  8490.                { shiftcount += 8;
  8491.                  digit |= ((uintD)(*--bitbufferptr) << shiftcount);
  8492.                });
  8493.              *--bigptr = digit;
  8494.            }
  8495.         }
  8496.         #endif
  8497.         # Wegen (intDsize/8)*(digitcount-1) < count <= (intDsize/8)*digitcount
  8498.         # ist alles gefⁿllt.
  8499.         return big;
  8500.       }
  8501.     }
  8502.  
  8503. # Typ rd_by_ix_I: eines dieser beiden Unterprogramme:
  8504.   typedef object rd_by_ix_I (object stream, uintL bitsize, uintL bytesize);
  8505.  
  8506. # UP fⁿr READ-BYTE auf File-Streams fⁿr Integers, Art a :
  8507. # Fⁿllt den Bitbuffer mit den nΣchsten bitsize Bits.
  8508. # > stream : File-Stream fⁿr Integers, Art a
  8509. # > finisher : Beendigungsroutine
  8510. # < ergebnis : gelesener Integer oder eof_value
  8511.   local object rd_by_iax_file (object stream, rd_by_ix_I* finisher);
  8512.   local object rd_by_iax_file(stream,finisher)
  8513.     var reg1 object stream;
  8514.     var reg7 rd_by_ix_I* finisher;
  8515.     { var reg6 uintL bitsize = posfixnum_to_L(TheStream(stream)->strm_file_bitsize);
  8516.       var reg5 uintL bytesize = bitsize/8;
  8517.       # genⁿgend viele Bytes in den Bitbuffer ⁿbertragen:
  8518.      {var reg2 uintB* bitbufferptr = &TheSbvector(TheStream(stream)->strm_file_bitbuffer)->data[bytesize];
  8519.       var reg4 uintL count;
  8520.       dotimespL(count,bytesize,
  8521.         { var reg3 uintB* ptr = b_file_nextbyte(stream);
  8522.           if (ptr == (uintB*)NULL) goto eof;
  8523.           # nΣchstes Byte holen:
  8524.           *--bitbufferptr = *ptr;
  8525.           # index incrementieren:
  8526.           TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  8527.         });
  8528.       # position incrementieren:
  8529.       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  8530.       # in Zahl umwandeln:
  8531.       return (*finisher)(stream,bitsize,bytesize);
  8532.       eof: # EOF erreicht
  8533.       position_b_file(stream,posfixnum_to_L(TheStream(stream)->strm_file_position)*bytesize);
  8534.       return eof_value;
  8535.     }}
  8536.  
  8537. # UP fⁿr READ-BYTE auf File-Streams fⁿr Integers, Art b :
  8538. # Fⁿllt den Bitbuffer mit den nΣchsten bitsize Bits.
  8539. # > stream : File-Stream fⁿr Integers, Art b
  8540. # > finisher : Beendigungsroutine
  8541. # < ergebnis : gelesener Integer oder eof_value
  8542.   local object rd_by_ibx_file (object stream, rd_by_ix_I* finisher);
  8543.   local object rd_by_ibx_file(stream,finisher)
  8544.     var reg1 object stream;
  8545.     var reg8 rd_by_ix_I* finisher;
  8546.     { # Nur bei position < eofposition gibt's was zu lesen:
  8547.       if (eq(TheStream(stream)->strm_file_position,TheStream(stream)->strm_file_eofposition))
  8548.         goto eof;
  8549.       { var reg6 uintL bitsize = posfixnum_to_L(TheStream(stream)->strm_file_bitsize); # bitsize (>0, <8)
  8550.         # genⁿgend viele Bits in den Bitbuffer ⁿbertragen:
  8551.         var reg4 uintL bitindex = posfixnum_to_L(TheStream(stream)->strm_file_bitindex);
  8552.         var reg5 uintL count = bitindex + bitsize;
  8553.         var reg1 uint8 bit_akku;
  8554.         var reg3 uintB* ptr = b_file_nextbyte(stream);
  8555.         if (ptr == (uintB*)NULL) goto eof;
  8556.         # angefangenes Byte holen:
  8557.         bit_akku = (*ptr)>>bitindex;
  8558.         # bitshift := 8-bitindex
  8559.         # Von bit_akku sind die Bits (bitshift-1)..0 gⁿltig.
  8560.         if (count > 8)
  8561.           { # index incrementieren, da gerade *ptr verarbeitet:
  8562.             TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  8563.             count -= 8; # Noch count (>0) Bits zu holen.
  8564.            {var reg3 uintB* ptr = b_file_nextbyte(stream);
  8565.             if (ptr == (uintB*)NULL) goto eof1;
  8566.             # nΣchstes Byte holen:
  8567.             # (8-bitindex < 8, da sonst count = 0+bitsize < 8 gewesen wΣre!)
  8568.             bit_akku |= (*ptr)<<(8-bitindex);
  8569.           }}# Von bit_akku sind alle 8 Bits gⁿltig.
  8570.         # 8 Bit abspeichern:
  8571.         TheSbvector(TheStream(stream)->strm_file_bitbuffer)->data[0] = bit_akku;
  8572.         TheStream(stream)->strm_file_bitindex = fixnum(count);
  8573.         # position incrementieren:
  8574.         TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  8575.         # in Zahl umwandeln:
  8576.         return (*finisher)(stream,bitsize,1);
  8577.         eof1:
  8578.           # Wieder zurⁿckpositionieren:
  8579.           position_i_file(stream,posfixnum_to_L(TheStream(stream)->strm_file_position));
  8580.       }
  8581.       eof: # EOF erreicht gewesen
  8582.         return eof_value;
  8583.     }
  8584.  
  8585. # UP fⁿr READ-BYTE auf File-Streams fⁿr Integers, Art c :
  8586. # Fⁿllt den Bitbuffer mit den nΣchsten bitsize Bits.
  8587. # > stream : File-Stream fⁿr Integers, Art c
  8588. # > finisher : Beendigungsroutine
  8589. # < ergebnis : gelesener Integer oder eof_value
  8590.   local object rd_by_icx_file (object stream, rd_by_ix_I* finisher);
  8591.   local object rd_by_icx_file(stream,finisher)
  8592.     var reg1 object stream;
  8593.     var reg8 rd_by_ix_I* finisher;
  8594.     { var reg6 uintL bitsize = posfixnum_to_L(TheStream(stream)->strm_file_bitsize);
  8595.       var reg7 uintL bytesize = ceiling(bitsize,8);
  8596.       # genⁿgend viele Bits in den Bitbuffer ⁿbertragen:
  8597.       var reg2 uintB* bitbufferptr = &TheSbvector(TheStream(stream)->strm_file_bitbuffer)->data[bytesize];
  8598.       var reg4 uintL count = bitsize;
  8599.       var reg5 uintL bitshift = posfixnum_to_L(TheStream(stream)->strm_file_bitindex);
  8600.       var reg3 uintB* ptr = b_file_nextbyte(stream);
  8601.       if (ptr == (uintB*)NULL) goto eof;
  8602.       if (bitshift==0)
  8603.         { loop
  8604.             { *--bitbufferptr = *ptr; # 8 Bits holen und abspeichern
  8605.               # index incrementieren, da gerade *ptr verarbeitet:
  8606.               TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  8607.               count -= 8;
  8608.               # Noch count (>0) Bits zu holen.
  8609.               ptr = b_file_nextbyte(stream);
  8610.               if (ptr == (uintB*)NULL) goto eof;
  8611.               if (count<=8) break; # Sind damit count Bits fertig?
  8612.             }
  8613.           # Noch count = bitsize mod 8 (>0,<8) Bits zu holen.
  8614.           *--bitbufferptr = *ptr; # count Bits holen und abspeichern
  8615.         }
  8616.         else # 0<bitindex<8
  8617.         { var reg1 uint16 bit_akku;
  8618.           # angefangenes Byte holen:
  8619.           bit_akku = (*ptr)>>bitshift;
  8620.           bitshift = 8-bitshift; # bitshift := 8-bitindex
  8621.           count -= bitshift;
  8622.           loop
  8623.             { # index incrementieren, da gerade *ptr verarbeitet:
  8624.               TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  8625.               # Von bit_akku sind die Bits (bitshift-1)..0 gⁿltig.
  8626.               # Noch count (>0) Bits zu holen.
  8627.              {var reg3 uintB* ptr = b_file_nextbyte(stream);
  8628.               if (ptr == (uintB*)NULL) goto eof;
  8629.               # nΣchstes Byte holen:
  8630.               bit_akku |= (uint16)(*ptr)<<bitshift;
  8631.              }# Von bit_akku sind die Bits (7+bitshift)..0 gⁿltig.
  8632.               *--bitbufferptr = (uint8)bit_akku; # 8 Bit abspeichern
  8633.               if (count<=8) break; # Sind damit count Bits fertig?
  8634.               count -= 8;
  8635.               bit_akku = bit_akku>>8;
  8636.             }
  8637.         }
  8638.       TheStream(stream)->strm_file_bitindex = fixnum(count);
  8639.       # position incrementieren:
  8640.       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  8641.       # in Zahl umwandeln:
  8642.       return (*finisher)(stream,bitsize,bytesize);
  8643.       eof: # EOF erreicht
  8644.       position_i_file(stream,posfixnum_to_L(TheStream(stream)->strm_file_position));
  8645.       return eof_value;
  8646.     }
  8647.  
  8648. # UP fⁿr WRITE-BYTE auf File-Streams fⁿr Integers, Art a :
  8649. # Schreibt den Bitbuffer-Inhalt aufs File.
  8650.   local void wr_by_ia (object stream, uintL bitsize, uintL bytesize);
  8651.   local void wr_by_ia(stream,bitsize,bytesize)
  8652.     var reg3 object stream;
  8653.     var uintL bitsize;
  8654.     var reg4 uintL bytesize;
  8655.     { var reg1 uintB* bitbufferptr = &TheSbvector(TheStream(stream)->strm_file_bitbuffer)->data[bytesize];
  8656.       var reg2 uintL count;
  8657.       dotimespL(count,bytesize, { b_file_writebyte(stream,*--bitbufferptr); } );
  8658.       # position incrementieren:
  8659.       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  8660.     }
  8661.  
  8662. # UP fⁿr WRITE-BYTE auf File-Streams fⁿr Integers, Art b :
  8663. # Schreibt den Bitbuffer-Inhalt aufs File.
  8664.   local void wr_by_ib (object stream, uintL bitsize, uintL bytesize);
  8665.   local void wr_by_ib(stream,bitsize,bytesize)
  8666.     var reg4 object stream;
  8667.     var reg6 uintL bitsize;
  8668.     var uintL bytesize;
  8669.     { var reg1 uintL bitshift = posfixnum_to_L(TheStream(stream)->strm_file_bitindex);
  8670.       var reg3 uint16 bit_akku = (uint16)(TheSbvector(TheStream(stream)->strm_file_bitbuffer)->data[0])<<bitshift;
  8671.       var reg2 uintL count = bitsize;
  8672.       var reg5 uintB* ptr = b_file_nextbyte(stream);
  8673.       # angefangenes Byte holen:
  8674.       if (!(ptr == (uintB*)NULL)) { bit_akku |= (*ptr)&(bit(bitshift)-1); }
  8675.       count += bitshift;
  8676.       # evtl. einzelnes Byte schreiben:
  8677.       if (count>=8)
  8678.         { b_file_writebyte(stream,(uint8)bit_akku);
  8679.           bit_akku = bit_akku>>8;
  8680.           count -= 8;
  8681.         }
  8682.       # letztes Byte (count Bits) schreiben:
  8683.       if (!(count==0))
  8684.         { ptr = b_file_nextbyte(stream);
  8685.           if (ptr == (uintB*)NULL) # EOF ?
  8686.             { ptr = b_file_eofbyte(stream); # 1 Byte Platz machen
  8687.               *ptr = (uint8)bit_akku; # Byte schreiben
  8688.             }
  8689.             else
  8690.             # nΣchstes Byte nur teilweise ⁿberschreiben:
  8691.             { var reg3 uint8 diff = (*ptr ^ (uint8)bit_akku) & (uint8)(bit(count)-1);
  8692.               if (diff == 0) goto no_modification;
  8693.               *ptr ^= diff;
  8694.             }
  8695.           set_modified_flag(stream);
  8696.           no_modification: ;
  8697.         }
  8698.       TheStream(stream)->strm_file_bitindex = fixnum(count);
  8699.       # position und evtl. eofposition incrementieren:
  8700.       if (eq(TheStream(stream)->strm_file_eofposition,TheStream(stream)->strm_file_position))
  8701.         { TheStream(stream)->strm_file_eofposition = fixnum_inc(TheStream(stream)->strm_file_eofposition,1); }
  8702.       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  8703.     }
  8704.  
  8705. # UP fⁿr WRITE-BYTE auf File-Streams fⁿr Integers, Art c :
  8706. # Schreibt den Bitbuffer-Inhalt aufs File.
  8707.   local void wr_by_ic (object stream, uintL bitsize, uintL bytesize);
  8708.   local void wr_by_ic(stream,bitsize,bytesize)
  8709.     var reg5 object stream;
  8710.     var reg7 uintL bitsize;
  8711.     var reg8 uintL bytesize;
  8712.     { var reg1 uintB* bitbufferptr = &TheSbvector(TheStream(stream)->strm_file_bitbuffer)->data[bytesize];
  8713.       var reg2 uintL bitshift = posfixnum_to_L(TheStream(stream)->strm_file_bitindex);
  8714.       var reg3 uintL count = bitsize;
  8715.       var reg4 uint16 bit_akku;
  8716.       var reg6 uintB* ptr = b_file_nextbyte(stream);
  8717.       # angefangenes Byte holen:
  8718.       bit_akku = (ptr==(uintB*)NULL ? 0 : (*ptr)&(bit(bitshift)-1) );
  8719.       count += bitshift;
  8720.       # einzelne Bytes schreiben:
  8721.       loop
  8722.         { bit_akku |= (uint16)(*--bitbufferptr)<<bitshift;
  8723.           if (count<8) break;
  8724.           b_file_writebyte(stream,(uint8)bit_akku);
  8725.           bit_akku = bit_akku>>8;
  8726.           count -= 8;
  8727.           if (count<=bitshift) break;
  8728.         }
  8729.       # letztes Byte (count Bits) schreiben:
  8730.       if (!(count==0))
  8731.         { ptr = b_file_nextbyte(stream);
  8732.           if (ptr == (uintB*)NULL) # EOF ?
  8733.             { ptr = b_file_eofbyte(stream); # 1 Byte Platz machen
  8734.               *ptr = (uint8)bit_akku; # Byte schreiben
  8735.             }
  8736.             else
  8737.             # nΣchstes Byte nur teilweise ⁿberschreiben:
  8738.             { var reg3 uint8 diff = (*ptr ^ (uint8)bit_akku) & (uint8)(bit(count)-1);
  8739.               if (diff == 0) goto no_modification;
  8740.               *ptr ^= diff;
  8741.             }
  8742.           set_modified_flag(stream);
  8743.           no_modification: ;
  8744.         }
  8745.       TheStream(stream)->strm_file_bitindex = fixnum(count);
  8746.       # position incrementieren:
  8747.       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  8748.     }
  8749.  
  8750. # Typ wr_by_ix: eines dieser drei Unterprogramme:
  8751.   typedef void wr_by_ix (object stream, uintL bitsize, uintL bytesize);
  8752.  
  8753. # UP fⁿr WRITE-BYTE auf File-Streams fⁿr Integers, Art u :
  8754. # Legt das Objekt (ein Integer >=0) als bytesize Bytes im Bitbuffer ab.
  8755. # > stream : File-Stream fⁿr Integers, Art u
  8756. # > obj : auszugebendes Objekt
  8757. # > finisher : Beendigungsroutine
  8758.   local void wr_by_ixu_file (object stream, object obj, wr_by_ix* finisher);
  8759.   local void wr_by_ixu_file(stream,obj,finisher)
  8760.     var reg1 object stream;
  8761.     var reg5 object obj;
  8762.     var reg8 wr_by_ix* finisher;
  8763.     { # obj ⁿberprⁿfen:
  8764.       if (!integerp(obj)) { fehler_wr_integer(stream,obj); }
  8765.       if (!positivep(obj)) { fehler_bad_integer(stream,obj); }
  8766.       # obj ist jetzt ein Integer >=0
  8767.      {var reg6 uintL bitsize = posfixnum_to_L(TheStream(stream)->strm_file_bitsize);
  8768.       var reg6 uintL bytesize = ceiling(bitsize,8);
  8769.       # obj in den Bitbuffer ⁿbertragen:
  8770.       { var reg2 uintB* bitbufferptr = &TheSbvector(TheStream(stream)->strm_file_bitbuffer)->data[bytesize];
  8771.         var reg4 uintL count = bytesize;
  8772.         if (posfixnump(obj))
  8773.           # obj ist ein Fixnum >=0
  8774.           { var reg3 uintL wert = posfixnum_to_L(obj);
  8775.             # wert < 2^bitsize ⁿberprⁿfen:
  8776.             if (!((bitsize>=oint_data_len) || (wert < bit(bitsize))))
  8777.               { fehler_bad_integer(stream,obj); }
  8778.             # wert im Bitbuffer ablegen:
  8779.             until (wert==0)
  8780.               { *--bitbufferptr = (uint8)wert; wert = wert>>8; count--; }
  8781.           }
  8782.           else
  8783.           # obj ist ein Bignum >0
  8784.           { var reg5 uintL len = (uintL)(TheBignum(obj)->length);
  8785.             # obj < 2^bitsize ⁿberprⁿfen:
  8786.             if (!((floor(bitsize,intDsize) >= len)
  8787.                   || ((floor(bitsize,intDsize) == len-1)
  8788.                       && (TheBignum(obj)->data[0] < bit(bitsize%intDsize))
  8789.                ) )   )
  8790.               { fehler_bad_integer(stream,obj); }
  8791.             #if BIG_ENDIAN_P
  8792.             {var reg3 uintB* ptr = (uintB*)&TheBignum(obj)->data[len];
  8793.              # Digit-LΣnge in Byte-LΣnge umrechnen:
  8794.              len = (intDsize/8)*len;
  8795.              #define CHECK_NEXT_BYTE(i)  \
  8796.                if (!( ((uintB*)(&TheBignum(obj)->data[0]))[i] ==0)) goto len_ok; \
  8797.                len--;
  8798.              DOCONSTTIMES(intDsize/8,CHECK_NEXT_BYTE); # CHECK_NEXT_BYTE(0..intDsize/8-1)
  8799.              #undef CHECK_NEXT_BYTE
  8800.              len_ok:
  8801.              # obj im Bitbuffer ablegen:
  8802.              count = count - len;
  8803.              dotimespL(len,len, { *--bitbufferptr = *--ptr; } );
  8804.             }
  8805.             #else
  8806.             {var reg3 uintD* ptr = &TheBignum(obj)->data[len];
  8807.              len--;
  8808.              count -= (intDsize/8)*len;
  8809.              dotimesL(len,len,
  8810.                { var reg2 uintD digit = *--ptr;
  8811.                  doconsttimes(intDsize/8,
  8812.                    { *--bitbufferptr = (uintB)digit; digit = digit >> 8; }
  8813.                    );
  8814.                });
  8815.              {var reg2 uintD digit = *--ptr;
  8816.               doconsttimes(intDsize/8,
  8817.                 { if (digit==0) goto ok;
  8818.                   *--bitbufferptr = (uintB)digit; digit = digit >> 8;
  8819.                   count--;
  8820.                 });
  8821.               ok: ;
  8822.             }}
  8823.             #endif
  8824.           }
  8825.         dotimesL(count,count, { *--bitbufferptr = 0; } );
  8826.       }
  8827.       (*finisher)(stream,bitsize,bytesize);
  8828.     }}
  8829.  
  8830. # UP fⁿr WRITE-BYTE auf File-Streams fⁿr Integers, Art s :
  8831. # Legt das Objekt (ein Integer) als bytesize Bytes im Bitbuffer ab.
  8832. # > stream : File-Stream fⁿr Integers, Art s
  8833. # > obj : auszugebendes Objekt
  8834. # > finisher : Beendigungsroutine
  8835.   local void wr_by_ixs_file (object stream, object obj, wr_by_ix* finisher);
  8836.   local void wr_by_ixs_file(stream,obj,finisher)
  8837.     var reg1 object stream;
  8838.     var reg5 object obj;
  8839.     var reg8 wr_by_ix* finisher;
  8840.     { # obj ⁿberprⁿfen:
  8841.       if (!integerp(obj)) { fehler_wr_integer(stream,obj); }
  8842.       # obj ist jetzt ein Integer
  8843.      {var reg6 uintL bitsize = posfixnum_to_L(TheStream(stream)->strm_file_bitsize);
  8844.       var reg6 uintL bytesize = ceiling(bitsize,8);
  8845.       # obj in den Bitbuffer ⁿbertragen:
  8846.       { var reg2 uintB* bitbufferptr = &TheSbvector(TheStream(stream)->strm_file_bitbuffer)->data[bytesize];
  8847.         var reg4 uintL count = bytesize;
  8848.         var reg6 uintL sign = (sintL)R_sign(obj);
  8849.         if (fixnump(obj))
  8850.           # obj ist ein Fixnum
  8851.           { var reg3 uintL wert = fixnum_to_L(obj); # >=0 oder <0, je nach sign
  8852.             # 0 <= wert < 2^(bitsize-1) bzw. -2^(bitsize-1) <= wert < 0 ⁿberprⁿfen:
  8853.             wert = wert^sign;
  8854.             if (!((bitsize>oint_data_len) || (wert < bit(bitsize-1))))
  8855.               { fehler_bad_integer(stream,obj); }
  8856.             # wert^sign im Bitbuffer ablegen:
  8857.             until (wert == 0)
  8858.               { *--bitbufferptr = (uint8)(wert^sign); wert = wert>>8; count--; }
  8859.             dotimesL(count,count, { *--bitbufferptr = (uint8)sign; } );
  8860.           }
  8861.           else
  8862.           # obj ist ein Bignum
  8863.           { var reg5 uintL len = (uintL)(TheBignum(obj)->length);
  8864.             # -2^(bitsize-1) <= obj < 2^(bitsize-1) ⁿberprⁿfen:
  8865.             if (!((floor(bitsize,intDsize) >= len)
  8866.                   || ((bitsize > intDsize*(len-1))
  8867.                       && ((TheBignum(obj)->data[0] ^ (uintD)sign) < bit((bitsize%intDsize)-1))
  8868.                ) )   )
  8869.               { fehler_bad_integer(stream,obj); }
  8870.             #if BIG_ENDIAN_P
  8871.             {var reg3 uintB* ptr = (uintB*)&TheBignum(obj)->data[len];
  8872.              # Digit-LΣnge in Byte-LΣnge umrechnen:
  8873.              len = (intDsize/8)*len;
  8874.              #define CHECK_NEXT_BYTE(i)  \
  8875.                if (!( ((uintB*)(&TheBignum(obj)->data[0]))[i] == (uintB)sign)) goto len_ok; \
  8876.                len--;
  8877.              DOCONSTTIMES(intDsize/8,CHECK_NEXT_BYTE); # CHECK_NEXT_BYTE(0..intDsize/8-1)
  8878.              #undef CHECK_NEXT_BYTE
  8879.              len_ok:
  8880.              # obj im Bitbuffer ablegen:
  8881.              count = count - len;
  8882.              dotimespL(len,len, { *--bitbufferptr = *--ptr; } );
  8883.             }
  8884.             #else
  8885.             {var reg3 uintD* ptr = &TheBignum(obj)->data[len];
  8886.              len--;
  8887.              count -= (intDsize/8)*len;
  8888.              dotimesL(len,len,
  8889.                { var reg2 uintD digit = *--ptr;
  8890.                  doconsttimes(intDsize/8,
  8891.                    { *--bitbufferptr = (uintB)digit; digit = digit >> 8; }
  8892.                    );
  8893.                });
  8894.              {var reg2 sintD digit = *--ptr;
  8895.               doconsttimes(intDsize/8,
  8896.                 { if (digit == (sintD)sign) goto ok;
  8897.                   *--bitbufferptr = (uintB)digit; digit = digit >> 8;
  8898.                   count--;
  8899.                 });
  8900.               ok: ;
  8901.             }}
  8902.             #endif
  8903.             dotimesL(count,count, { *--bitbufferptr = (uintB)sign; } );
  8904.           }
  8905.       }
  8906.       (*finisher)(stream,bitsize,bytesize);
  8907.     }}
  8908.  
  8909. # READ-BYTE - Pseudofunktion fⁿr File-Streams fⁿr Integers, Art au :
  8910.   local object rd_by_iau_file (object stream);
  8911.   local object rd_by_iau_file(stream)
  8912.     var reg1 object stream;
  8913.     { return rd_by_iax_file(stream,&rd_by_iu_I); }
  8914.  
  8915. # WRITE-BYTE - Pseudofunktion fⁿr File-Streams fⁿr Integers, Art au :
  8916.   local void wr_by_iau_file (object stream, object obj);
  8917.   local void wr_by_iau_file(stream,obj)
  8918.     var reg1 object stream;
  8919.     var reg2 object obj;
  8920.     { wr_by_ixu_file(stream,obj,&wr_by_ia); }
  8921.  
  8922. # READ-BYTE - Pseudofunktion fⁿr File-Streams fⁿr Integers, Art as :
  8923.   local object rd_by_ias_file (object stream);
  8924.   local object rd_by_ias_file(stream)
  8925.     var reg1 object stream;
  8926.     { return rd_by_iax_file(stream,&rd_by_is_I); }
  8927.  
  8928. # WRITE-BYTE - Pseudofunktion fⁿr File-Streams fⁿr Integers, Art as :
  8929.   local void wr_by_ias_file (object stream, object obj);
  8930.   local void wr_by_ias_file(stream,obj)
  8931.     var reg1 object stream;
  8932.     var reg2 object obj;
  8933.     { wr_by_ixs_file(stream,obj,&wr_by_ia); }
  8934.  
  8935. # READ-BYTE - Pseudofunktion fⁿr File-Streams fⁿr Integers, Art bu :
  8936.   local object rd_by_ibu_file (object stream);
  8937.   local object rd_by_ibu_file(stream)
  8938.     var reg1 object stream;
  8939.     { return rd_by_ibx_file(stream,&rd_by_iu_I); }
  8940.  
  8941. # WRITE-BYTE - Pseudofunktion fⁿr File-Streams fⁿr Integers, Art bu :
  8942.   local void wr_by_ibu_file (object stream, object obj);
  8943.   local void wr_by_ibu_file(stream,obj)
  8944.     var reg1 object stream;
  8945.     var reg2 object obj;
  8946.     { wr_by_ixu_file(stream,obj,&wr_by_ib); }
  8947.  
  8948. # READ-BYTE - Pseudofunktion fⁿr File-Streams fⁿr Integers, Art bs :
  8949.   local object rd_by_ibs_file (object stream);
  8950.   local object rd_by_ibs_file(stream)
  8951.     var reg1 object stream;
  8952.     { return rd_by_ibx_file(stream,&rd_by_is_I); }
  8953.  
  8954. # WRITE-BYTE - Pseudofunktion fⁿr File-Streams fⁿr Integers, Art bs :
  8955.   local void wr_by_ibs_file (object stream, object obj);
  8956.   local void wr_by_ibs_file(stream,obj)
  8957.     var reg1 object stream;
  8958.     var reg2 object obj;
  8959.     { wr_by_ixs_file(stream,obj,&wr_by_ib); }
  8960.  
  8961. # READ-BYTE - Pseudofunktion fⁿr File-Streams fⁿr Integers, Art cu :
  8962.   local object rd_by_icu_file (object stream);
  8963.   local object rd_by_icu_file(stream)
  8964.     var reg1 object stream;
  8965.     { return rd_by_icx_file(stream,&rd_by_iu_I); }
  8966.  
  8967. # WRITE-BYTE - Pseudofunktion fⁿr File-Streams fⁿr Integers, Art cu :
  8968.   local void wr_by_icu_file (object stream, object obj);
  8969.   local void wr_by_icu_file(stream,obj)
  8970.     var reg1 object stream;
  8971.     var reg2 object obj;
  8972.     { wr_by_ixu_file(stream,obj,&wr_by_ic); }
  8973.  
  8974. # READ-BYTE - Pseudofunktion fⁿr File-Streams fⁿr Integers, Art cs :
  8975.   local object rd_by_ics_file (object stream);
  8976.   local object rd_by_ics_file(stream)
  8977.     var reg1 object stream;
  8978.     { return rd_by_icx_file(stream,&rd_by_is_I); }
  8979.  
  8980. # WRITE-BYTE - Pseudofunktion fⁿr File-Streams fⁿr Integers, Art cs :
  8981.   local void wr_by_ics_file (object stream, object obj);
  8982.   local void wr_by_ics_file(stream,obj)
  8983.     var reg1 object stream;
  8984.     var reg2 object obj;
  8985.     { wr_by_ixs_file(stream,obj,&wr_by_ic); }
  8986.  
  8987. # WRITE-BYTE-SEQUENCE fⁿr File-Streams fⁿr Integers, Art au, bitsize = 8 :
  8988.   local uintB* write_byte_array_iau8_file (object stream, uintB* byteptr, uintL len);
  8989.   local uintB* write_byte_array_iau8_file(stream,byteptr,len)
  8990.     var reg5 object stream;
  8991.     var reg2 uintB* byteptr;
  8992.     var reg9 uintL len;
  8993.     { var reg6 uintL remaining = len;
  8994.       var reg1 uintB* ptr;
  8995.       do # Noch remaining>0 Bytes abzulegen.
  8996.         { ptr = b_file_nextbyte(stream);
  8997.           if (ptr == (uintB*)NULL) goto eof_reached;
  8998.          {var reg8 object eofindex = TheStream(stream)->strm_file_eofindex;
  8999.           var reg7 uintL next = # so viel wie noch in den Buffer oder bis EOF pa▀t
  9000.             (eq(eofindex,T) ? strm_file_bufflen : posfixnum_to_L(eofindex))
  9001.             - (uintW)posfixnum_to_L(TheStream(stream)->strm_file_index); # > 0 !
  9002.           if (next > remaining) { next = remaining; }
  9003.           # next Bytes in den Buffer kopieren:
  9004.           {var reg4 uintL count;
  9005.            dotimespL(count,next,
  9006.              { var reg3 uintB b = *byteptr++; # nΣchstes Byte
  9007.                if (!(*ptr == b)) { *ptr = b; set_modified_flag(stream); } # in den Buffer
  9008.                ptr++;
  9009.              });
  9010.           }
  9011.           remaining = remaining - next;
  9012.           # index incrementieren:
  9013.           TheStream(stream)->strm_file_index =
  9014.             fixnum_inc(TheStream(stream)->strm_file_index,next);
  9015.         }}
  9016.         until (remaining == 0);
  9017.       if (FALSE)
  9018.         eof_reached: # Schreiben am EOF, eofindex = index
  9019.         do # Noch remaining>0 Bytes abzulegen.
  9020.           { var reg7 uintL next = # so viel wie noch Platz im Buffer ist
  9021.               strm_file_bufflen
  9022.               - (uintW)posfixnum_to_L(TheStream(stream)->strm_file_index);
  9023.             if (next==0)
  9024.               { # Buffer mu▀ neu gefⁿllt werden. Da nach ihm sowieso EOF kommt,
  9025.                 # genⁿgt es, ihn hinauszuschreiben:
  9026.                 if (modified_flag(stream)) { b_file_half_flush(stream); }
  9027.                 TheStream(stream)->strm_file_buffstart =
  9028.                   fixnum_inc(TheStream(stream)->strm_file_buffstart,strm_file_bufflen);
  9029.                 TheStream(stream)->strm_file_eofindex = Fixnum_0; # eofindex := 0
  9030.                 TheStream(stream)->strm_file_index = Fixnum_0; # index := 0, unmodifiziert
  9031.                 # Dann nochmals versuchen:
  9032.                 next = strm_file_bufflen;
  9033.               }
  9034.             if (next > remaining) { next = remaining; }
  9035.             # next Bytes in den Buffer kopieren:
  9036.             {var reg3 uintL count;
  9037.              ptr = &TheSstring(TheStream(stream)->strm_file_buffer)->data[(uintW)posfixnum_to_L(TheStream(stream)->strm_file_index)];
  9038.              dotimespL(count,next, { *ptr++ = *byteptr++; } );
  9039.              set_modified_flag(stream);
  9040.             }
  9041.             remaining = remaining - next;
  9042.             # index und eofindex incrementieren:
  9043.             TheStream(stream)->strm_file_index =
  9044.               fixnum_inc(TheStream(stream)->strm_file_index,next);
  9045.             TheStream(stream)->strm_file_eofindex =
  9046.               fixnum_inc(TheStream(stream)->strm_file_eofindex,next);
  9047.           }
  9048.           until (remaining == 0);
  9049.       # position incrementieren:
  9050.       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,len);
  9051.       return byteptr;
  9052.     }
  9053.  
  9054. # File-Stream allgemein
  9055. # =====================
  9056.  
  9057. # UP: Positioniert einen (offenen) File-Stream an den Anfang.
  9058. # position_file_start(stream);
  9059. # > stream : (offener) File-Stream.
  9060. # verΣndert in stream: index, eofindex, buffstart, ..., position, rd_ch_last
  9061.   local void position_file_start (object stream);
  9062.   local void position_file_start(stream)
  9063.     var reg1 object stream;
  9064.     { position_b_file(stream,
  9065.                       TheStream(stream)->strmflags & strmflags_ib_B # Integer-Stream vom Typ b ?
  9066.                       ? sizeof(uintL) : 0 # ja -> Position 4, sonst Position 0
  9067.                      );
  9068.       if (TheStream(stream)->strmflags & (strmflags_ib_B | strmflags_ic_B))
  9069.         # Integer-Stream der Art b,c
  9070.         { TheStream(stream)->strm_file_bitindex = Fixnum_0; } # bitindex := 0
  9071.       TheStream(stream)->strm_file_position = Fixnum_0; # position := 0
  9072.       TheStream(stream)->strm_rd_ch_last = NIL; # Lastchar := NIL
  9073.     }
  9074.  
  9075. # UP: Positioniert einen (offenen) File-Stream an eine gegebene Position.
  9076. # position_file(stream,position);
  9077. # > stream : (offener) File-Stream.
  9078. # > position : neue (logische) Position
  9079. # verΣndert in stream: index, eofindex, buffstart, ..., position, rd_ch_last
  9080.   local void position_file (object stream, uintL position);
  9081.   local void position_file(stream,position)
  9082.     var reg1 object stream;
  9083.     var reg2 uintL position;
  9084.     { var reg3 uintB flags = TheStream(stream)->strmflags;
  9085.       if (flags & strmflags_i_B) # Integer-Stream ?
  9086.         { if (flags & strmflags_ia_B)
  9087.             # Art a
  9088.             { var reg4 uintL bitsize = posfixnum_to_L(TheStream(stream)->strm_file_bitsize);
  9089.               position_b_file(stream,position*(bitsize/8));
  9090.             }
  9091.             else
  9092.             # Art b,c
  9093.             { position_i_file(stream,position); }
  9094.         }
  9095.         else
  9096.         { if (TheStream(stream)->strmtype == strmtype_ch_file) # Character-Stream ?
  9097.             { position_b_file(stream,position*char_size); }
  9098.           else # String-Char-Stream
  9099.             { position_b_file(stream,position); }
  9100.           TheStream(stream)->strm_rd_ch_last = NIL; # Lastchar := NIL
  9101.         }
  9102.       TheStream(stream)->strm_file_position = fixnum(position);
  9103.     }
  9104.  
  9105. # UP: Positioniert einen (offenen) File-Stream ans Ende.
  9106. # position_file_end(stream);
  9107. # > stream : (offener) File-Stream.
  9108. # verΣndert in stream: index, eofindex, buffstart, ..., position, rd_ch_last
  9109.   local void position_file_end (object stream);
  9110.   local void position_file_end(stream)
  9111.     var reg1 object stream;
  9112.     { # evtl. Buffer hinausschreiben:
  9113.       if (modified_flag(stream)) { b_file_flush(stream); }
  9114.      {var reg2 uintL eofbytes; # EOF-Position, gemessen in Bytes
  9115.       # ans Ende positionieren:
  9116.       begin_system_call();
  9117.       file_lseek(stream,0,SEEK_END,eofbytes=);
  9118.       end_system_call();
  9119.       # logische Position berechnen und eofbytes korrigieren:
  9120.       { var reg5 uintL position; # logische Position
  9121.         var reg6 uintL eofbits = 0; # Bit-ErgΣnzung zu eofbytes
  9122.         var reg3 uintB flags = TheStream(stream)->strmflags;
  9123.         if (flags & strmflags_i_B) # Integer-Stream ?
  9124.           { var reg4 uintL bitsize = posfixnum_to_L(TheStream(stream)->strm_file_bitsize);
  9125.             if (flags & strmflags_ia_B)
  9126.               # Art a
  9127.               { var reg4 uintL bytesize = bitsize/8;
  9128.                 position = floor(eofbytes,bytesize);
  9129.                 eofbytes = position*bytesize;
  9130.               }
  9131.             elif (flags & strmflags_ib_B)
  9132.               # Art b
  9133.               { eofbytes -= sizeof(uintL); # Header berⁿcksichtigen
  9134.                 # Ist die gemerkte EOF-Position plausibel?
  9135.                 position = posfixnum_to_L(TheStream(stream)->strm_file_eofposition);
  9136.                 if (!(ceiling(position*bitsize,8)==eofbytes)) # ja -> verwende sie
  9137.                   { position = floor(eofbytes*8,bitsize); } # nein -> rechne sie neu aus
  9138.                 # Rechne eofbytes und eofbits neu aus:
  9139.                 eofbytes = floor(position*bitsize,8);
  9140.                 eofbits = (position*bitsize)%8;
  9141.                 eofbytes += sizeof(uintL); # Header berⁿcksichtigen
  9142.               }
  9143.             else
  9144.               # Art c
  9145.               { position = floor(eofbytes*8,bitsize);
  9146.                 eofbytes = floor(position*bitsize,8);
  9147.                 eofbits = (position*bitsize)%8;
  9148.               }
  9149.           }
  9150.           else
  9151.           { if (TheStream(stream)->strmtype == strmtype_ch_file) # Character-Stream ?
  9152.               { position = floor(eofbytes,char_size); eofbytes = position*char_size; }
  9153.             else # String-Char-Stream
  9154.               { position = eofbytes; }
  9155.           }
  9156.         # auf den Anfang des letzten Sectors positionieren:
  9157.         { var reg4 uintL buffstart;
  9158.           begin_system_call();
  9159.           file_lseek(stream,floor(eofbytes,strm_file_bufflen)*strm_file_bufflen,SEEK_SET,buffstart=);
  9160.           end_system_call();
  9161.           TheStream(stream)->strm_file_buffstart = fixnum(buffstart);
  9162.         }
  9163.         # Sector lesen:
  9164.         TheStream(stream)->strm_file_eofindex = NIL; # eofindex := NIL
  9165.         TheStream(stream)->strm_file_index = Fixnum_0; # index := 0, unmodifiziert
  9166.         { var reg4 uintL eofindex = eofbytes % strm_file_bufflen;
  9167.           if (!((eofindex==0) && (eofbits==0))) # EOF am Sectorende -> brauche nichts zu lesen
  9168.             { b_file_nextbyte(stream);
  9169.               # Jetzt ist index=0. index und eofindex setzen:
  9170.               TheStream(stream)->strm_file_index = fixnum(eofindex);
  9171.               if (!(eofbits==0)) { eofindex += 1; }
  9172.               TheStream(stream)->strm_file_eofindex = fixnum(eofindex);
  9173.         }   }
  9174.         if (flags & (strmflags_ib_B | strmflags_ic_B))
  9175.           # Integer-Stream der Art b,c
  9176.           { TheStream(stream)->strm_file_bitindex = fixnum(eofbits); }
  9177.         # position setzen:
  9178.         TheStream(stream)->strm_file_position = fixnum(position);
  9179.         TheStream(stream)->strm_rd_ch_last = NIL; # Lastchar := NIL
  9180.     }}}
  9181.  
  9182. # UP: erzeugt ein File-Stream
  9183. # make_file_stream(handle,direction,type,eltype_size,append_flag)
  9184. # > handle: Handle des ge÷ffneten Files
  9185. # > STACK_1: Filename, ein Pathname
  9186. # > STACK_0: Truename, ein Pathname
  9187. # > direction: Modus (0 = :PROBE, 1 = :INPUT, 4 = :OUTPUT, 5 = :IO, 3 = :INPUT-IMMUTABLE)
  9188. # > type: nΣhere Typinfo
  9189. #         (STRMTYPE_SCH_FILE oder STRMTYPE_CH_FILE oder
  9190. #          STRMTYPE_IU_FILE oder STRMTYPE_IS_FILE)
  9191. # > eltype_size: (bei Integer-Streams) Gr÷▀e der Elemente in Bits,
  9192. #         ein Fixnum >0 und <intDsize*uintC_max
  9193. # > append_flag: TRUE falls der Stream gleich ans Ende positioniert werden
  9194. #         soll, FALSE sonst
  9195. # < ergebnis: File-Stream (oder evtl. File-Handle-Stream)
  9196. # < STACK: aufgerΣumt
  9197. # kann GC ausl÷sen
  9198.   global object make_file_stream (object handle, uintB direction, uintB type, object eltype_size, boolean append_flag);
  9199.   global object make_file_stream(handle,direction,type,eltype_size,append_flag)
  9200.     var reg9 object handle;
  9201.     var reg8 uintB direction;
  9202.     var reg4 uintB type;
  9203.     var reg9 object eltype_size;
  9204.     var reg10 boolean append_flag;
  9205.     {
  9206.       #if defined(HANDLES)
  9207.       # Nur regulΣre Files zu gebufferten File-Streams machen.
  9208.       # Alles andere gibt File-Handle-Streams, weil vermutlich lseek() nicht geht.
  9209.       if (!nullp(handle))
  9210.         {
  9211.           #if defined(UNIX) || defined(MSDOS) # || defined(RISCOS)
  9212.           var struct stat statbuf;
  9213.           begin_system_call();
  9214.           if (!( fstat(TheHandle(handle),&statbuf) ==0)) { OS_error(); }
  9215.           end_system_call();
  9216.           if (!S_ISREG(statbuf.st_mode))
  9217.           #endif
  9218.           #ifdef AMIGAOS
  9219.           var reg1 LONG not_regular_p;
  9220.           begin_system_call();
  9221.           not_regular_p = IsInteractive(TheHandle(handle)); # Behandlung nicht interaktiver, nicht regulΣrer Files??
  9222.           end_system_call();
  9223.           if (not_regular_p)
  9224.           #endif
  9225.             { if (((type == strmtype_sch_file)
  9226.                    || ((type == strmtype_iu_file) && eq(eltype_size,fixnum(8)))
  9227.                   )
  9228.                   && !append_flag
  9229.                  )
  9230.                 { return make_handle_stream(handle,direction); }
  9231.                 else
  9232.                 { # Truename noch in STACK_0, Wert fⁿr Slot PATHNAME von FILE-ERROR
  9233.                   pushSTACK(STACK_0);
  9234.                   pushSTACK(S(open));
  9235.                   fehler(file_error,
  9236.                          DEUTSCH ? "~: ~ ist kein regulΣres File." :
  9237.                          ENGLISH ? "~: ~ is not a regular file." :
  9238.                          FRANCAIS ? "~: ~ n'est pas un fichier rΘgulier." :
  9239.                          ""
  9240.                         );
  9241.         }   }   }
  9242.       #endif
  9243.      { # Flags:
  9244.        var reg6 uintB flags =
  9245.          (direction==0 ? 0 : # bei Modus :PROBE sind alle Flags =0
  9246.            # sonst:
  9247.            (direction>=4 ? strmflags_open_B : strmflags_rd_B) # Modus :INPUT[-IMMUTABLE] -> nur Read, sonst Read/Write
  9248.            &
  9249.            (type>=strmtype_iu_file ? strmflags_by_B : strmflags_ch_B) # auf Integers oder Characters
  9250.            #ifdef IMMUTABLE
  9251.            | (direction==3 ? strmflags_immut_B : 0) # Modus :INPUT-IMMUTABLE ?
  9252.            #endif
  9253.          );
  9254.        # Art von Integer-Streams:
  9255.        var reg5 uintB art;
  9256.        # LΣnge:
  9257.        var reg7 uintC len = strm_len; # Das hat jeder Stream
  9258.        len += 8; # Das haben alle File-Streams
  9259.        if (type==strmtype_sch_file)
  9260.          { len += 1; } # Das haben die File-Streams fⁿr String-Chars
  9261.        elif (type>=strmtype_iu_file)
  9262.          { len += 2; # Das haben die File-Streams fⁿr Integers
  9263.            {var reg1 uintL bitsize = posfixnum_to_L(eltype_size);
  9264.             if ((bitsize%8)==0)
  9265.               { art = strmflags_ia_bit_B; } # Art a
  9266.               else
  9267.               { len += 1; # Arten b,c
  9268.                 if (bitsize<8)
  9269.                   { art = strmflags_ib_bit_B; len += 1; } # Art b
  9270.                   else
  9271.                   { art = strmflags_ic_bit_B; } # Art c
  9272.            }  }
  9273.            flags |= bit(art); # Art in die Flags mit aufnehmen
  9274.          }
  9275.        #if defined(FOREIGN_HANDLE) || !NIL_IS_CONSTANT
  9276.        pushSTACK(handle); # Handle retten
  9277.        #endif
  9278.       {# Stream allozieren:
  9279.        var reg1 object stream = allocate_stream(flags,type,len);
  9280.        # und fⁿllen:
  9281.        # Komponenten aller Streams:
  9282.        switch (type)
  9283.          { case strmtype_sch_file:
  9284.              TheStream(stream)->strm_rd_ch = P(rd_ch_sch_file);
  9285.              TheStream(stream)->strm_wr_ch = P(wr_ch_sch_file);
  9286.              #ifdef STRM_WR_SS
  9287.              TheStream(stream)->strm_wr_ss = P(wr_ss_sch_file);
  9288.              #endif
  9289.              break;
  9290.            case strmtype_ch_file:
  9291.              TheStream(stream)->strm_rd_ch = P(rd_ch_ch_file);
  9292.              TheStream(stream)->strm_wr_ch = P(wr_ch_ch_file);
  9293.              #ifdef STRM_WR_SS
  9294.              TheStream(stream)->strm_wr_ss = P(wr_ss_dummy_nogc);
  9295.              #endif
  9296.              break;
  9297.            case strmtype_iu_file:
  9298.              TheStream(stream)->strm_rd_by =
  9299.                (art==strmflags_ia_bit_B ? P(rd_by_iau_file) :
  9300.                 art==strmflags_ib_bit_B ? P(rd_by_ibu_file) :
  9301.                                           P(rd_by_icu_file)
  9302.                );
  9303.              TheStream(stream)->strm_wr_by =
  9304.                (art==strmflags_ia_bit_B ? P(wr_by_iau_file) :
  9305.                 art==strmflags_ib_bit_B ? P(wr_by_ibu_file) :
  9306.                                           P(wr_by_icu_file)
  9307.                );
  9308.              break;
  9309.            case strmtype_is_file:
  9310.              TheStream(stream)->strm_rd_by =
  9311.                (art==strmflags_ia_bit_B ? P(rd_by_ias_file) :
  9312.                 art==strmflags_ib_bit_B ? P(rd_by_ibs_file) :
  9313.                                           P(rd_by_ics_file)
  9314.                );
  9315.              TheStream(stream)->strm_wr_by =
  9316.                (art==strmflags_ia_bit_B ? P(wr_by_ias_file) :
  9317.                 art==strmflags_ib_bit_B ? P(wr_by_ibs_file) :
  9318.                                           P(wr_by_ics_file)
  9319.                );
  9320.              break;
  9321.            default: NOTREACHED
  9322.          }
  9323.        # Default fⁿr READ-BYTE-Pseudofunktion:
  9324.        if ((flags & strmflags_rd_by_B)==0)
  9325.          { TheStream(stream)->strm_rd_by = P(rd_by_dummy); }
  9326.        # Default fⁿr WRITE-BYTE-Pseudofunktion:
  9327.        if ((flags & strmflags_wr_by_B)==0)
  9328.          { TheStream(stream)->strm_wr_by = P(wr_by_dummy); }
  9329.        # Default fⁿr READ-CHAR-Pseudofunktion:
  9330.        if ((flags & strmflags_rd_ch_B)==0)
  9331.          { TheStream(stream)->strm_rd_ch = P(rd_ch_dummy); }
  9332.        TheStream(stream)->strm_rd_ch_last = NIL; # Lastchar := NIL
  9333.        # Default fⁿr WRITE-CHAR-Pseudofunktion:
  9334.        if ((flags & strmflags_wr_ch_B)==0)
  9335.          { TheStream(stream)->strm_wr_ch = P(wr_ch_dummy);
  9336.            #ifdef STRM_WR_SS
  9337.            TheStream(stream)->strm_wr_ss = P(wr_ss_dummy);
  9338.            #endif
  9339.          }
  9340.        TheStream(stream)->strm_wr_ch_lpos = Fixnum_0; # Line Position := 0
  9341.        # Komponenten von File-Streams:
  9342.        #if defined(FOREIGN_HANDLE) || !NIL_IS_CONSTANT
  9343.        handle = popSTACK(); # Handle zurⁿck
  9344.        #endif
  9345.        TheStream(stream)->strm_file_truename = popSTACK(); # Truename eintragen
  9346.        TheStream(stream)->strm_file_name = popSTACK(); # Filename eintragen
  9347.        if (!nullp(handle)) # Handle=NIL -> Rest bereits mit NIL initialisiert, fertig
  9348.          { TheStream(stream)->strm_file_handle = handle; # Handle eintragen
  9349.            TheStream(stream)->strm_file_buffstart = Fixnum_0; # buffstart := 0
  9350.            # Buffer allozieren:
  9351.            pushSTACK(stream);
  9352.           {var reg2 object buffer = allocate_string(strm_file_bufflen); # neuer String
  9353.            stream = popSTACK();
  9354.            TheStream(stream)->strm_file_buffer = buffer;
  9355.           }
  9356.            TheStream(stream)->strm_file_eofindex = NIL; # eofindex := NIL
  9357.            TheStream(stream)->strm_file_index = Fixnum_0; # index := 0, Buffer unmodifiziert
  9358.            TheStream(stream)->strm_file_position = Fixnum_0; # position := 0
  9359.            if (type==strmtype_sch_file)
  9360.              # File-Stream fⁿr String-Chars
  9361.              { TheStream(stream)->strm_sch_file_lineno = Fixnum_1; }
  9362.            elif (type>=strmtype_iu_file)
  9363.              # File-Stream fⁿr Integers
  9364.              { TheStream(stream)->strm_file_bitsize = eltype_size;
  9365.                # Bitbuffer allozieren:
  9366.                pushSTACK(stream);
  9367.               {var reg2 object bitbuffer = allocate_bit_vector(ceiling(posfixnum_to_L(eltype_size),8)*8);
  9368.                stream = popSTACK();
  9369.                TheStream(stream)->strm_file_bitbuffer = bitbuffer;
  9370.               }
  9371.                if (!(art==strmflags_ia_bit_B))
  9372.                  # Arten b,c
  9373.                  { TheStream(stream)->strm_file_bitindex = Fixnum_0; # bitindex := 0
  9374.                    if (art==strmflags_ib_bit_B)
  9375.                      # Art b
  9376.                      { # eofposition lesen:
  9377.                        var reg3 uintL eofposition = 0;
  9378.                        var reg2 uintC count;
  9379.                        for (count=0; count < 8*sizeof(uintL); count += 8 )
  9380.                          { var reg1 uintB* ptr = b_file_nextbyte(stream);
  9381.                            if (ptr == (uintB*)NULL) goto too_short;
  9382.                            eofposition |= ((*ptr) << count);
  9383.                            # index incrementieren, da gerade *ptr verarbeitet:
  9384.                            TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  9385.                          }
  9386.                        if (FALSE)
  9387.                          { too_short:
  9388.                            # File zu kurz (< sizeof(uintL) Bytes)
  9389.                            if ((TheStream(stream)->strmflags & strmflags_wr_by_B) == 0) # Read-Only-Stream?
  9390.                              goto bad_eofposition;
  9391.                            # File Read/Write -> setze eofposition := 0
  9392.                            eofposition = 0;
  9393.                            position_b_file(stream,0); # an Position 0 positionieren
  9394.                           {var reg2 uintC count; # und eofposition = 0 herausschreiben
  9395.                            dotimespC(count,sizeof(uintL), { b_file_writebyte(stream,0); } );
  9396.                          }}
  9397.                        elif (eofposition > (uintL)(bitm(oint_data_len)-1))
  9398.                          { bad_eofposition:
  9399.                            # Keine gⁿltige EOF-Position.
  9400.                            # File schlie▀en und Error melden:
  9401.                            TheStream(stream)->strmflags &= ~strmflags_wr_by_B; # Stream Read-Only machen
  9402.                            pushSTACK(stream);
  9403.                            stream_close(&STACK_0);
  9404.                            # STACK_0 = Wert fⁿr Slot STREAM von STREAM-ERROR
  9405.                            pushSTACK(TheStream(STACK_0)->strm_file_truename);
  9406.                            fehler(stream_error,
  9407.                                   DEUTSCH ? "File ~ hat nicht das Format eines Integer-Files." :
  9408.                                   ENGLISH ? "file ~ is not an integer file" :
  9409.                                   FRANCAIS ? "Le fichier ~ n'a pas le format d'un fichier d'entiers." :
  9410.                                   ""
  9411.                                  );
  9412.                          }
  9413.                        # Auf die gelesene EOF-Position verlassen wir uns jetzt!
  9414.                        TheStream(stream)->strm_file_eofposition =
  9415.                          fixnum(eofposition);
  9416.              }   }   }
  9417.            # Liste der offenen File-Streams um stream erweitern:
  9418.            pushSTACK(stream);
  9419.           {var reg1 object new_cons = allocate_cons();
  9420.            Car(new_cons) = stream = popSTACK();
  9421.            Cdr(new_cons) = O(open_files);
  9422.            O(open_files) = new_cons;
  9423.           }# Modus :APPEND behandeln:
  9424.            if (append_flag) { position_file_end(stream); }
  9425.          }
  9426.        return stream;
  9427.     }}}
  9428.  
  9429. # UP: Bereitet das Schlie▀en eines File-Streams vor.
  9430. # Dabei wird der Buffer und evtl. eofposition hinausgeschrieben.
  9431. # file_flush(stream);
  9432. # > stream : (offener) File-Stream.
  9433. # verΣndert in stream: index, eofindex, buffstart, ...
  9434.   local void file_flush (object stream);
  9435.   local void file_flush(stream)
  9436.     var reg1 object stream;
  9437.     { # Bei Integer-Streams (Art b) eofposition abspeichern:
  9438.       if (TheStream(stream)->strmflags & strmflags_ib_B)
  9439.         if (TheStream(stream)->strmflags & strmflags_wr_by_B) # nur falls nicht Read-Only
  9440.           { position_b_file(stream,0); # an Position 0 positionieren
  9441.            {var reg2 uintL eofposition = posfixnum_to_L(TheStream(stream)->strm_file_eofposition);
  9442.             var reg3 uintC count;
  9443.             dotimespC(count,sizeof(uintL),
  9444.               { b_file_writebyte(stream,(uintB)eofposition);
  9445.                 eofposition = eofposition>>8;
  9446.               });
  9447.           }}
  9448.       # evtl. Buffer hinausschreiben:
  9449.       if (modified_flag(stream)) { b_file_flush(stream); }
  9450.       # Nun ist das modified_flag gel÷scht.
  9451.     }
  9452.  
  9453. # UP: Bringt den wartenden Output eines File-Stream ans Ziel.
  9454. # Schreibt dazu den Buffer des File-Streams (auch physikalisch) aufs File.
  9455. # finish_output_file(stream);
  9456. # > stream : File-Stream.
  9457. # verΣndert in stream: handle, index, eofindex, buffstart, ..., rd_ch_last
  9458. # kann GC ausl÷sen
  9459.   local void finish_output_file (object stream);
  9460.   local void finish_output_file(stream)
  9461.     var reg1 object stream;
  9462.     { # Handle=NIL (Stream bereits geschlossen) -> fertig:
  9463.       if (nullp(TheStream(stream)->strm_file_handle)) { return; }
  9464.       # kein File mit Schreibzugriff -> gar nichts zu tun:
  9465.       if (!(TheStream(stream)->strmflags & strmflags_wr_B)) { return; }
  9466.       # evtl. Buffer und evtl. eofposition hinausschreiben:
  9467.       file_flush(stream);
  9468.       # Nun ist das modified_flag gel÷scht.
  9469.      #ifdef ATARI
  9470.         # File schlie▀en (GEMDOS schreibt physikalisch):
  9471.        {var reg2 sintW ergebnis =
  9472.           GEMDOS_close(TheHandle(TheStream(stream)->strm_file_handle));
  9473.         if (ergebnis<0) { OS_error(ergebnis); } # Fehler aufgetreten?
  9474.        }
  9475.         # File neu ÷ffnen:
  9476.         pushSTACK(stream); # stream retten
  9477.         pushSTACK(TheStream(stream)->strm_file_truename); # Filename
  9478.        {# Directory existiert schon:
  9479.         var reg3 object namestring = assume_dir_exists(); # Filename als ASCIZ-String
  9480.         var reg2 sintW errorcode;
  9481.         errorcode = # Datei neu ÷ffnen, Modus 2 (Read/Write)
  9482.           GEMDOS_open(TheAsciz(namestring),2);
  9483.         if (errorcode < 0) { OS_error(errorcode); } # Error melden
  9484.         # Nun enthΣlt errorcode das Handle des ge÷ffneten Files.
  9485.         skipSTACK(1);
  9486.         stream = popSTACK(); # stream zurⁿck
  9487.         # neues Handle eintragen:
  9488.         TheStream(stream)->strm_file_handle = allocate_handle(errorcode);
  9489.        }
  9490.      #endif
  9491.      #ifdef MSDOS
  9492.        # File-Handle duplizieren und schlie▀en:
  9493.        { var reg3 uintW handle = TheHandle(TheStream(stream)->strm_file_handle);
  9494.          begin_system_call();
  9495.         {var reg2 sintW handle2 = dup(handle);
  9496.          if (handle2 < 0) { OS_error(); } # Error melden
  9497.          if ( CLOSE(handle2) <0) { OS_error(); }
  9498.          end_system_call();
  9499.        }}
  9500.      #endif
  9501.      #ifdef RISCOS # || MSDOS, wenn wir da nicht schon was besseres hΣtten
  9502.        # File schlie▀en (DOS schreibt physikalisch):
  9503.        begin_system_call();
  9504.        if ( CLOSE(TheHandle(TheStream(stream)->strm_file_handle)) <0) { OS_error(); }
  9505.        end_system_call();
  9506.        # File neu ÷ffnen:
  9507.        pushSTACK(stream); # stream retten
  9508.        pushSTACK(TheStream(stream)->strm_file_truename); # Filename
  9509.       {# Directory existiert schon:
  9510.        var reg3 object namestring = assume_dir_exists(); # Filename als ASCIZ-String
  9511.        var reg2 sintW handle;
  9512.        begin_system_call();
  9513.        handle = OPEN(TheAsciz(namestring),O_RDWR); # Datei neu ÷ffnen
  9514.        if (handle < 0) { OS_error(); } # Error melden
  9515.        #ifdef MSDOS
  9516.        setmode(handle,O_BINARY);
  9517.        #endif
  9518.        end_system_call();
  9519.        # Nun enthΣlt handle das Handle des ge÷ffneten Files.
  9520.        skipSTACK(1);
  9521.        stream = popSTACK(); # stream zurⁿck
  9522.        # neues Handle eintragen:
  9523.        TheStream(stream)->strm_file_handle = allocate_handle(handle);
  9524.       }
  9525.      #endif
  9526.      #ifdef UNIX
  9527.       #ifdef HAVE_FSYNC
  9528.       begin_system_call();
  9529.       if (!( fsync(TheHandle(TheStream(stream)->strm_file_handle)) ==0))
  9530.         { OS_error(); }
  9531.       end_system_call();
  9532.       #endif
  9533.      #endif
  9534.      #ifdef AMIGAOS
  9535.       #if 0 # Manche Devices vertragen es nicht, wenn man ge÷ffnete Dateien
  9536.             # zu- und wieder aufmacht. Z.B. bei Pipes hat das eine besondere
  9537.             # Bedeutung.
  9538.       begin_system_call();
  9539.       {var reg1 Handle handle = TheHandle(TheStream(stream)->strm_file_handle);
  9540.        if (!IsInteractive(handle))
  9541.          { # File schlie▀en (OS schreibt physikalisch):
  9542.            Close(handle);
  9543.            # File neu ÷ffnen:
  9544.            pushSTACK(stream); # stream retten
  9545.            pushSTACK(TheStream(stream)->strm_file_truename); # Filename
  9546.           {# Directory existiert schon, Datei neu ÷ffnen:
  9547.            var reg2 object namestring = assume_dir_exists(); # Filename als ASCIZ-String
  9548.            handle = Open(TheAsciz(namestring),MODE_OLDFILE);
  9549.            if (handle==NULL) { OS_error(); } # Error melden
  9550.            skipSTACK(1);
  9551.            stream = popSTACK(); # stream zurⁿck
  9552.            # neues Handle eintragen:
  9553.            TheHandle(TheStream(stream)->strm_file_handle) = handle;
  9554.       }  }}
  9555.       end_system_call();
  9556.       #endif
  9557.      #endif
  9558.       # und neu positionieren:
  9559.      {var reg2 uintL position = posfixnum_to_L(TheStream(stream)->strm_file_buffstart)
  9560.                                 + posfixnum_to_L(TheStream(stream)->strm_file_index);
  9561.       TheStream(stream)->strm_file_buffstart = Fixnum_0; # buffstart := 0
  9562.       TheStream(stream)->strm_file_index = Fixnum_0; # index := 0
  9563.       TheStream(stream)->strm_file_eofindex = NIL; # eofindex := NIL
  9564.       position_b_file(stream,position);
  9565.      }# Komponenten position, ..., lastchar bleiben unverΣndert
  9566.     }
  9567.  
  9568. # UP: Bringt den wartenden Output eines File-Stream ans Ziel.
  9569. # Schreibt dazu den Buffer des File-Streams (auch physikalisch) aufs File.
  9570. # force_output_file(stream);
  9571. # > stream : File-Stream.
  9572. # verΣndert in stream: handle, index, eofindex, buffstart, ..., rd_ch_last
  9573. # kann GC ausl÷sen
  9574.   #define force_output_file  finish_output_file
  9575.  
  9576. # UP: ErklΣrt einen File-Stream fⁿr geschlossen.
  9577. # closed_file(stream);
  9578. # > stream : (offener) File-Stream.
  9579. # verΣndert in stream: alle Komponenten au▀er name und truename
  9580.   local void closed_file (object stream);
  9581.   local void closed_file(stream)
  9582.     var reg1 object stream;
  9583.     { TheStream(stream)->strm_file_handle = NIL; # Handle wird ungⁿltig
  9584.       TheStream(stream)->strm_file_buffer = NIL; # Buffer freimachen
  9585.       TheStream(stream)->strm_file_buffstart = NIL; # buffstart l÷schen (unn÷tig)
  9586.       TheStream(stream)->strm_file_eofindex = NIL; # eofindex l÷schen (unn÷tig)
  9587.       TheStream(stream)->strm_file_index = NIL; # index l÷schen (unn÷tig)
  9588.       TheStream(stream)->strm_file_position = NIL; # position l÷schen (unn÷tig)
  9589.       if (TheStream(stream)->strmflags & strmflags_i_B)
  9590.         { TheStream(stream)->strm_file_bitsize = NIL; # bitsize l÷schen (unn÷tig)
  9591.           TheStream(stream)->strm_file_bitbuffer = NIL; # Bitbuffer freimachen
  9592.         }
  9593.     }
  9594.  
  9595. # UP: Schlie▀t einen File-Stream.
  9596. # close_file(stream);
  9597. # > stream : File-Stream.
  9598. # verΣndert in stream: alle Komponenten au▀er name und truename
  9599.   local void close_file (object stream);
  9600.   local void close_file(stream)
  9601.     var reg1 object stream;
  9602.     { # Handle=NIL (Stream bereits geschlossen) -> fertig:
  9603.       if (nullp(TheStream(stream)->strm_file_handle)) { return; }
  9604.       # evtl. Buffer und evtl. eofposition hinausschreiben:
  9605.       file_flush(stream);
  9606.       # Nun ist das modified_flag gel÷scht.
  9607.       # File schlie▀en:
  9608.       #ifdef ATARI
  9609.       { var reg2 sintW ergebnis =
  9610.           GEMDOS_close(TheHandle(TheStream(stream)->strm_file_handle));
  9611.         if (ergebnis<0) # Fehler aufgetreten?
  9612.           # Fehler beim Schlie▀en abfragen, dabei die "weniger schlimmen"
  9613.           # GEMDOS_close_DiskChange (Diskettenwechsel) und
  9614.           # GEMDOS_close_BadHandle (ungⁿltige Handle-Nummer)
  9615.           # vorⁿbergehend ignorieren und verz÷gert ausgeben:
  9616.           { if ((ergebnis==GEMDOS_close_DiskChange)||(ergebnis==GEMDOS_close_BadHandle)) # harmloser Fehler?
  9617.               { closed_file(stream); close_dummys(stream); }
  9618.             OS_error(ergebnis);
  9619.       }   }
  9620.       #endif
  9621.       #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS)
  9622.       begin_system_call();
  9623.       if (!( CLOSE(TheHandle(TheStream(stream)->strm_file_handle)) ==0))
  9624.         { OS_error(); }
  9625.       end_system_call();
  9626.       #endif
  9627.       #ifdef AMIGAOS
  9628.       begin_system_call();
  9629.       Close(TheHandle(TheStream(stream)->strm_file_handle));
  9630.       end_system_call();
  9631.       #endif
  9632.       # Komponenten ungⁿltig machen (close_dummys kommt spΣter):
  9633.       closed_file(stream);
  9634.       # stream aus der Liste aller offenen File-Streams streichen:
  9635.       O(open_files) = deleteq(O(open_files),stream);
  9636.     }
  9637.  
  9638.  
  9639. # Synonym-Stream
  9640. # ==============
  9641.  
  9642. # ZusΣtzliche Komponenten:
  9643.   # define strm_synonym_symbol  strm_other[0]  # Symbol, auf dessen Wert verwiesen wird
  9644.  
  9645. # Macro: Liefert den Wert eines Symbols, ein Stream.
  9646. # get_synonym_stream(sym)
  9647. # > sym: Symbol
  9648. # < ergebnis: sein Wert, ein Stream
  9649.   #define get_synonym_stream(sym)  \
  9650.     ((!mstreamp(Symbol_value(sym))) ?         \
  9651.        (fehler_value_stream(sym), unbound) :  \
  9652.        Symbol_value(sym)                      \
  9653.     )
  9654.  
  9655. # READ-BYTE - Pseudofunktion fⁿr Synonym-Streams:
  9656.   local object rd_by_synonym (object stream);
  9657.   local object rd_by_synonym(stream)
  9658.     var reg2 object stream;
  9659.     { check_SP();
  9660.      {var reg1 object symbol = TheStream(stream)->strm_synonym_symbol;
  9661.       return read_byte(get_synonym_stream(symbol));
  9662.     }}
  9663.  
  9664. # WRITE-BYTE - Pseudofunktion fⁿr Synonym-Streams:
  9665.   local void wr_by_synonym (object stream, object obj);
  9666.   local void wr_by_synonym(stream,obj)
  9667.     var reg2 object stream;
  9668.     var reg3 object obj;
  9669.     { check_SP();
  9670.      {var reg1 object symbol = TheStream(stream)->strm_synonym_symbol;
  9671.       write_byte(get_synonym_stream(symbol),obj);
  9672.     }}
  9673.  
  9674. # READ-CHAR - Pseudofunktion fⁿr Synonym-Streams:
  9675.   local object rd_ch_synonym (object* stream_);
  9676.   local object rd_ch_synonym(stream_)
  9677.     var reg3 object* stream_;
  9678.     {  check_SP(); check_STACK();
  9679.      { var reg2 object stream = *stream_;
  9680.        var reg1 object symbol = TheStream(stream)->strm_synonym_symbol;
  9681.        pushSTACK(get_synonym_stream(symbol));
  9682.       {var reg1 object ergebnis = read_char(&STACK_0);
  9683.        skipSTACK(1);
  9684.        return ergebnis;
  9685.     }}}
  9686.  
  9687. # WRITE-CHAR - Pseudofunktion fⁿr Synonym-Streams:
  9688.   local void wr_ch_synonym (object* stream_, object obj);
  9689.   local void wr_ch_synonym(stream_,obj)
  9690.     var reg3 object* stream_;
  9691.     var reg4 object obj;
  9692.     { check_SP(); check_STACK();
  9693.      {var reg2 object stream = *stream_;
  9694.       var reg1 object symbol = TheStream(stream)->strm_synonym_symbol;
  9695.       pushSTACK(get_synonym_stream(symbol));
  9696.       write_char(&STACK_0,obj);
  9697.       skipSTACK(1);
  9698.     }}
  9699.  
  9700. #ifdef STRM_WR_SS
  9701. # WRITE-SIMPLE-STRING - Pseudofunktion fⁿr Synonym-Streams:
  9702.   local void wr_ss_synonym (object* stream_, object string, uintL start, uintL len);
  9703.   local void wr_ss_synonym(stream_,string,start,len)
  9704.     var reg1 object* stream_;
  9705.     var reg3 object string;
  9706.     var reg4 uintL start;
  9707.     var reg5 uintL len;
  9708.     { check_SP(); check_STACK();
  9709.      {var reg2 object symbol = TheStream(*stream_)->strm_synonym_symbol;
  9710.       pushSTACK(get_synonym_stream(symbol));
  9711.       wr_ss(STACK_0)(&STACK_0,string,start,len);
  9712.       skipSTACK(1);
  9713.       # Line-Position aktualisieren kann hier entfallen.
  9714.     }}
  9715. #endif
  9716.  
  9717. # Schlie▀t einen Synonym-Stream.
  9718. # close_synonym(stream);
  9719. # > stream : Synonym-Stream
  9720.   local void close_synonym (object stream);
  9721.   local void close_synonym(stream)
  9722.     var reg2 object stream;
  9723.     { check_SP(); check_STACK();
  9724.      {var reg1 object symbol = TheStream(stream)->strm_synonym_symbol;
  9725.       pushSTACK(get_synonym_stream(symbol));
  9726.       stream_close(&STACK_0);
  9727.       skipSTACK(1);
  9728.     }}
  9729.  
  9730. # Stellt fest, ob ein Synonym-Stream ein Zeichen verfⁿgbar hat.
  9731. # listen_synonym(stream)
  9732. # > stream : Synonym-Stream
  9733. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  9734. #             -1 falls bei EOF angelangt,
  9735. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  9736. # kann GC ausl÷sen
  9737.   local signean listen_synonym (object stream);
  9738.   local signean listen_synonym(stream)
  9739.     var reg2 object stream;
  9740.     { check_SP();
  9741.      {var reg1 object symbol = TheStream(stream)->strm_synonym_symbol;
  9742.       return stream_listen(get_synonym_stream(symbol));
  9743.     }}
  9744.  
  9745. # UP: L÷scht bereits eingegebenen interaktiven Input von einem Synonym-Stream.
  9746. # clear_input_synonym(stream)
  9747. # > stream: Synonym-Stream
  9748. # < ergebnis: TRUE falls Input gel÷scht wurde
  9749. # kann GC ausl÷sen
  9750.   local boolean clear_input_synonym (object stream);
  9751.   local boolean clear_input_synonym(stream)
  9752.     var reg2 object stream;
  9753.     { check_SP();
  9754.      {var reg1 object symbol = TheStream(stream)->strm_synonym_symbol;
  9755.       return clear_input(get_synonym_stream(symbol));
  9756.     }}
  9757.  
  9758. # UP: Wartenden Output eines Synonym-Stream ans Ziel bringen.
  9759. # finish_output_synonym(stream);
  9760. # > stream: Synonym-Stream
  9761. # kann GC ausl÷sen
  9762.   local void finish_output_synonym (object stream);
  9763.   local void finish_output_synonym(stream)
  9764.     var reg2 object stream;
  9765.     { check_SP();
  9766.      {var reg1 object symbol = TheStream(stream)->strm_synonym_symbol;
  9767.       finish_output(get_synonym_stream(symbol));
  9768.     }}
  9769.  
  9770. # UP: Wartenden Output eines Synonym-Stream ans Ziel bringen.
  9771. # force_output_synonym(stream);
  9772. # > stream: Synonym-Stream
  9773. # kann GC ausl÷sen
  9774.   local void force_output_synonym (object stream);
  9775.   local void force_output_synonym(stream)
  9776.     var reg2 object stream;
  9777.     { check_SP();
  9778.      {var reg1 object symbol = TheStream(stream)->strm_synonym_symbol;
  9779.       force_output(get_synonym_stream(symbol));
  9780.     }}
  9781.  
  9782. # UP: L÷scht den wartenden Output eines Synonym-Stream.
  9783. # clear_output_synonym(stream);
  9784. # > stream: Synonym-Stream
  9785. # kann GC ausl÷sen
  9786.   local void clear_output_synonym (object stream);
  9787.   local void clear_output_synonym(stream)
  9788.     var reg2 object stream;
  9789.     { check_SP();
  9790.      {var reg1 object symbol = TheStream(stream)->strm_synonym_symbol;
  9791.       clear_output(get_synonym_stream(symbol));
  9792.     }}
  9793.  
  9794. # Liefert einen Synonym-Stream zu einem Symbol.
  9795. # make_synonym_stream(symbol)
  9796. # > symbol : Symbol
  9797. # < ergebnis : neuer Synonym-Stream
  9798. # kann GC ausl÷sen
  9799.   local object make_synonym_stream (object symbol);
  9800.   local object make_synonym_stream(symbol)
  9801.     var reg2 object symbol;
  9802.     { pushSTACK(symbol); # Symbol retten
  9803.      {var reg1 object stream = # neuer Stream, alle Operationen erlaubt
  9804.         allocate_stream(strmflags_open_B,strmtype_synonym,strm_len+1);
  9805.       TheStream(stream)->strm_rd_by = P(rd_by_synonym);
  9806.       TheStream(stream)->strm_wr_by = P(wr_by_synonym);
  9807.       TheStream(stream)->strm_rd_ch = P(rd_ch_synonym);
  9808.       TheStream(stream)->strm_rd_ch_last = NIL;
  9809.       TheStream(stream)->strm_wr_ch = P(wr_ch_synonym);
  9810.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  9811.       #ifdef STRM_WR_SS
  9812.       TheStream(stream)->strm_wr_ss = P(wr_ss_synonym);
  9813.       #endif
  9814.       TheStream(stream)->strm_synonym_symbol = popSTACK();
  9815.       return stream;
  9816.     }}
  9817.  
  9818. LISPFUNN(make_synonym_stream,1)
  9819. # (MAKE-SYNONYM-STREAM symbol), CLTL S. 329
  9820.   { var reg1 object arg = popSTACK();
  9821.     if (!symbolp(arg))
  9822.       { pushSTACK(arg); # Wert fⁿr Slot DATUM von TYPE-ERROR
  9823.         pushSTACK(S(symbol)); # Wert fⁿr Slot EXPECTED-TYPE von TYPE-ERROR
  9824.         pushSTACK(arg); pushSTACK(TheSubr(subr_self)->name);
  9825.         fehler(type_error,
  9826.                DEUTSCH ? "~: Argument mu▀ ein Symbol sein, nicht ~" :
  9827.                ENGLISH ? "~: argument should be a symbol, not ~" :
  9828.                FRANCAIS ? "~ : L'argument doit Ωtre un symbole et non ~":
  9829.                ""
  9830.               );
  9831.       }
  9832.     value1 = make_synonym_stream(arg); mv_count=1;
  9833.   }
  9834.  
  9835.  
  9836. # Broadcast-Stream
  9837. # ================
  9838.  
  9839. # ZusΣtzliche Komponenten:
  9840.   # define strm_broad_list  strm_other[0] # Liste von Streams
  9841.  
  9842. # WRITE-BYTE - Pseudofunktion fⁿr Broadcast-Streams:
  9843.   local void wr_by_broad (object stream, object obj);
  9844.   local void wr_by_broad(stream,obj)
  9845.     var reg2 object stream;
  9846.     var reg3 object obj;
  9847.     { check_SP(); check_STACK();
  9848.       pushSTACK(obj);
  9849.       { var reg1 object streamlist = TheStream(stream)->strm_broad_list; # Liste von Streams
  9850.         # obj auf jeden Stream aus der Liste ausgeben:
  9851.         while (consp(streamlist))
  9852.           { pushSTACK(Cdr(streamlist)); # restliche Streams
  9853.             write_byte(Car(streamlist),STACK_1); # obj ausgeben
  9854.             streamlist = popSTACK();
  9855.       }   }
  9856.       skipSTACK(1);
  9857.     }
  9858.  
  9859. # WRITE-CHAR - Pseudofunktion fⁿr Broadcast-Streams:
  9860.   local void wr_ch_broad (object* stream_, object obj);
  9861.   local void wr_ch_broad(stream_,obj)
  9862.     var reg3 object* stream_;
  9863.     var reg4 object obj;
  9864.     { check_SP(); check_STACK();
  9865.       pushSTACK(obj);
  9866.       pushSTACK(NIL); # dummy
  9867.       pushSTACK(TheStream(*stream_)->strm_broad_list); # Liste von Streams
  9868.       # obj auf jeden Stream aus der Liste ausgeben:
  9869.       while (mconsp(STACK_0))
  9870.         { # Stackaufbau: obj, dummy, streamlistr.
  9871.           STACK_1 = Car(STACK_0); # ein Stream aus der Liste
  9872.           write_char(&STACK_1,STACK_2); # obj ausgeben
  9873.           STACK_0 = Cdr(STACK_0);
  9874.         }
  9875.       skipSTACK(3);
  9876.     }
  9877.  
  9878. #ifdef STRM_WR_SS
  9879. # WRITE-CHAR - Pseudofunktion fⁿr Broadcast-Streams:
  9880.   local void wr_ss_broad (object* stream_, object string, uintL start, uintL len);
  9881.   local void wr_ss_broad(stream_,string,start,len)
  9882.     var reg1 object* stream_;
  9883.     var reg4 object string;
  9884.     var reg2 uintL start;
  9885.     var reg3 uintL len;
  9886.     { check_SP(); check_STACK();
  9887.       pushSTACK(string);
  9888.       pushSTACK(NIL); # dummy
  9889.       pushSTACK(TheStream(*stream_)->strm_broad_list); # Liste von Streams
  9890.       # string auf jeden Stream aus der Liste ausgeben:
  9891.       while (mconsp(STACK_0))
  9892.         { # Stackaufbau: string, dummy, streamlistr.
  9893.           STACK_1 = Car(STACK_0); # ein Stream aus der Liste
  9894.           wr_ss(STACK_1)(&STACK_1,STACK_2,start,len); # string-Stⁿck ausgeben
  9895.           STACK_0 = Cdr(STACK_0);
  9896.         }
  9897.       skipSTACK(3);
  9898.       # Line-Position aktualisieren kann hier entfallen.
  9899.     }
  9900. #endif
  9901.  
  9902. # UP: Bringt den wartenden Output eines Broadcast-Stream ans Ziel.
  9903. # finish_output_broad(stream);
  9904. # > stream: Broadcast-Stream
  9905. # kann GC ausl÷sen
  9906.   local void finish_output_broad (object stream);
  9907.   local void finish_output_broad(stream)
  9908.     var reg2 object stream;
  9909.     { check_SP(); check_STACK();
  9910.       { var reg1 object streamlist = TheStream(stream)->strm_broad_list; # Liste von Streams
  9911.         # Jeden Stream aus der Liste einzeln behandeln:
  9912.         while (consp(streamlist))
  9913.           { pushSTACK(Cdr(streamlist)); # restliche Streams
  9914.             finish_output(Car(streamlist));
  9915.             streamlist = popSTACK();
  9916.       }   }
  9917.     }
  9918.  
  9919. # UP: Bringt den wartenden Output eines Broadcast-Stream ans Ziel.
  9920. # force_output_broad(stream);
  9921. # > stream: Broadcast-Stream
  9922. # kann GC ausl÷sen
  9923.   local void force_output_broad (object stream);
  9924.   local void force_output_broad(stream)
  9925.     var reg2 object stream;
  9926.     { check_SP(); check_STACK();
  9927.       { var reg1 object streamlist = TheStream(stream)->strm_broad_list; # Liste von Streams
  9928.         # Jeden Stream aus der Liste einzeln behandeln:
  9929.         while (consp(streamlist))
  9930.           { pushSTACK(Cdr(streamlist)); # restliche Streams
  9931.             force_output(Car(streamlist));
  9932.             streamlist = popSTACK();
  9933.       }   }
  9934.     }
  9935.  
  9936. # UP: L÷scht den wartenden Output eines Broadcast-Stream.
  9937. # clear_output_broad(stream);
  9938. # > stream: Broadcast-Stream
  9939. # kann GC ausl÷sen
  9940.   local void clear_output_broad (object stream);
  9941.   local void clear_output_broad(stream)
  9942.     var reg2 object stream;
  9943.     { check_SP(); check_STACK();
  9944.       { var reg1 object streamlist = TheStream(stream)->strm_broad_list; # Liste von Streams
  9945.         # Jeden Stream aus der Liste einzeln behandeln:
  9946.         while (consp(streamlist))
  9947.           { pushSTACK(Cdr(streamlist)); # restliche Streams
  9948.             clear_output(Car(streamlist));
  9949.             streamlist = popSTACK();
  9950.       }   }
  9951.     }
  9952.  
  9953. # Liefert einen Broadcast-Stream zu einer Streamliste.
  9954. # make_broadcast_stream(list)
  9955. # > list : Liste von Streams
  9956. # < ergebnis : Broadcast-Stream
  9957. # Die Liste list wird dabei zerst÷rt.
  9958. # kann GC ausl÷sen
  9959.   local object make_broadcast_stream (object list);
  9960.   local object make_broadcast_stream(list)
  9961.     var reg2 object list;
  9962.     { pushSTACK(list); # list retten
  9963.      {var reg1 object stream = # neuer Stream, nur WRITEs erlaubt
  9964.         allocate_stream(strmflags_wr_B,strmtype_broad,strm_len+1);
  9965.       TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  9966.       TheStream(stream)->strm_wr_by = P(wr_by_broad);
  9967.       TheStream(stream)->strm_rd_ch = P(rd_ch_dummy);
  9968.       TheStream(stream)->strm_rd_ch_last = NIL;
  9969.       TheStream(stream)->strm_wr_ch = P(wr_ch_broad);
  9970.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  9971.       #ifdef STRM_WR_SS
  9972.       TheStream(stream)->strm_wr_ss = P(wr_ss_broad);
  9973.       #endif
  9974.       TheStream(stream)->strm_broad_list = popSTACK();
  9975.       return stream;
  9976.     }}
  9977.  
  9978. # Liefert einen Broadcast-Stream zum Stream stream.
  9979. # make_broadcast1_stream(stream)
  9980. # > stream : Stream
  9981. # < ergebnis : Broadcast-Stream
  9982. # kann GC ausl÷sen
  9983.   global object make_broadcast1_stream (object stream);
  9984.   global object make_broadcast1_stream(oldstream)
  9985.     var reg3 object oldstream;
  9986.     {  pushSTACK(oldstream);
  9987.        # oldstream in eine einelementige Liste packen:
  9988.      { var reg2 object new_cons = allocate_cons();
  9989.        Car(new_cons) = STACK_0;
  9990.       {var reg1 object stream = make_broadcast_stream(new_cons); # neuer Stream
  9991.        oldstream = popSTACK();
  9992.        # Line-Position ⁿbernehmen:
  9993.        TheStream(stream)->strm_wr_ch_lpos = TheStream(oldstream)->strm_wr_ch_lpos;
  9994.        return stream;
  9995.     }}}
  9996.  
  9997. LISPFUN(make_broadcast_stream,0,0,rest,nokey,0,NIL)
  9998. # (MAKE-BROADCAST-STREAM {stream}), CLTL S. 329
  9999.   { # ▄berprⁿfen, ob alle Argumente Streams sind:
  10000.     test_stream_args(rest_args_pointer,argcount);
  10001.     # zu einer Liste zusammenfassen:
  10002.    {var reg1 object list = listof(argcount);
  10003.     # Stream bauen:
  10004.     value1 = make_broadcast_stream(list); mv_count=1;
  10005.   }}
  10006.  
  10007.  
  10008. # Concatenated-Stream
  10009. # ===================
  10010.  
  10011. # ZusΣtzliche Komponenten:
  10012.   # define strm_concat_list   strm_other[0]  # Liste von Streams
  10013.   #define strm_concat_list2  strm_other[1]  # Liste der verbrauchten Streams
  10014.  
  10015. # READ-BYTE - Pseudofunktion fⁿr Concatenated-Streams:
  10016.   local object rd_by_concat (object stream);
  10017.   local object rd_by_concat(stream)
  10018.     var reg3 object stream;
  10019.     { check_SP(); check_STACK();
  10020.       pushSTACK(stream);
  10021.      {var reg1 object streamlist = TheStream(stream)->strm_concat_list; # Liste von Streams
  10022.       var reg2 object ergebnis;
  10023.       while (consp(streamlist))
  10024.         { ergebnis = read_byte(Car(streamlist)); # Integer lesen
  10025.           if (!eq(ergebnis,eof_value)) { goto OK; } # nicht EOF ?
  10026.           # EOF erreicht -> verbrauchten Stream aus der Liste nehmen
  10027.           # und in die zweite Liste stecken:
  10028.           stream = STACK_0;
  10029.          {var reg4 object first_cons = TheStream(stream)->strm_concat_list;
  10030.           streamlist = Cdr(first_cons);
  10031.           Cdr(first_cons) = TheStream(stream)->strm_concat_list2;
  10032.           TheStream(stream)->strm_concat_list2 = first_cons;
  10033.           TheStream(stream)->strm_concat_list = streamlist;
  10034.         }}
  10035.       # alle Streams verbraucht -> liefere EOF:
  10036.       ergebnis = eof_value;
  10037.       OK: # ergebnis fertig
  10038.       skipSTACK(1);
  10039.       return ergebnis;
  10040.     }}
  10041.  
  10042. # READ-CHAR - Pseudofunktion fⁿr Concatenated-Streams:
  10043.   local object rd_ch_concat (object* stream_);
  10044.   local object rd_ch_concat(stream_)
  10045.     var reg3 object* stream_;
  10046.     { check_SP(); check_STACK();
  10047.      {var reg1 object streamlist = TheStream(*stream_)->strm_concat_list; # Liste von Streams
  10048.       while (consp(streamlist))
  10049.         { pushSTACK(Car(streamlist));
  10050.          {var reg2 object ergebnis = read_char(&STACK_0); # Character lesen
  10051.           skipSTACK(1);
  10052.           if (!eq(ergebnis,eof_value)) { return ergebnis; }
  10053.           # EOF erreicht -> verbrauchten Stream aus der Liste nehmen
  10054.           # und in die zweite Liste stecken:
  10055.           {var reg2 object stream = *stream_;
  10056.            var reg4 object first_cons = TheStream(stream)->strm_concat_list;
  10057.            streamlist = Cdr(first_cons);
  10058.            Cdr(first_cons) = TheStream(stream)->strm_concat_list2;
  10059.            TheStream(stream)->strm_concat_list2 = first_cons;
  10060.            TheStream(stream)->strm_concat_list = streamlist;
  10061.         }}}
  10062.       # alle Streams verbraucht -> liefere EOF:
  10063.       return eof_value;
  10064.     }}
  10065.  
  10066. # Stellt fest, ob ein Concatenated-Stream ein Zeichen verfⁿgbar hat.
  10067. # listen_concat(stream)
  10068. # > stream : Concatenated-Stream
  10069. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  10070. #             -1 falls bei EOF angelangt,
  10071. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  10072. # kann GC ausl÷sen
  10073.   local signean listen_concat (object stream);
  10074.   local signean listen_concat(stream)
  10075.     var reg3 object stream;
  10076.     { pushSTACK(stream);
  10077.      {var reg1 object streamlist = TheStream(stream)->strm_concat_list; # Liste von Streams
  10078.       var reg2 signean ergebnis;
  10079.       while (consp(streamlist))
  10080.         { ergebnis = stream_listen(Car(streamlist));
  10081.           if (ergebnis>=0) { goto OK; } # nicht EOF ?
  10082.           # EOF erreicht -> verbrauchten Stream aus der Liste nehmen
  10083.           # und in die zweite Liste stecken:
  10084.           stream = STACK_0;
  10085.          {var reg4 object first_cons = TheStream(stream)->strm_concat_list;
  10086.           streamlist = Cdr(first_cons);
  10087.           Cdr(first_cons) = TheStream(stream)->strm_concat_list2;
  10088.           TheStream(stream)->strm_concat_list2 = first_cons;
  10089.           TheStream(stream)->strm_concat_list = streamlist;
  10090.         }}
  10091.       # alle Streams verbraucht -> liefere EOF:
  10092.       ergebnis = signean_minus;
  10093.       OK: # ergebnis fertig
  10094.       skipSTACK(1);
  10095.       return ergebnis;
  10096.     }}
  10097.  
  10098. # UP: L÷scht bereits eingegebenen interaktiven Input von einem
  10099. # Concatenated-Stream.
  10100. # clear_input_concat(stream)
  10101. # > stream: Concatenated-Stream
  10102. # < ergebnis: TRUE falls Input gel÷scht wurde
  10103. # kann GC ausl÷sen
  10104.   local boolean clear_input_concat (object stream);
  10105.   local boolean clear_input_concat(stream)
  10106.     var reg3 object stream;
  10107.     { var reg2 boolean ergebnis = FALSE; # noch kein Input gel÷scht
  10108.       # alle Streams einzeln behandeln:
  10109.       var reg1 object streamlist = TheStream(stream)->strm_concat_list; # Liste von Streams
  10110.       while (consp(streamlist))
  10111.         { pushSTACK(Cdr(streamlist)); # restliche Streamliste
  10112.           ergebnis |= clear_input(Car(streamlist)); # allen Input des Teilstreams l÷schen
  10113.           streamlist = popSTACK();
  10114.         }
  10115.       return ergebnis;
  10116.     }
  10117.  
  10118. # Liefert einen Concatenated-Stream zu einer Streamliste.
  10119. # make_concatenated_stream(list)
  10120. # > list : Liste von Streams
  10121. # < ergebnis : Concatenated-Stream
  10122. # Die Liste list wird dabei zerst÷rt.
  10123. # kann GC ausl÷sen
  10124.   local object make_concatenated_stream (object list);
  10125.   local object make_concatenated_stream(list)
  10126.     var reg2 object list;
  10127.     { pushSTACK(list); # list retten
  10128.      {var reg1 object stream = # neuer Stream, nur READs erlaubt
  10129.         allocate_stream(strmflags_rd_B,strmtype_concat,strm_len+2);
  10130.       TheStream(stream)->strm_rd_by = P(rd_by_concat);
  10131.       TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  10132.       TheStream(stream)->strm_rd_ch = P(rd_ch_concat);
  10133.       TheStream(stream)->strm_rd_ch_last = NIL;
  10134.       TheStream(stream)->strm_wr_ch = P(wr_ch_dummy);
  10135.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  10136.       #ifdef STRM_WR_SS
  10137.       TheStream(stream)->strm_wr_ss = P(wr_ss_dummy);
  10138.       #endif
  10139.       TheStream(stream)->strm_concat_list = popSTACK();
  10140.       TheStream(stream)->strm_concat_list2 = NIL;
  10141.       return stream;
  10142.     }}
  10143.  
  10144. LISPFUN(make_concatenated_stream,0,0,rest,nokey,0,NIL)
  10145. # (MAKE-CONCATENATED-STREAM {stream}), CLTL S. 329
  10146.   { # ▄berprⁿfen, ob alle Argumente Streams sind:
  10147.     test_stream_args(rest_args_pointer,argcount);
  10148.     # zu einer Liste zusammenfassen:
  10149.    {var reg1 object list = listof(argcount);
  10150.     # Stream bauen:
  10151.     value1 = make_concatenated_stream(list); mv_count=1;
  10152.   }}
  10153.  
  10154.  
  10155. # Two-Way-Stream, Echo-Stream
  10156. # ===========================
  10157.  
  10158. # ZusΣtzliche Komponenten:
  10159.   #define strm_twoway_input   strm_other[0]  # Stream fⁿr Input
  10160.   #define strm_twoway_output  strm_other[1]  # Stream fⁿr Output
  10161.  
  10162. # WRITE-BYTE - Pseudofunktion fⁿr Two-Way- und Echo-Streams:
  10163.   local void wr_by_twoway (object stream, object obj);
  10164.   local void wr_by_twoway(stream,obj)
  10165.     var reg1 object stream;
  10166.     var reg2 object obj;
  10167.     { check_SP();
  10168.       write_byte(TheStream(stream)->strm_twoway_output,obj);
  10169.     }
  10170.  
  10171. # WRITE-CHAR - Pseudofunktion fⁿr Two-Way- und Echo-Streams:
  10172.   local void wr_ch_twoway (object* stream_, object obj);
  10173.   local void wr_ch_twoway(stream_,obj)
  10174.     var reg1 object* stream_;
  10175.     var reg2 object obj;
  10176.     { check_SP(); check_STACK();
  10177.       pushSTACK(TheStream(*stream_)->strm_twoway_output);
  10178.       write_char(&STACK_0,obj);
  10179.       skipSTACK(1);
  10180.     }
  10181.  
  10182. #ifdef STRM_WR_SS
  10183. # WRITE-SIMPLE-STRING - Pseudofunktion fⁿr Two-Way- und Echo-Streams:
  10184.   local void wr_ss_twoway (object* stream_, object string, uintL start, uintL len);
  10185.   local void wr_ss_twoway(stream_,string,start,len)
  10186.     var reg1 object* stream_;
  10187.     var reg2 object string;
  10188.     var reg3 uintL start;
  10189.     var reg4 uintL len;
  10190.     { check_SP(); check_STACK();
  10191.       pushSTACK(TheStream(*stream_)->strm_twoway_output);
  10192.       wr_ss(STACK_0)(&STACK_0,string,start,len);
  10193.       skipSTACK(1);
  10194.       # Line-Position aktualisieren kann hier entfallen.
  10195.     }
  10196. #endif
  10197.  
  10198. # Stellt fest, ob ein Two-Way- oder Echo-Stream ein Zeichen verfⁿgbar hat.
  10199. # listen_twoway(stream)
  10200. # > stream : Two-Way- oder Echo-Stream
  10201. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  10202. #             -1 falls bei EOF angelangt,
  10203. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  10204. # kann GC ausl÷sen
  10205.   local signean listen_twoway (object stream);
  10206.   local signean listen_twoway(stream)
  10207.     var reg1 object stream;
  10208.     { check_SP();
  10209.       return stream_listen(TheStream(stream)->strm_twoway_input);
  10210.     }
  10211.  
  10212. # UP: L÷scht bereits eingegebenen interaktiven Input von einem Two-Way-
  10213. # oder Echo-Stream.
  10214. # clear_input_twoway(stream)
  10215. # > stream: Two-Way- oder Echo-Stream
  10216. # < ergebnis: TRUE falls Input gel÷scht wurde
  10217. # kann GC ausl÷sen
  10218.   local boolean clear_input_twoway (object stream);
  10219.   local boolean clear_input_twoway(stream)
  10220.     var reg1 object stream;
  10221.     { check_SP();
  10222.       return clear_input(TheStream(stream)->strm_twoway_input);
  10223.     }
  10224.  
  10225. # UP: Bringt den wartenden Output eines Two-Way- oder Echo-Stream ans Ziel.
  10226. # finish_output_twoway(stream);
  10227. # > stream: Two-Way- oder Echo-Stream
  10228. # kann GC ausl÷sen
  10229.   local void finish_output_twoway (object stream);
  10230.   local void finish_output_twoway(stream)
  10231.     var reg1 object stream;
  10232.     { check_SP();
  10233.       finish_output(TheStream(stream)->strm_twoway_output);
  10234.     }
  10235.  
  10236. # UP: Bringt den wartenden Output eines Two-Way- oder Echo-Stream ans Ziel.
  10237. # force_output_twoway(stream);
  10238. # > stream: Two-Way- oder Echo-Stream
  10239. # kann GC ausl÷sen
  10240.   local void force_output_twoway (object stream);
  10241.   local void force_output_twoway(stream)
  10242.     var reg1 object stream;
  10243.     { check_SP();
  10244.       force_output(TheStream(stream)->strm_twoway_output);
  10245.     }
  10246.  
  10247. # UP: L÷scht den wartenden Output eines Two-Way- oder Echo-Stream.
  10248. # clear_output_twoway(stream);
  10249. # > stream: Two-Way- oder Echo-Stream
  10250. # kann GC ausl÷sen
  10251.   local void clear_output_twoway (object stream);
  10252.   local void clear_output_twoway(stream)
  10253.     var reg1 object stream;
  10254.     { check_SP();
  10255.       clear_output(TheStream(stream)->strm_twoway_output);
  10256.     }
  10257.  
  10258.  
  10259. # Two-Way-Stream
  10260. # ==============
  10261.  
  10262. # READ-BYTE - Pseudofunktion fⁿr Two-Way-Streams:
  10263.   local object rd_by_twoway (object stream);
  10264.   local object rd_by_twoway(stream)
  10265.     var reg1 object stream;
  10266.     { check_SP();
  10267.       return read_byte(TheStream(stream)->strm_twoway_input);
  10268.     }
  10269.  
  10270. # READ-CHAR - Pseudofunktion fⁿr Two-Way-Streams:
  10271.   local object rd_ch_twoway (object* stream_);
  10272.   local object rd_ch_twoway(stream_)
  10273.     var reg1 object* stream_;
  10274.     { check_SP(); check_STACK();
  10275.       pushSTACK(TheStream(*stream_)->strm_twoway_input);
  10276.      {var reg2 object ergebnis = read_char(&STACK_0);
  10277.       skipSTACK(1);
  10278.       return ergebnis;
  10279.     }}
  10280.  
  10281. # Liefert einen Two-Way-Stream zu einem Input-Stream und einem Output-Stream.
  10282. # make_twoway_stream(input_stream,output_stream)
  10283. # > input_stream : Input-Stream
  10284. # > output_stream : Output-Stream
  10285. # < ergebnis : Two-Way-Stream
  10286. # kann GC ausl÷sen
  10287.   global object make_twoway_stream (object input_stream, object output_stream);
  10288.   global object make_twoway_stream(input_stream,output_stream)
  10289.     var reg2 object input_stream;
  10290.     var reg2 object output_stream;
  10291.     { pushSTACK(input_stream); pushSTACK(output_stream); # Streams retten
  10292.      {var reg1 object stream = # neuer Stream, alle Operationen erlaubt
  10293.         allocate_stream(strmflags_open_B,strmtype_twoway,strm_len+2);
  10294.       TheStream(stream)->strm_rd_by = P(rd_by_twoway);
  10295.       TheStream(stream)->strm_wr_by = P(wr_by_twoway);
  10296.       TheStream(stream)->strm_rd_ch = P(rd_ch_twoway);
  10297.       TheStream(stream)->strm_rd_ch_last = NIL;
  10298.       TheStream(stream)->strm_wr_ch = P(wr_ch_twoway);
  10299.       output_stream = popSTACK(); input_stream = popSTACK(); # Streams zurⁿck
  10300.       TheStream(stream)->strm_wr_ch_lpos = TheStream(output_stream)->strm_wr_ch_lpos;
  10301.       #ifdef STRM_WR_SS
  10302.       TheStream(stream)->strm_wr_ss = P(wr_ss_twoway);
  10303.       #endif
  10304.       TheStream(stream)->strm_twoway_input = input_stream;
  10305.       TheStream(stream)->strm_twoway_output = output_stream;
  10306.       return stream;
  10307.     }}
  10308.  
  10309. LISPFUNN(make_two_way_stream,2)
  10310. # (MAKE-TWO-WAY-STREAM input-stream output-stream), CLTL S. 329
  10311.   { # ▄berprⁿfen, ob beides Streams sind:
  10312.     test_stream_args(args_end_pointer STACKop 2, 2);
  10313.    {var reg2 object output_stream = popSTACK();
  10314.     var reg1 object input_stream = popSTACK();
  10315.     # Stream bauen:
  10316.     value1 = make_twoway_stream(input_stream,output_stream); mv_count=1;
  10317.   }}
  10318.  
  10319.  
  10320. # Echo-Stream
  10321. # ===========
  10322.  
  10323. # READ-BYTE - Pseudofunktion fⁿr Echo-Streams:
  10324.   local object rd_by_echo (object stream);
  10325.   local object rd_by_echo(stream)
  10326.     var reg1 object stream;
  10327.     { check_SP(); check_STACK();
  10328.       pushSTACK(stream);
  10329.      {var reg1 object obj = read_byte(TheStream(stream)->strm_twoway_input);
  10330.       stream = popSTACK();
  10331.       if (!eq(obj,eof_value))
  10332.         { pushSTACK(obj);
  10333.           write_byte(TheStream(stream)->strm_twoway_output,obj);
  10334.           obj = popSTACK();
  10335.         }
  10336.       return obj;
  10337.     }}
  10338.  
  10339. # READ-CHAR - Pseudofunktion fⁿr Echo-Streams:
  10340.   local object rd_ch_echo (object* stream_);
  10341.   local object rd_ch_echo(stream_)
  10342.     var reg1 object* stream_;
  10343.     { check_SP(); check_STACK();
  10344.       pushSTACK(TheStream(*stream_)->strm_twoway_input);
  10345.      {var reg2 object obj = read_char(&STACK_0);
  10346.       if (!eq(obj,eof_value))
  10347.         { STACK_0 = TheStream(*stream_)->strm_twoway_output;
  10348.           pushSTACK(obj);
  10349.           write_char(&STACK_1,obj);
  10350.           obj = popSTACK();
  10351.         }
  10352.       skipSTACK(1);
  10353.       return obj;
  10354.     }}
  10355.  
  10356. # Liefert einen Echo-Stream zu einem Input-Stream und einem Output-Stream.
  10357. # make_echo_stream(input_stream,output_stream)
  10358. # > input_stream : Input-Stream
  10359. # > output_stream : Output-Stream
  10360. # < ergebnis : Echo-Stream
  10361. # kann GC ausl÷sen
  10362.   local object make_echo_stream (object input_stream, object output_stream);
  10363.   local object make_echo_stream(input_stream,output_stream)
  10364.     var reg3 object input_stream;
  10365.     var reg2 object output_stream;
  10366.     { pushSTACK(input_stream); pushSTACK(output_stream); # Streams retten
  10367.      {var reg4 uintB flags = strmflags_open_B
  10368.         #ifdef IMMUTABLE
  10369.         | (TheStream(input_stream)->strmflags & strmflags_immut_B)
  10370.         #endif
  10371.         ;
  10372.       var reg1 object stream = # neuer Stream, alle Operationen erlaubt
  10373.         allocate_stream(flags,strmtype_echo,strm_len+2);
  10374.       TheStream(stream)->strm_rd_by = P(rd_by_echo);
  10375.       TheStream(stream)->strm_wr_by = P(wr_by_twoway);
  10376.       TheStream(stream)->strm_rd_ch = P(rd_ch_echo);
  10377.       TheStream(stream)->strm_rd_ch_last = NIL;
  10378.       TheStream(stream)->strm_wr_ch = P(wr_ch_twoway);
  10379.       output_stream = popSTACK(); input_stream = popSTACK(); # Streams zurⁿck
  10380.       TheStream(stream)->strm_wr_ch_lpos = TheStream(output_stream)->strm_wr_ch_lpos;
  10381.       #ifdef STRM_WR_SS
  10382.       TheStream(stream)->strm_wr_ss = P(wr_ss_twoway);
  10383.       #endif
  10384.       TheStream(stream)->strm_twoway_input = input_stream;
  10385.       TheStream(stream)->strm_twoway_output = output_stream;
  10386.       return stream;
  10387.     }}
  10388.  
  10389. LISPFUNN(make_echo_stream,2)
  10390. # (MAKE-ECHO-STREAM input-stream output-stream), CLTL S. 330
  10391.   { # ▄berprⁿfen, ob beides Streams sind:
  10392.     test_stream_args(args_end_pointer STACKop 2, 2);
  10393.    {var reg2 object output_stream = popSTACK();
  10394.     var reg1 object input_stream = popSTACK();
  10395.     # Stream bauen:
  10396.     value1 = make_echo_stream(input_stream,output_stream); mv_count=1;
  10397.   }}
  10398.  
  10399.  
  10400. # String-Input-Stream
  10401. # ===================
  10402.  
  10403. # ZusΣtzliche Komponenten:
  10404.   #define strm_str_in_string    strm_other[0]  # String fⁿr Input
  10405.   #define strm_str_in_index     strm_other[1]  # Index in den String (Fixnum >=0)
  10406.   #define strm_str_in_endindex  strm_other[2]  # Endindex (Fixnum >= index >=0)
  10407.  
  10408. # Fehlermeldung, wenn index >= length(string):
  10409. # fehler_str_in_adjusted(stream);
  10410. # > stream: problematischer String-Input-Stream
  10411.   nonreturning_function(local, fehler_str_in_adjusted, (object stream));
  10412.   local void fehler_str_in_adjusted(stream)
  10413.     var reg1 object stream;
  10414.     { pushSTACK(TheStream(stream)->strm_str_in_string);
  10415.       pushSTACK(stream);
  10416.       fehler(error,
  10417.              DEUTSCH ? "~ hinterm Stringende angelangt, weil String ~ adjustiert wurde." :
  10418.              ENGLISH ? "~ is beyond the end because the string ~ has been adjusted" :
  10419.              FRANCAIS ? "~ est arrivΘ aprΦs la fin de la chaεne, parce que la chaεne ~ a ΘtΘ ajustΘe." :
  10420.              ""
  10421.             );
  10422.     }
  10423.  
  10424. # READ-CHAR - Pseudofunktion fⁿr String-Input-Streams:
  10425.   local object rd_ch_str_in (object* stream_);
  10426.   local object rd_ch_str_in(stream_)
  10427.     var reg4 object* stream_;
  10428.     { var reg1 object stream = *stream_;
  10429.       var reg2 uintL index = posfixnum_to_L(TheStream(stream)->strm_str_in_index); # Index
  10430.       var reg4 uintL endindex = posfixnum_to_L(TheStream(stream)->strm_str_in_endindex);
  10431.       if (index >= endindex)
  10432.         { return eof_value; } # EOF erreicht
  10433.         else
  10434.         # index < eofindex
  10435.         { var uintL len;
  10436.           var reg3 uintB* charptr = unpack_string(TheStream(stream)->strm_str_in_string,&len);
  10437.           # Ab charptr kommen len Zeichen.
  10438.           if (index >= len) # Index zu gro▀ ?
  10439.             { fehler_str_in_adjusted(stream); }
  10440.          {var reg1 object ch = code_char(charptr[index]); # Zeichen aus dem String holen
  10441.           # Index erh÷hen:
  10442.           TheStream(stream)->strm_str_in_index = fixnum_inc(TheStream(stream)->strm_str_in_index,1);
  10443.           return ch;
  10444.         }}
  10445.     }
  10446.  
  10447. # Schlie▀t einen String-Input-Stream.
  10448. # close_str_in(stream);
  10449. # > stream : String-Input-Stream
  10450.   local void close_str_in (object stream);
  10451.   local void close_str_in(stream)
  10452.     var reg1 object stream;
  10453.     { TheStream(stream)->strm_str_in_string = NIL; } # String := NIL
  10454.  
  10455. # Stellt fest, ob ein String-Input-Stream ein Zeichen verfⁿgbar hat.
  10456. # listen_str_in(stream)
  10457. # > stream : String-Input-Stream
  10458. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  10459. #             -1 falls bei EOF angelangt,
  10460. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  10461. # kann GC ausl÷sen
  10462.   local signean listen_str_in (object stream);
  10463.   local signean listen_str_in(stream)
  10464.     var reg1 object stream;
  10465.     { var reg2 uintL index = posfixnum_to_L(TheStream(stream)->strm_str_in_index); # Index
  10466.       var reg3 uintL endindex = posfixnum_to_L(TheStream(stream)->strm_str_in_endindex);
  10467.       if (index >= endindex)
  10468.         { return signean_minus; } # EOF erreicht
  10469.         else
  10470.         { return signean_null; }
  10471.     }
  10472.  
  10473. LISPFUN(make_string_input_stream,1,2,norest,nokey,0,NIL)
  10474. # (MAKE-STRING-INPUT-STREAM string [start [end]]), CLTL S. 330
  10475.   { # String holen und Grenzen ⁿberprⁿfen:
  10476.     var object string;
  10477.     var uintL start;
  10478.     var uintL len;
  10479.     test_string_limits(&string,&start,&len);
  10480.    {var reg2 object start_arg = fixnum(start); # start-Argument (Fixnum >=0)
  10481.     var reg3 object end_arg = fixnum_inc(start_arg,len); # end-Argument (Fixnum >=0)
  10482.     pushSTACK(string); # String retten
  10483.     { var reg1 object stream = # neuer Stream, nur READ-CHAR erlaubt
  10484.         allocate_stream(strmflags_rd_ch_B,strmtype_str_in,strm_len+3);
  10485.       TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  10486.       TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  10487.       TheStream(stream)->strm_rd_ch = P(rd_ch_str_in);
  10488.       TheStream(stream)->strm_rd_ch_last = NIL;
  10489.       TheStream(stream)->strm_wr_ch = P(wr_ch_dummy);
  10490.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  10491.       #ifdef STRM_WR_SS
  10492.       TheStream(stream)->strm_wr_ss = P(wr_ss_dummy);
  10493.       #endif
  10494.       TheStream(stream)->strm_str_in_string = popSTACK();
  10495.       TheStream(stream)->strm_str_in_index = start_arg; # Index := start-Argument
  10496.       TheStream(stream)->strm_str_in_endindex = end_arg; # Endindex := end-Argument
  10497.       value1 = stream; mv_count=1; # stream als Wert
  10498.   }}}
  10499.  
  10500. LISPFUNN(string_input_stream_index,1)
  10501. # (SYSTEM::STRING-INPUT-STREAM-INDEX string-input-stream) liefert den Index
  10502.   { var reg1 object stream = popSTACK(); # Argument
  10503.     # mu▀ ein String-Input-Stream sein:
  10504.     if (!(streamp(stream) && (TheStream(stream)->strmtype == strmtype_str_in)))
  10505.       { pushSTACK(stream);
  10506.         pushSTACK(TheSubr(subr_self)->name);
  10507.         fehler(error,
  10508.                DEUTSCH ? "~: ~ ist kein String-Input-Stream." :
  10509.                ENGLISH ? "~: ~ is not a string input stream" :
  10510.                FRANCAIS ? "~ : ~ n'est pas un ½stream╗ lisant d'une chaεne." :
  10511.                ""
  10512.               );
  10513.       }
  10514.    {var reg2 object index = TheStream(stream)->strm_str_in_index;
  10515.     # Falls ein Character mit UNREAD-CHAR zurⁿckgeschoben wurde,
  10516.     # verwende (1- index), ein Fixnum >=0, als Wert:
  10517.     if (mposfixnump(TheStream(stream)->strm_rd_ch_last))
  10518.       { index = fixnum_inc(index,-1); }
  10519.     value1 = index; mv_count=1;
  10520.   }}
  10521.  
  10522.  
  10523. # String-Output-Stream
  10524. # ====================
  10525.  
  10526. # ZusΣtzliche Komponenten:
  10527.   #define strm_str_out_string  strm_other[0]  # Semi-Simple-String fⁿr Output
  10528.  
  10529. # WRITE-CHAR - Pseudofunktion fⁿr String-Output-Streams:
  10530.   local void wr_ch_str_out (object* stream_, object ch);
  10531.   local void wr_ch_str_out(stream_,ch)
  10532.     var reg3 object* stream_;
  10533.     var reg1 object ch;
  10534.     { var reg2 object stream = *stream_;
  10535.       # obj sollte String-Char sein:
  10536.       if (!string_char_p(ch)) { fehler_wr_string_char(stream,ch); }
  10537.       # Character in den String schieben:
  10538.       ssstring_push_extend(TheStream(stream)->strm_str_out_string,char_code(ch));
  10539.     }
  10540.  
  10541. #ifdef STRM_WR_SS
  10542. # WRITE-SIMPLE-STRING - Pseudofunktion fⁿr String-Output-Streams:
  10543.   local void wr_ss_str_out (object* stream_, object string, uintL start, uintL len);
  10544.   local void wr_ss_str_out(stream_,srcstring,start,len)
  10545.     var reg8 object* stream_;
  10546.     var reg9 object srcstring;
  10547.     var reg10 uintL start;
  10548.     var reg6 uintL len;
  10549.     { if (len==0) return;
  10550.      {var reg4 object ssstring = TheStream(*stream_)->strm_str_out_string; # Semi-Simple-String
  10551.       var reg5 uintL old_len = TheArray(ssstring)->dims[1]; # jetzige LΣnge = Fill-Pointer
  10552.       if (old_len + len > TheArray(ssstring)->dims[0]) # passen keine len Bytes mehr hinein
  10553.         { pushSTACK(srcstring);
  10554.           ssstring = ssstring_extend(ssstring,old_len+len); # dann lΣnger machen
  10555.           srcstring = popSTACK();
  10556.         }
  10557.       # Zeichen hineinschieben:
  10558.       {var reg1 uintB* srcptr = &TheSstring(srcstring)->data[start];
  10559.        var reg3 uintL count;
  10560.        {var reg2 uintB* ptr = &TheSstring(TheArray(ssstring)->data)->data[old_len];
  10561.         dotimespL(count,len, { *ptr++ = *srcptr++; } );
  10562.        }
  10563.        # und Fill-Pointer erh÷hen:
  10564.        TheArray(ssstring)->dims[1] = old_len + len;
  10565.        wr_ss_lpos(*stream_,srcptr,len); # Line-Position aktualisieren
  10566.     }}}
  10567. #endif
  10568.  
  10569. # Liefert einen String-Output-Stream.
  10570. # make_string_output_stream()
  10571. # kann GC ausl÷sen
  10572.   global object make_string_output_stream (void);
  10573.   global object make_string_output_stream()
  10574.     { # kleinen Semi-Simple-String der LΣnge 50 allozieren:
  10575.       pushSTACK(make_ssstring(50));
  10576.      {var reg1 object stream = # neuer Stream, nur WRITE-CHAR erlaubt
  10577.         allocate_stream(strmflags_wr_ch_B,strmtype_str_out,strm_len+1);
  10578.       TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  10579.       TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  10580.       TheStream(stream)->strm_rd_ch = P(rd_ch_dummy);
  10581.       TheStream(stream)->strm_rd_ch_last = NIL;
  10582.       TheStream(stream)->strm_wr_ch = P(wr_ch_str_out);
  10583.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  10584.       #ifdef STRM_WR_SS
  10585.       TheStream(stream)->strm_wr_ss = P(wr_ss_str_out);
  10586.       #endif
  10587.       TheStream(stream)->strm_str_out_string = popSTACK(); # String eintragen
  10588.       return stream;
  10589.     }}
  10590.  
  10591. LISPFUN(make_string_output_stream,0,1,norest,nokey,0,NIL)
  10592. # (MAKE-STRING-OUTPUT-STREAM [line-position]), CLTL S. 330
  10593.   { # line-position ⁿberprⁿfen:
  10594.     if (eq(STACK_0,unbound))
  10595.       { STACK_0 = Fixnum_0; } # Defaultwert 0
  10596.       else
  10597.       # line-position angegeben, sollte ein Fixnum >=0 sein:
  10598.       { if (!mposfixnump(STACK_0)) { fehler_bad_lpos(); } }
  10599.    {var reg1 object stream = make_string_output_stream(); # String-Output-Stream
  10600.     TheStream(stream)->strm_wr_ch_lpos = popSTACK(); # Line Position eintragen
  10601.     value1 = stream; mv_count=1; # stream als Wert
  10602.   }}
  10603.  
  10604. # UP: Liefert das von einem String-Output-Stream Angesammelte.
  10605. # get_output_stream_string(&stream)
  10606. # > stream: String-Output-Stream
  10607. # < stream: geleerter Stream
  10608. # < ergebnis: Angesammeltes, ein Simple-String
  10609. # kann GC ausl÷sen
  10610.   global object get_output_stream_string (object* stream_);
  10611.   global object get_output_stream_string(stream_)
  10612.     var reg1 object* stream_;
  10613.     { var reg2 object string = TheStream(*stream_)->strm_str_out_string; # alter String
  10614.       string = coerce_ss(string); # in Simple-String umwandeln (erzwingt ein Kopieren)
  10615.       # alten String durch Fill-Pointer:=0 leeren:
  10616.       TheArray(TheStream(*stream_)->strm_str_out_string)->dims[1] = 0;
  10617.       return string;
  10618.     }
  10619.  
  10620. LISPFUNN(get_output_stream_string,1)
  10621. # (GET-OUTPUT-STREAM-STRING string-output-stream), CLTL S. 330
  10622.   { var reg1 object stream = STACK_0; # Argument
  10623.     # mu▀ ein String-Output-Stream sein:
  10624.     if (!(streamp(stream) && (TheStream(stream)->strmtype == strmtype_str_out)))
  10625.       { # stream in STACK_0
  10626.         pushSTACK(TheSubr(subr_self)->name);
  10627.         fehler(error,
  10628.                DEUTSCH ? "~: ~ ist kein String-Output-Stream." :
  10629.                ENGLISH ? "~: ~ is not a string output stream" :
  10630.                FRANCAIS ? "~ : ~ n'est pas un ½stream╗ Θcrivant dans une chaεne." :
  10631.                ""
  10632.               );
  10633.       }
  10634.    {value1 = get_output_stream_string(&STACK_0); mv_count=1; # Angesammeltes als Wert
  10635.     skipSTACK(1);
  10636.   }}
  10637.  
  10638.  
  10639. # String-Push-Stream
  10640. # ==================
  10641.  
  10642. # ZusΣtzliche Komponenten:
  10643.   #define strm_str_push_string  strm_other[0]  # String mit Fill-Pointer fⁿr Output
  10644.  
  10645. # WRITE-CHAR - Pseudofunktion fⁿr String-Push-Streams:
  10646.   local void wr_ch_str_push (object* stream_, object ch);
  10647.   local void wr_ch_str_push(stream_,ch)
  10648.     var reg3 object* stream_;
  10649.     var reg1 object ch;
  10650.     { var reg2 object stream = *stream_;
  10651.       # ch sollte String-Char sein:
  10652.       if (!string_char_p(ch)) { fehler_wr_string_char(stream,ch); }
  10653.       # Character in den String schieben:
  10654.       pushSTACK(ch); pushSTACK(TheStream(stream)->strm_str_push_string);
  10655.       funcall(L(vector_push_extend),2); # (VECTOR-PUSH-EXTEND ch string)
  10656.     }
  10657.  
  10658. # (SYSTEM::MAKE-STRING-PUSH-STREAM string) liefert einen Stream, dessen
  10659. # WRITE-CHAR-Operation mit einem VECTOR-PUSH-EXTEND auf den gegebenen String
  10660. # Σquivalent ist.
  10661. LISPFUNN(make_string_push_stream,1)
  10662.   { {var reg1 object arg = STACK_0; # Argument
  10663.      # mu▀ ein String mit Fill-Pointer sein:
  10664.      if (!(stringp(arg) && array_has_fill_pointer_p(arg)))
  10665.        { # arg in STACK_0
  10666.          pushSTACK(S(with_output_to_string));
  10667.          fehler(error,
  10668.                 DEUTSCH ? "~: Argument mu▀ ein String mit Fill-Pointer sein, nicht ~" :
  10669.                 ENGLISH ? "~: argument ~ should be a string with fill pointer" :
  10670.                 FRANCAIS ? "~ : L'argument ~ doit Ωtre une chaεne munie d'un pointeur de remplissage." :
  10671.                 ""
  10672.                );
  10673.     }  }
  10674.     {var reg1 object stream = # neuer Stream, nur WRITE-CHAR erlaubt
  10675.        allocate_stream(strmflags_wr_ch_B,strmtype_str_push,strm_len+1);
  10676.      TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  10677.      TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  10678.      TheStream(stream)->strm_rd_ch = P(rd_ch_dummy);
  10679.      TheStream(stream)->strm_rd_ch_last = NIL;
  10680.      TheStream(stream)->strm_wr_ch = P(wr_ch_str_push);
  10681.      TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  10682.      #ifdef STRM_WR_SS
  10683.      TheStream(stream)->strm_wr_ss = P(wr_ss_dummy);
  10684.      #endif
  10685.      TheStream(stream)->strm_str_push_string = popSTACK(); # String eintragen
  10686.      value1 = stream; mv_count=1; # stream als Wert
  10687.   } }
  10688.  
  10689.  
  10690. # Pretty-Printer-Hilfs-Stream
  10691. # ===========================
  10692.  
  10693. # ZusΣtzliche Komponenten:
  10694.   # define strm_pphelp_strings  strm_other[0]   # Semi-Simple-Strings fⁿr Output
  10695.   # define strm_pphelp_modus    strm_other[1]   # Modus (NIL=Einzeiler, T=Mehrzeiler)
  10696.  
  10697. # WRITE-CHAR - Pseudofunktion fⁿr Pretty-Printer-Hilfs-Streams:
  10698.   local void wr_ch_pphelp (object* stream_, object ch);
  10699.   local void wr_ch_pphelp(stream_,ch)
  10700.     var reg4 object* stream_;
  10701.     var reg2 object ch;
  10702.     { var reg1 object stream = *stream_;
  10703.       # ch sollte String-Char sein:
  10704.       if (!string_char_p(ch)) { fehler_wr_string_char(stream,ch); }
  10705.      {var reg3 uintB c = char_code(ch); # Character
  10706.       # Bei NL: Ab jetzt  Modus := Mehrzeiler
  10707.       if (c == NL) { TheStream(stream)->strm_pphelp_modus = T; }
  10708.       # Character in den ersten String schieben:
  10709.       ssstring_push_extend(Car(TheStream(stream)->strm_pphelp_strings),c);
  10710.     }}
  10711.  
  10712. #ifdef STRM_WR_SS
  10713. # WRITE-SIMPLE-STRING - Pseudofunktion fⁿr Pretty-Printer-Hilfs-Streams:
  10714.   local void wr_ss_pphelp (object* stream_, object string, uintL start, uintL len);
  10715.   local void wr_ss_pphelp(stream_,srcstring,start,len)
  10716.     var reg8 object* stream_;
  10717.     var reg9 object srcstring;
  10718.     var reg10 uintL start;
  10719.     var reg6 uintL len;
  10720.     { if (len==0) return;
  10721.      {var reg4 object ssstring = Car(TheStream(*stream_)->strm_pphelp_strings); # Semi-Simple-String
  10722.       var reg5 uintL old_len = TheArray(ssstring)->dims[1]; # jetzige LΣnge = Fill-Pointer
  10723.       if (old_len + len > TheArray(ssstring)->dims[0]) # passen keine len Bytes mehr hinein
  10724.         { pushSTACK(srcstring);
  10725.           ssstring = ssstring_extend(ssstring,old_len+len); # dann lΣnger machen
  10726.           srcstring = popSTACK();
  10727.         }
  10728.       # Zeichen hineinschieben:
  10729.       {var reg1 uintB* srcptr = &TheSstring(srcstring)->data[start];
  10730.        var reg3 uintL count;
  10731.        {var reg2 uintB* ptr = &TheSstring(TheArray(ssstring)->data)->data[old_len];
  10732.         dotimespL(count,len, { *ptr++ = *srcptr++; } );
  10733.        }
  10734.        # und Fill-Pointer erh÷hen:
  10735.        TheArray(ssstring)->dims[1] = old_len + len;
  10736.        if (wr_ss_lpos(*stream_,srcptr,len)) # Line-Position aktualisieren
  10737.          { TheStream(*stream_)->strm_pphelp_modus = T; } # Nach NL: Modus := Mehrzeiler
  10738.     }}}
  10739. #endif
  10740.  
  10741. # UP: Liefert einen Pretty-Printer-Hilfs-Stream.
  10742. # make_pphelp_stream()
  10743. # kann GC ausl÷sen
  10744.   global object make_pphelp_stream (void);
  10745.   global object make_pphelp_stream()
  10746.     { # kleinen Semi-Simple-String der LΣnge 50 allozieren:
  10747.       pushSTACK(make_ssstring(50));
  10748.       # einelementige Stringliste bauen:
  10749.      {var reg1 object new_cons = allocate_cons();
  10750.       Car(new_cons) = popSTACK();
  10751.       pushSTACK(new_cons);
  10752.      }
  10753.      {var reg1 object stream = # neuer Stream, nur WRITE-CHAR erlaubt
  10754.         allocate_stream(strmflags_wr_ch_B,strmtype_pphelp,strm_len+2);
  10755.       TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  10756.       TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  10757.       TheStream(stream)->strm_rd_ch = P(rd_ch_dummy);
  10758.       TheStream(stream)->strm_rd_ch_last = NIL;
  10759.       TheStream(stream)->strm_wr_ch = P(wr_ch_pphelp);
  10760.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  10761.       #ifdef STRM_WR_SS
  10762.       TheStream(stream)->strm_wr_ss = P(wr_ss_pphelp);
  10763.       #endif
  10764.       TheStream(stream)->strm_pphelp_strings = popSTACK(); # String-Liste eintragen
  10765.       TheStream(stream)->strm_pphelp_modus = NIL; # Modus := Einzeiler
  10766.       return stream;
  10767.     }}
  10768.  
  10769. # Buffered-Input-Stream
  10770. # =====================
  10771.  
  10772. # Elementtyp: string-char
  10773. # Richtungen: nur input
  10774. # (make-buffered-input-stream fun mode) liefert einen solchen.
  10775. #   Dabei ist fun eine Funktion von 0 Argumenten, die bei Aufruf
  10776. #   entweder NIL (steht fⁿr EOF) oder bis zu drei Werte string, start, end
  10777. #   zurⁿckliefert.
  10778. #   Funktionsweise: (read-char ...) liefert nacheinander die Zeichen des
  10779. #   aktuellen Strings; ist der zu Ende, wird fun aufgerufen, und ist der
  10780. #   Wert davon ein String, so wird der neue aktuelle String gegeben durch
  10781. #     (multiple-value-bind (str start end) (funcall fun)
  10782. #       (subseq str (or start 0) (or end 'NIL))
  10783. #     )
  10784. #   Der von fun zurⁿckgegebene String sollte nicht verΣndert werden.
  10785. #   (Ansonsten sollte fun vorher den String mit COPY-SEQ kopieren.)
  10786. #   mode bestimmt, wie sich der Stream bezⁿglich LISTEN verhΣlt.
  10787. #   mode = NIL: Stream verhΣlt sich wie ein File-Stream, d.h. bei LISTEN
  10788. #               und leerem aktuellen String wird fun aufgerufen.
  10789. #   mode = T: Stream verhΣlt sich wie ein interaktiver Stream ohne EOF,
  10790. #             d.h. man kann davon ausgehen, das stets noch weitere Zeichen
  10791. #             kommen, auch ohne fun aufzurufen.
  10792. #   mode eine Funktion: Diese Funktion teilt, wenn aufgerufen, mit, ob
  10793. #             noch weitere nichtleere Strings zu erwarten sind.
  10794. #   (clear-input ...) beendet die Bearbeitung des aktuellen Strings.
  10795.  
  10796. # ZusΣtzliche Komponenten:
  10797.   # define strm_buff_in_fun      strm_other[0]  # Lesefunktion
  10798.   #define strm_buff_in_mode      strm_other[1]  # Modus oder Listen-Funktion
  10799.   #define strm_buff_in_string    strm_other[2]  # aktueller String fⁿr Input
  10800.   #define strm_buff_in_index     strm_other[3]  # Index in den String (Fixnum >=0)
  10801.   #define strm_buff_in_endindex  strm_other[4]  # Endindex (Fixnum >= index >=0)
  10802.  
  10803. # READ-CHAR - Pseudofunktion fⁿr Buffered-Input-Streams:
  10804.   local object rd_ch_buff_in (object* stream_);
  10805.   local object rd_ch_buff_in(stream_)
  10806.     var reg5 object* stream_;
  10807.     { var reg1 object stream = *stream_;
  10808.       var reg2 uintL index = posfixnum_to_L(TheStream(stream)->strm_buff_in_index); # Index
  10809.       var reg4 uintL endindex = posfixnum_to_L(TheStream(stream)->strm_buff_in_endindex);
  10810.       loop
  10811.         { if (index < endindex) break; # noch was im aktuellen String?
  10812.           # String-Ende erreicht
  10813.           # fun aufrufen:
  10814.           funcall(TheStream(stream)->strm_buff_in_fun,0);
  10815.           if (!stringp(value1))
  10816.             { return eof_value; } # EOF erreicht
  10817.           # neuen String holen und Grenzen ⁿberprⁿfen:
  10818.           pushSTACK(value1); # String
  10819.           pushSTACK(mv_count >= 2 ? value2 : unbound); # start
  10820.           pushSTACK(mv_count >= 3 ? value3 : unbound); # end
  10821.          {var object string;
  10822.           var uintL start;
  10823.           var uintL len;
  10824.           subr_self = L(read_char);
  10825.           test_string_limits(&string,&start,&len);
  10826.           stream = *stream_;
  10827.           index = start;
  10828.           endindex = index+len;
  10829.           TheStream(stream)->strm_buff_in_string = string;
  10830.           TheStream(stream)->strm_buff_in_index = fixnum(index);
  10831.           TheStream(stream)->strm_buff_in_endindex = fixnum(endindex);
  10832.         }}
  10833.       # index < eofindex
  10834.       { var uintL len;
  10835.         var reg3 uintB* charptr = unpack_string(TheStream(stream)->strm_buff_in_string,&len);
  10836.         # Ab charptr kommen len Zeichen.
  10837.         if (index >= len) # Index zu gro▀ ?
  10838.           { pushSTACK(TheStream(stream)->strm_buff_in_string);
  10839.             pushSTACK(stream);
  10840.             fehler(error,
  10841.                    DEUTSCH ? "~ hinterm Stringende angelangt, weil String ~ adjustiert wurde." :
  10842.                    ENGLISH ? "~ is beyond the end because the string ~ has been adjusted" :
  10843.                    FRANCAIS ? "~ est arrivΘ aprΦs la fin de la chaεne, parce que la chaεne ~ a ΘtΘ ajustΘe." :
  10844.                    ""
  10845.                   );
  10846.           }
  10847.        {var reg1 object ch = code_char(charptr[index]); # Zeichen aus dem String holen
  10848.         # Index erh÷hen:
  10849.         TheStream(stream)->strm_buff_in_index = fixnum_inc(TheStream(stream)->strm_buff_in_index,1);
  10850.         return ch;
  10851.       }}
  10852.     }
  10853.  
  10854. # Schlie▀t einen Buffered-Input-Stream.
  10855. # close_buff_in(stream);
  10856. # > stream : Buffered-Input-Stream
  10857.   local void close_buff_in (object stream);
  10858.   local void close_buff_in(stream)
  10859.     var reg1 object stream;
  10860.     { TheStream(stream)->strm_buff_in_fun = NIL; # Funktion := NIL
  10861.       TheStream(stream)->strm_buff_in_mode = NIL; # Mode := NIL
  10862.       TheStream(stream)->strm_buff_in_string = NIL; # String := NIL
  10863.     }
  10864.  
  10865. # Stellt fest, ob ein Buffered-Input-Stream ein Zeichen verfⁿgbar hat.
  10866. # listen_buff_in(stream)
  10867. # > stream : Buffered-Input-Stream
  10868. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  10869. #             -1 falls bei EOF angelangt,
  10870. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  10871. # kann GC ausl÷sen
  10872.   local signean listen_buff_in (object stream);
  10873.   local signean listen_buff_in(stream)
  10874.     var reg1 object stream;
  10875.     { var reg3 uintL index = posfixnum_to_L(TheStream(stream)->strm_buff_in_index); # Index
  10876.       var reg4 uintL endindex = posfixnum_to_L(TheStream(stream)->strm_buff_in_endindex);
  10877.       if (index < endindex) { return signean_null; }
  10878.      {var reg2 object mode = TheStream(stream)->strm_buff_in_mode;
  10879.       if (eq(mode,S(nil)))
  10880.         { pushSTACK(stream);
  10881.           mode = peek_char(&STACK_0); # peek_char macht read_char, ruft fun auf
  10882.           skipSTACK(1);
  10883.           if (eq(mode,eof_value))
  10884.             { return signean_minus; } # EOF erreicht
  10885.             else
  10886.             { return signean_null; }
  10887.         }
  10888.       elif (eq(mode,S(t)))
  10889.         { return signean_null; }
  10890.       else
  10891.         { funcall(mode,0); # mode aufrufen
  10892.           if (nullp(value1)) # keine Strings mehr zu erwarten?
  10893.             { return signean_minus; } # ja -> EOF erreicht
  10894.             else
  10895.             { return signean_null; }
  10896.         }
  10897.     }}
  10898.  
  10899. # UP: L÷scht bereits eingegebenen interaktiven Input von einem Buffered-Input-Stream.
  10900. # clear_input_buff_in(stream)
  10901. # > stream: Buffered-Input-Stream
  10902. # < ergebnis: TRUE falls Input gel÷scht wurde
  10903. # kann GC ausl÷sen
  10904.   local boolean clear_input_buff_in (object stream);
  10905.   local boolean clear_input_buff_in(stream)
  10906.     var reg1 object stream;
  10907.     { # Bearbeitung des aktuellen Strings beenden:
  10908.       var reg3 object index = TheStream(stream)->strm_buff_in_index; # Index
  10909.       var reg2 object endindex = TheStream(stream)->strm_buff_in_endindex;
  10910.       TheStream(stream)->strm_buff_in_index = endindex; # index := endindex
  10911.       if (eq(index,endindex)) { return FALSE; } else { return TRUE; }
  10912.     }
  10913.  
  10914. LISPFUNN(make_buffered_input_stream,2)
  10915. # (MAKE-BUFFERED-INPUT-STREAM fun mode)
  10916.   { var reg1 object stream = # neuer Stream, nur READ-CHAR erlaubt
  10917.       allocate_stream(strmflags_rd_ch_B,strmtype_buff_in,strm_len+5);
  10918.     TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  10919.     TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  10920.     TheStream(stream)->strm_rd_ch = P(rd_ch_buff_in);
  10921.     TheStream(stream)->strm_rd_ch_last = NIL;
  10922.     TheStream(stream)->strm_wr_ch = P(wr_ch_dummy);
  10923.     TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  10924.     #ifdef STRM_WR_SS
  10925.     TheStream(stream)->strm_wr_ss = P(wr_ss_dummy);
  10926.     #endif
  10927.     TheStream(stream)->strm_buff_in_mode = popSTACK();
  10928.     TheStream(stream)->strm_buff_in_fun = popSTACK();
  10929.     TheStream(stream)->strm_buff_in_string = O(leer_string); # String := ""
  10930.     TheStream(stream)->strm_buff_in_index = Fixnum_0; # Index := 0
  10931.     TheStream(stream)->strm_buff_in_endindex = Fixnum_0; # Endindex := 0
  10932.     value1 = stream; mv_count=1; # stream als Wert
  10933.   }
  10934.  
  10935. LISPFUNN(buffered_input_stream_index,1)
  10936. # (SYSTEM::BUFFERED-INPUT-STREAM-INDEX buffered-input-stream) liefert den Index
  10937.   { var reg1 object stream = popSTACK(); # Argument
  10938.     # mu▀ ein Buffered-Input-Stream sein:
  10939.     if (!(streamp(stream) && (TheStream(stream)->strmtype == strmtype_buff_in)))
  10940.       { pushSTACK(stream);
  10941.         pushSTACK(TheSubr(subr_self)->name);
  10942.         fehler(error,
  10943.                DEUTSCH ? "~: ~ ist kein Buffered-Input-Stream." :
  10944.                ENGLISH ? "~: ~ is not a buffered input stream" :
  10945.                FRANCAIS ? "~ : ~ n'est pas un ½stream╗ d'entrΘe bufferisΘ." :
  10946.                ""
  10947.               );
  10948.       }
  10949.    {var reg2 object index = TheStream(stream)->strm_buff_in_index;
  10950.     # Falls ein Character mit UNREAD-CHAR zurⁿckgeschoben wurde,
  10951.     # verwende (1- index), ein Fixnum >=0, als Wert:
  10952.     if (mposfixnump(TheStream(stream)->strm_rd_ch_last))
  10953.       { index = fixnum_inc(index,-1); }
  10954.     value1 = index; mv_count=1;
  10955.   }}
  10956.  
  10957.  
  10958. # Buffered-Output-Stream
  10959. # ======================
  10960.  
  10961. # Elementtyp: string-char
  10962. # Richtungen: nur output
  10963. # (make-buffered-output-stream fun) liefert einen solchen.
  10964. #   Dabei ist fun eine Funktion von einem Argument, die, mit einem
  10965. #   Simple-String als Argument aufgerufen, dessen Inhalt in Empfang nimmt.
  10966. #   Funktionsweise: (write-char ...) sammelt die geschriebenen Zeichen in
  10967. #   einem String, bis ein #\Newline oder eine FORCE-/FINISH-OUTPUT-
  10968. #   Anforderung kommt, und ruft dann fun mit einem Simple-String, der das
  10969. #   bisher Angesammelte enthΣlt, als Argument auf.
  10970. #   (clear-output ...) wirft die bisher angesammelten Zeichen weg.
  10971.  
  10972. # ZusΣtzliche Komponenten:
  10973.   # define strm_buff_out_fun    strm_other[0]  # Ausgabefunktion
  10974.   #define strm_buff_out_string  strm_other[1]  # Semi-Simple-String fⁿr Output
  10975.  
  10976. # UP: Bringt den wartenden Output eines Buffered-Output-Stream ans Ziel.
  10977. # finish_output_buff_out(stream);
  10978. # > stream: Buffered-Output-Stream
  10979. # kann GC ausl÷sen
  10980.   local void finish_output_buff_out (object stream);
  10981.   local void finish_output_buff_out(stream)
  10982.     var reg1 object stream;
  10983.     { pushSTACK(stream);
  10984.      {var reg2 object string = TheStream(stream)->strm_buff_out_string; # String
  10985.       string = coerce_ss(string); # in Simple-String umwandeln (erzwingt ein Kopieren)
  10986.       stream = STACK_0; STACK_0 = string;
  10987.       # String durch Fill-Pointer:=0 leeren:
  10988.       TheArray(TheStream(stream)->strm_buff_out_string)->dims[1] = 0;
  10989.       funcall(TheStream(stream)->strm_buff_out_fun,1); # Funktion aufrufen
  10990.     }}
  10991.  
  10992. # UP: Bringt den wartenden Output eines Buffered-Output-Stream ans Ziel.
  10993. # force_output_buff_out(stream);
  10994. # > stream: Buffered-Output-Stream
  10995. # kann GC ausl÷sen
  10996.   #define force_output_buff_out  finish_output_buff_out
  10997.  
  10998. # UP: L÷scht den wartenden Output eines Buffered-Output-Stream.
  10999. # clear_output_buff_out(stream);
  11000. # > stream: Buffered-Output-Stream
  11001. # kann GC ausl÷sen
  11002.   local void clear_output_buff_out (object stream);
  11003.   local void clear_output_buff_out(stream)
  11004.     var reg1 object stream;
  11005.     { # String durch Fill-Pointer:=0 leeren:
  11006.       TheArray(TheStream(stream)->strm_buff_out_string)->dims[1] = 0;
  11007.       # Line-Position unverΣndert lassen??
  11008.     }
  11009.  
  11010. # WRITE-CHAR - Pseudofunktion fⁿr Buffered-Output-Streams:
  11011.   local void wr_ch_buff_out (object* stream_, object ch);
  11012.   local void wr_ch_buff_out(stream_,ch)
  11013.     var reg3 object* stream_;
  11014.     var reg1 object ch;
  11015.     { var reg2 object stream = *stream_;
  11016.       # obj sollte String-Char sein:
  11017.       if (!string_char_p(ch)) { fehler_wr_string_char(stream,ch); }
  11018.       # Character in den String schieben:
  11019.       ssstring_push_extend(TheStream(stream)->strm_buff_out_string,char_code(ch));
  11020.       # Nach #\Newline den Buffer durchreichen:
  11021.       if (char_code(ch) == NL) { force_output_buff_out(*stream_); }
  11022.     }
  11023.  
  11024. # Schlie▀t einen Buffered-Output-Stream.
  11025. # close_buff_out(stream);
  11026. # > stream : Buffered-Output-Stream
  11027. # kann GC ausl÷sen
  11028.   local void close_buff_out (object stream);
  11029.   local void close_buff_out(stream)
  11030.     var reg1 object stream;
  11031.     { pushSTACK(stream); # stream retten
  11032.       finish_output_buff_out(stream);
  11033.       stream = popSTACK(); # stream zurⁿck
  11034.       TheStream(stream)->strm_buff_out_fun = NIL; # Funktion := NIL
  11035.       TheStream(stream)->strm_buff_out_string = NIL; # String := NIL
  11036.     }
  11037.  
  11038. LISPFUN(make_buffered_output_stream,1,1,norest,nokey,0,NIL)
  11039. # (MAKE-BUFFERED-OUTPUT-STREAM fun [line-position])
  11040.   { # line-position ⁿberprⁿfen:
  11041.     if (eq(STACK_0,unbound))
  11042.       { STACK_0 = Fixnum_0; } # Defaultwert 0
  11043.       else
  11044.       # line-position angegeben, sollte ein Fixnum >=0 sein:
  11045.       { if (!mposfixnump(STACK_0)) { fehler_bad_lpos(); } }
  11046.     # kleinen Semi-Simple-String der LΣnge 50 allozieren:
  11047.     pushSTACK(make_ssstring(50));
  11048.    {var reg1 object stream = # neuer Stream, nur WRITE-CHAR erlaubt
  11049.       allocate_stream(strmflags_wr_ch_B,strmtype_buff_out,strm_len+2);
  11050.     TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  11051.     TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  11052.     TheStream(stream)->strm_rd_ch = P(rd_ch_dummy);
  11053.     TheStream(stream)->strm_rd_ch_last = NIL;
  11054.     TheStream(stream)->strm_wr_ch = P(wr_ch_buff_out);
  11055.     #ifdef STRM_WR_SS
  11056.     TheStream(stream)->strm_wr_ss = P(wr_ss_dummy);
  11057.     #endif
  11058.     TheStream(stream)->strm_buff_out_string = popSTACK(); # String eintragen
  11059.     TheStream(stream)->strm_wr_ch_lpos = popSTACK(); # Line Position eintragen
  11060.     TheStream(stream)->strm_buff_out_fun = popSTACK(); # Funktion eintragen
  11061.     value1 = stream; mv_count=1; # stream als Wert
  11062.   }}
  11063.  
  11064.  
  11065. #ifdef PRINTER_ATARI
  11066.  
  11067. # Printer-Stream
  11068. # ==============
  11069.  
  11070. # ZusΣtzliche Komponenten:
  11071.   #define strm_printer_port  strm_other[0]  # Fixnum >=0:
  11072.   # 0 bei paralleler, 1 bei serieller Schnittstelle
  11073.  
  11074. # Gibt ein Zeichen auf den Printer aus.
  11075. # wr_printer(port,c)
  11076. # > port: Printer-Port
  11077. # > c: Auszugebendes Zeichen
  11078. # kann GC ausl÷sen
  11079.   local void printer_out (object port, uintB c);
  11080.   local void printer_out(port,c)
  11081.     var reg1 object port;
  11082.     var reg2 uintB c;
  11083.     { # Stellt fest, ob der Drucker bereit ist:
  11084.       #define printer_ready()  (eq(port,Fixnum_0) ? GEMDOS_PrtStat() : GEMDOS_AuxStat())
  11085.       until (printer_ready()) # Drucker bereit?
  11086.         { # Drucker noch nicht bereit -> eine Warteschleife einlegen:
  11087.           var reg5 object timeout = Symbol_value(S(printer_timeout)) ; # Wert von *PRINTER-TIMEOUT*
  11088.           var reg4 uintL wait_time;
  11089.           # sollte ein Fixnum >=0, <= 60*ticks_per_second = 12000 sein:
  11090.           if (posfixnump(timeout)
  11091.               && ((wait_time = posfixnum_to_L(timeout)) <= 12000)
  11092.              )
  11093.             { var reg3 uintL wait_start_time = get_real_time();
  11094.               loop # Warteschleife
  11095.                 { if (printer_ready()) goto OK; # Drucker bereit -> Warteschleife beenden
  11096.                   if ((get_real_time() - wait_start_time) >= wait_time) break;
  11097.                 }
  11098.               # Habe wait_time gewartet, jetzt wird's Zeit fⁿr eine Fehlermeldung:
  11099.               # Continuable Error melden:
  11100.               pushSTACK(OL(druckerstreik_string1)); # "Neuer Versuch."
  11101.               pushSTACK(OL(druckerstreik_string2)); # "Drucker nicht angeschlossen oder off-line."
  11102.               funcall(S(cerror),2); # (CERROR "..." "...")
  11103.             }
  11104.             else
  11105.             { # Illegaler Wert in *PRINTER-TIMEOUT* vorgefunden.
  11106.               # Wert zurⁿcksetzen:
  11107.               Symbol_value(S(printer_timeout)) = fixnum(1000); # *PRINTER-TIMEOUT* := 1000
  11108.               # Warnung ausgeben:
  11109.               pushSTACK(OL(printer_timeout_warnung_string)); # "Der Wert ..."
  11110.               pushSTACK(S(printer_timeout));
  11111.               funcall(S(warn),2); # (WARN "..." '*PRINTER-TIMEOUT*)
  11112.             }
  11113.         }
  11114.       OK: # Drucker bereit -> Zeichen endlich ausgeben:
  11115.       if (eq(port,Fixnum_0)) { GEMDOS_PrtOut(c); } else { GEMDOS_AuxOut(c); }
  11116.       #undef printer_ready
  11117.     }
  11118.  
  11119. # WRITE-CHAR - Pseudofunktion fⁿr Printer-Streams:
  11120.   local void wr_ch_printer (object* stream_, object ch);
  11121.   local void wr_ch_printer(stream_,ch)
  11122.     var reg4 object* stream_;
  11123.     var reg2 object ch;
  11124.     { var reg1 object stream = *stream_;
  11125.       # ch sollte String-Char sein:
  11126.       if (!string_char_p(ch)) { fehler_wr_string_char(stream,ch); }
  11127.      {var reg4 object port = TheStream(stream)->strm_printer_port;
  11128.       var reg3 uintB c = char_code(ch); # Character
  11129.       if (c==NL)
  11130.         # NL in CR/LF umwandeln:
  11131.         { printer_out(port,CR); printer_out(port,LF); }
  11132.         else
  11133.         { printer_out(port,c); }
  11134.     }}
  11135.  
  11136. # UP: Liefert einen Printer-Stream.
  11137. # kann GC ausl÷sen
  11138.   local object make_printer_stream (void);
  11139.   local object make_printer_stream()
  11140.     { var reg1 object stream = # neuer Stream, nur WRITE-CHAR erlaubt
  11141.         allocate_stream(strmflags_wr_ch_B,strmtype_printer,strm_len+1);
  11142.       TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  11143.       TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  11144.       TheStream(stream)->strm_rd_ch = P(rd_ch_dummy);
  11145.       TheStream(stream)->strm_rd_ch_last = NIL;
  11146.       TheStream(stream)->strm_wr_ch = P(wr_ch_printer);
  11147.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  11148.       #ifdef STRM_WR_SS
  11149.       TheStream(stream)->strm_wr_ss = P(wr_ss_dummy);
  11150.       #endif
  11151.       TheStream(stream)->strm_printer_port = # Printer-Port
  11152.         # Druckerkonfiguration abfragen:
  11153.         (XBIOS_GetPrtConfig() & bit(4) ? Fixnum_1 # serielle Schnittstelle
  11154.                                        : Fixnum_0 # parallele Schnittstelle
  11155.         );
  11156.       return stream;
  11157.     }
  11158.  
  11159. #endif
  11160.  
  11161. #ifdef PRINTER_AMIGAOS
  11162.  
  11163. # Printer-Stream
  11164. # ==============
  11165.  
  11166. # ZusΣtzliche Komponenten:
  11167.   #define strm_printer_handle  strm_other[0]  # Handle von "PRT:"
  11168.  
  11169. # WRITE-CHAR - Pseudofunktion fⁿr Printer-Streams:
  11170.   local void wr_ch_printer (object* stream_, object ch);
  11171.   local void wr_ch_printer(stream_,ch)
  11172.     var reg4 object* stream_;
  11173.     var reg2 object ch;
  11174.     { var reg1 object stream = *stream_;
  11175.       # ch sollte String-Char sein:
  11176.       if (!string_char_p(ch)) { fehler_wr_string_char(stream,ch); }
  11177.       begin_system_call();
  11178.      {var uintB c = char_code(ch);
  11179.       var reg3 long ergebnis = # Zeichen auszugeben versuchen
  11180.         Write(TheHandle(TheStream(stream)->strm_printer_handle),&c,1L);
  11181.       end_system_call();
  11182.       if (ergebnis<0) { OS_error(); } # Error melden
  11183.       # ergebnis = Anzahl der ausgegebenen Zeichen (0 oder 1)
  11184.       if (ergebnis==0) # nicht erfolgreich?
  11185.         { fehler_unwritable(S(write_char),stream); }
  11186.     }}
  11187.  
  11188. # Schlie▀t einen Printer-Stream.
  11189.   local void close_printer (object stream);
  11190.   local void close_printer(stream)
  11191.     var reg1 object stream;
  11192.     { begin_system_call();
  11193.       Close(TheHandle(TheStream(stream)->strm_printer_handle));
  11194.       end_system_call();
  11195.     }
  11196.  
  11197. # UP: Liefert einen Printer-Stream.
  11198. # kann GC ausl÷sen
  11199.   local object make_printer_stream (void);
  11200.   local object make_printer_stream()
  11201.     { pushSTACK(allocate_cons()); # Cons fⁿr Liste
  11202.       pushSTACK(allocate_handle(Handle_NULL)); # Handle-Verpackung
  11203.      {var reg1 object stream = # neuer Stream, nur WRITE-CHAR erlaubt
  11204.         allocate_stream(strmflags_wr_ch_B,strmtype_printer,strm_len+1);
  11205.       set_break_sem_4();
  11206.       begin_system_call();
  11207.       {var reg2 Handle handle = Open("PRT:",MODE_NEWFILE);
  11208.        if (handle==Handle_NULL) { OS_error(); } # Error melden
  11209.        end_system_call();
  11210.        TheHandle(STACK_0) = handle; # Handle verpacken
  11211.       }
  11212.       TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  11213.       TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  11214.       TheStream(stream)->strm_rd_ch = P(rd_ch_dummy);
  11215.       TheStream(stream)->strm_rd_ch_last = NIL;
  11216.       TheStream(stream)->strm_wr_ch = P(wr_ch_printer);
  11217.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  11218.       #ifdef STRM_WR_SS
  11219.       TheStream(stream)->strm_wr_ss = P(wr_ss_dummy_nogc);
  11220.       #endif
  11221.       TheStream(stream)->strm_printer_handle = popSTACK();
  11222.       # Liste der offenen Streams um stream erweitern:
  11223.       {var reg1 object new_cons = popSTACK();
  11224.        Car(new_cons) = stream;
  11225.        Cdr(new_cons) = O(open_files);
  11226.        O(open_files) = new_cons;
  11227.       }
  11228.       clr_break_sem_4();
  11229.       return stream;
  11230.     }}
  11231.  
  11232. LISPFUNN(make_printer_stream,0)
  11233. # (SYSTEM::MAKE-PRINTER-STREAM) liefert einen Printer-Stream.
  11234. # Fⁿr die verstandenen Escape-Sequenzen siehe in PRINTER.DOC.
  11235.   { value1 = make_printer_stream(); mv_count=1; return; }
  11236.  
  11237. #endif
  11238.  
  11239.  
  11240. #ifdef PIPES
  11241.  
  11242. # Pipe-Input-Stream
  11243. # =================
  11244.  
  11245. # ZusΣtzliche Komponenten:
  11246.   # define strm_pipe_pid       strm_other[3] # Proze▀-Id, ein Fixnum >=0
  11247.   #define strm_pipe_in_handle  strm_ihandle  # Handle fⁿr Input
  11248.   #if defined(EMUNIX) && defined(PIPES2)
  11249.   #define strm_pipe_in_other   strm_ohandle  # Pipe-Stream in Gegenrichtung
  11250.   #define strm_pipe_out_other  strm_ihandle  # Pipe-Stream in Gegenrichtung
  11251.   #endif
  11252.  
  11253. # READ-CHAR - Pseudofunktion fⁿr Pipe-Input-Streams:
  11254.   #define rd_ch_pipe_in  rd_ch_handle
  11255.  
  11256. # Schlie▀t einen Pipe-Input-Stream.
  11257. # close_pipe_in(stream);
  11258. # > stream : Pipe-Input-Stream
  11259.   #ifdef EMUNIX
  11260.     local void close_pipe_in (object stream);
  11261.     local void close_pipe_in(stream)
  11262.       var reg1 object stream;
  11263.       { var reg2 Handle handle = TheHandle(TheStream(stream)->strm_pipe_in_handle);
  11264.         #ifdef PIPES2
  11265.         if (mstreamp(TheStream(stream)->strm_pipe_in_other))
  11266.           # Der andere Pipe-Stream ist noch offen. Wir dⁿrfen nicht pclose()
  11267.           # aufrufen, da das ein waitpid() ausfⁿhrt.
  11268.           { TheStream(TheStream(stream)->strm_pipe_in_other)->strm_pipe_out_other = NIL;
  11269.             TheStream(stream)->strm_pipe_in_other = NIL;
  11270.             begin_system_call();
  11271.             if ( fclose(&_streamv[handle]) != 0) { OS_error(); }
  11272.             end_system_call();
  11273.             # Die Pipes sind nun getrennt, so da▀ beim Schlie▀en der anderen
  11274.             # Pipe das pclose() ausgefⁿhrt werden wird.
  11275.             return;
  11276.           }
  11277.         #endif
  11278.         begin_system_call();
  11279.         if ( pclose(&_streamv[handle]) == -1) { OS_error(); }
  11280.         end_system_call();
  11281.       }
  11282.   #endif
  11283.   #ifdef UNIX
  11284.     #define close_pipe_in  close_ihandle
  11285.   #endif
  11286.  
  11287. # Stellt fest, ob ein Pipe-Input-Stream ein Zeichen verfⁿgbar hat.
  11288. # listen_pipe_in(stream)
  11289. # > stream : Pipe-Input-Stream
  11290. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  11291. #             -1 falls bei EOF angelangt,
  11292. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  11293. # kann GC ausl÷sen
  11294.   #define listen_pipe_in  listen_handle
  11295.  
  11296. LISPFUNN(make_pipe_input_stream,1)
  11297. # (MAKE-PIPE-INPUT-STREAM command)
  11298. # ruft eine Shell auf, die command ausfⁿhrt, wobei deren Standard-Output
  11299. # in unsere Pipe hinein geht.
  11300.   { # command ⁿberprⁿfen:
  11301.     var reg4 object command;
  11302.     funcall(L(string),1); # (STRING command)
  11303.     command = string_to_asciz(value1); # als ASCIZ-String
  11304.     pushSTACK(command);
  11305.    {var int handles[2]; # zwei Handles fⁿr die Pipe
  11306.     var reg3 int child;
  11307.     #ifdef EMUNIX
  11308.     { # Stackaufbau: command.
  11309.       begin_system_call();
  11310.      {var reg1 FILE* f = popen(TheAsciz(STACK_0),"r");
  11311.       if (f==NULL) { OS_error(); }
  11312.       child = f->pid;
  11313.       handles[0] = fileno(f);
  11314.       end_system_call();
  11315.     }}
  11316.     #endif
  11317.     #ifdef UNIX
  11318.     { # Als Shell nehmen wir immer die Kommando-Shell.
  11319.       # Stackaufbau: command.
  11320.       # command in den Stack kopieren:
  11321.       var reg5 uintL command_length = TheSstring(command)->length;
  11322.       var DYNAMIC_ARRAY(reg6,command_data,char,command_length);
  11323.       { var reg2 char* ptr1 = TheAsciz(command);
  11324.         var reg1 char* ptr2 = &command_data[0];
  11325.         dotimespL(command_length,command_length, { *ptr2++ = *ptr1++; } );
  11326.       }
  11327.       begin_system_call();
  11328.       # Pipe aufbauen:
  11329.       if (!( pipe(handles) ==0))
  11330.         { FREE_DYNAMIC_ARRAY(command_data); OS_error(); }
  11331.       # Alles, was in handles[1] reingeschoben wird, kommt bei handles[0]
  11332.       # wieder raus. Wir werden dies so benutzen:
  11333.       #
  11334.       #       write            system            read
  11335.       # child  ->   handles[1]   ->   handles[0]  ->  parent
  11336.       #
  11337.       # einen neuen Proze▀ starten:
  11338.       if ((child = vfork()) ==0)
  11339.         # Dieses Programmstⁿck wird vom Child-Proze▀ ausgefⁿhrt:
  11340.         { if ( dup2(handles[1],stdout_handle) >=0) # Standard-Output umleiten
  11341.             if ( CLOSE(handles[1]) ==0) # Wir wollen nur ⁿber stdout_handle schreiben
  11342.               if ( CLOSE(handles[0]) ==0) # Wir wollen von der Pipe nicht lesen
  11343.                 # (Mu▀ das dem Betriebssystem sagen, damit - wenn der Child
  11344.                 # die Pipe gefⁿllt hat - der Parent-Proze▀ und nicht etwa der
  11345.                 # Child-Proze▀ aufgerufen wird, um die Pipe zu leeren.)
  11346.                 { # Child-Proze▀ zum Hintergrundproze▀ machen:
  11347.                   SETSID(); # er bekommt eine eigene Process Group
  11348.                   execl(SHELL,            # Shell aufrufen
  11349.                         SHELL,            # =: argv[0]
  11350.                         "-c",             # =: argv[1]
  11351.                         &command_data[0], # =: argv[2]
  11352.                         NULL
  11353.                        );
  11354.                 }
  11355.           _exit(-1); # sollte dies mi▀lingen, Child-Proze▀ beenden
  11356.         }
  11357.       # Dieses Programmstⁿck wird wieder vom Aufrufer ausgefⁿhrt:
  11358.       if (child==-1)
  11359.         # Etwas ist mi▀lungen, entweder beim vfork oder beim execl.
  11360.         # In beiden FΣllen wurde errno gesetzt.
  11361.         { var int saved_errno = errno;
  11362.           CLOSE(handles[1]); CLOSE(handles[0]);
  11363.           FREE_DYNAMIC_ARRAY(command_data);
  11364.           errno = saved_errno; OS_error();
  11365.         }
  11366.       # Wir wollen von der Pipe nur lesen, nicht schreiben:
  11367.       if (!( CLOSE(handles[1]) ==0))
  11368.         { var int saved_errno = errno;
  11369.           CLOSE(handles[0]);
  11370.           FREE_DYNAMIC_ARRAY(command_data);
  11371.           errno = saved_errno; OS_error();
  11372.         }
  11373.       # (Mu▀ das dem Betriebssystem sagen, damit - wenn der Parent-Proze▀
  11374.       # die Pipe geleert hat - der Child-Proze▀ und nicht etwa der
  11375.       # Parent-Proze▀ aufgerufen wird, um die Pipe wieder zu fⁿllen.)
  11376.       end_system_call();
  11377.       FREE_DYNAMIC_ARRAY(command_data);
  11378.     }
  11379.     #endif
  11380.     # Stream allozieren:
  11381.     { var reg1 object stream = # neuer Stream, nur READ-CHAR erlaubt
  11382.         allocate_stream(strmflags_rd_ch_B,strmtype_pipe_in,strm_len+4);
  11383.       TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  11384.       TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  11385.       TheStream(stream)->strm_rd_ch = P(rd_ch_pipe_in);
  11386.       TheStream(stream)->strm_rd_ch_last = NIL;
  11387.       TheStream(stream)->strm_wr_ch = P(wr_ch_dummy);
  11388.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  11389.       #ifdef STRM_WR_SS
  11390.       TheStream(stream)->strm_wr_ss = P(wr_ss_dummy);
  11391.       #endif
  11392.       TheStream(stream)->strm_pipe_pid = fixnum(child); # Child-Pid
  11393.       TheStream(stream)->strm_pipe_in_handle = allocate_handle(handles[0]); # Read-Handle
  11394.       TheStream(stream)->strm_isatty = NIL;
  11395.       value1 = stream; mv_count=1; # stream als Wert
  11396.       skipSTACK(1);
  11397.   }}}
  11398.  
  11399.  
  11400. # Pipe-Output-Stream
  11401. # ==================
  11402.  
  11403. # ZusΣtzliche Komponenten:
  11404.   # define strm_pipe_pid          strm_other[3] # Proze▀-Id, ein Fixnum >=0
  11405.   #define strm_pipe_out_handle    strm_ohandle  # Handle fⁿr Output
  11406.   #if defined(EMUNIX) && defined(PIPES2)
  11407.   # define strm_pipe_out_other    strm_ihandle  # Pipe-Stream in Gegenrichtung
  11408.   #endif
  11409.  
  11410. # WRITE-CHAR - Pseudofunktion fⁿr Pipe-Output-Streams:
  11411.   #define wr_ch_pipe_out  wr_ch_handle
  11412.  
  11413. #ifdef STRM_WR_SS
  11414. # WRITE-SIMPLE-STRING - Pseudofunktion fⁿr Pipe-Output-Streams:
  11415.   #define wr_ss_pipe_out  wr_ss_handle
  11416. #endif
  11417.  
  11418. # Schlie▀t einen Pipe-Output-Stream.
  11419. # close_pipe_out(stream);
  11420. # > stream : Pipe-Output-Stream
  11421.   #ifdef EMUNIX
  11422.     local void close_pipe_out (object stream);
  11423.     local void close_pipe_out(stream)
  11424.       var reg1 object stream;
  11425.       { var reg2 Handle handle = TheHandle(TheStream(stream)->strm_pipe_out_handle);
  11426.         #ifdef PIPES2
  11427.         if (mstreamp(TheStream(stream)->strm_pipe_out_other))
  11428.           # Der andere Pipe-Stream ist noch offen. Wir dⁿrfen nicht pclose()
  11429.           # aufrufen, da das ein waitpid() ausfⁿhrt.
  11430.           { TheStream(TheStream(stream)->strm_pipe_out_other)->strm_pipe_in_other = NIL;
  11431.             TheStream(stream)->strm_pipe_out_other = NIL;
  11432.             begin_system_call();
  11433.             if ( fclose(&_streamv[handle]) != 0) { OS_error(); }
  11434.             end_system_call();
  11435.             # Die Pipes sind nun getrennt, so da▀ beim Schlie▀en der anderen
  11436.             # Pipe das pclose() ausgefⁿhrt werden wird.
  11437.             return;
  11438.           }
  11439.         #endif
  11440.         begin_system_call();
  11441.         if ( pclose(&_streamv[handle]) == -1) { OS_error(); }
  11442.         end_system_call();
  11443.       }
  11444.   #endif
  11445.   #ifdef UNIX
  11446.     #define close_pipe_out  close_ohandle
  11447.   #endif
  11448.  
  11449. LISPFUNN(make_pipe_output_stream,1)
  11450. # (MAKE-PIPE-OUTPUT-STREAM command)
  11451. # ruft eine Shell auf, die command ausfⁿhrt, wobei unsere Pipe in deren
  11452. # Standard-Input hinein geht.
  11453.   { # command ⁿberprⁿfen:
  11454.     var reg4 object command;
  11455.     funcall(L(string),1); # (STRING command)
  11456.     command = string_to_asciz(value1); # als ASCIZ-String
  11457.     pushSTACK(command);
  11458.    {var int handles[2]; # zwei Handles fⁿr die Pipe
  11459.     var reg3 int child;
  11460.     #ifdef EMUNIX
  11461.     { # Stackaufbau: command.
  11462.       begin_system_call();
  11463.      {var reg1 FILE* f = popen(TheAsciz(STACK_0),"w");
  11464.       if (f==NULL) { OS_error(); }
  11465.       child = f->pid;
  11466.       handles[1] = fileno(f);
  11467.       end_system_call();
  11468.     }}
  11469.     #endif
  11470.     #ifdef UNIX
  11471.     { # Als Shell nehmen wir immer die Kommando-Shell.
  11472.       # Stackaufbau: command.
  11473.       # command in den Stack kopieren:
  11474.       var reg5 uintL command_length = TheSstring(command)->length;
  11475.       var DYNAMIC_ARRAY(reg6,command_data,char,command_length);
  11476.       { var reg2 char* ptr1 = TheAsciz(command);
  11477.         var reg1 char* ptr2 = &command_data[0];
  11478.         dotimespL(command_length,command_length, { *ptr2++ = *ptr1++; } );
  11479.       }
  11480.       begin_system_call();
  11481.       if (!( pipe(handles) ==0))
  11482.         { FREE_DYNAMIC_ARRAY(command_data); OS_error(); }
  11483.       # Alles, was in handles[1] reingeschoben wird, kommt bei handles[0]
  11484.       # wieder raus. Wir werden dies so benutzen:
  11485.       #
  11486.       #        write            system            read
  11487.       # parent  ->   handles[1]   ->   handles[0]  ->  child
  11488.       #
  11489.       # einen neuen Proze▀ starten:
  11490.       if ((child = vfork()) ==0)
  11491.         # Dieses Programmstⁿck wird vom Child-Proze▀ ausgefⁿhrt:
  11492.         { if ( dup2(handles[0],stdin_handle) >=0) # Standard-Input umleiten
  11493.             if ( CLOSE(handles[0]) ==0) # Wir wollen nur ⁿber stdin_handle lesen
  11494.               if ( CLOSE(handles[1]) ==0) # Wir wollen auf die Pipe nicht schreiben
  11495.                 # (Mu▀ das dem Betriebssystem sagen, damit - wenn der Child
  11496.                 # die Pipe geleert hat - der Parent-Proze▀ und nicht etwa der
  11497.                 # Child-Proze▀ aufgerufen wird, um die Pipe zu wieder zu fⁿllen.)
  11498.                 { # Child-Proze▀ zum Hintergrundproze▀ machen:
  11499.                   SETSID(); # er bekommt eine eigene Process Group
  11500.                   execl(SHELL,            # Shell aufrufen
  11501.                         SHELL,            # =: argv[0]
  11502.                         "-c",             # =: argv[1]
  11503.                         &command_data[0], # =: argv[2]
  11504.                         NULL
  11505.                        );
  11506.                 }
  11507.           _exit(-1); # sollte dies mi▀lingen, Child-Proze▀ beenden
  11508.         }
  11509.       # Dieses Programmstⁿck wird wieder vom Aufrufer ausgefⁿhrt:
  11510.       if (child==-1)
  11511.         # Etwas ist mi▀lungen, entweder beim vfork oder beim execl.
  11512.         # In beiden FΣllen wurde errno gesetzt.
  11513.         { var int saved_errno = errno;
  11514.           CLOSE(handles[1]); CLOSE(handles[0]);
  11515.           FREE_DYNAMIC_ARRAY(command_data);
  11516.           errno = saved_errno; OS_error();
  11517.         }
  11518.       # Wir wollen auf die Pipe nur schreiben, nicht lesen:
  11519.       if (!( CLOSE(handles[0]) ==0))
  11520.         { var int saved_errno = errno;
  11521.           CLOSE(handles[1]);
  11522.           FREE_DYNAMIC_ARRAY(command_data);
  11523.           errno = saved_errno; OS_error();
  11524.         }
  11525.       # (Mu▀ das dem Betriebssystem sagen, damit - wenn der Parent-Proze▀
  11526.       # die Pipe gefⁿllt hat - der Child-Proze▀ und nicht etwa der
  11527.       # Parent-Proze▀ aufgerufen wird, um die Pipe wieder zu leeren.)
  11528.       end_system_call();
  11529.       FREE_DYNAMIC_ARRAY(command_data);
  11530.     }
  11531.     #endif
  11532.     # Stream allozieren:
  11533.     { var reg1 object stream = # neuer Stream, nur WRITE-CHAR erlaubt
  11534.         allocate_stream(strmflags_wr_ch_B,strmtype_pipe_out,strm_len+4);
  11535.       TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  11536.       TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  11537.       TheStream(stream)->strm_rd_ch = P(rd_ch_dummy);
  11538.       TheStream(stream)->strm_rd_ch_last = NIL;
  11539.       TheStream(stream)->strm_wr_ch = P(wr_ch_pipe_out);
  11540.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  11541.       #ifdef STRM_WR_SS
  11542.       TheStream(stream)->strm_wr_ss = P(wr_ss_pipe_out);
  11543.       #endif
  11544.       TheStream(stream)->strm_pipe_pid = fixnum(child); # Child-Pid
  11545.       TheStream(stream)->strm_pipe_out_handle = allocate_handle(handles[1]); # Write-Handle
  11546.       value1 = stream; mv_count=1; # stream als Wert
  11547.       skipSTACK(1);
  11548.   }}}
  11549.  
  11550. #ifdef PIPES2
  11551.  
  11552. # Bidirektionale Pipes
  11553. # ====================
  11554.  
  11555. LISPFUNN(make_pipe_io_stream,1)
  11556. # (MAKE-PIPE-IO-STREAM command)
  11557. # ruft eine Shell auf, die command ausfⁿhrt, wobei der Output unserer Pipe
  11558. # in deren Standard-Input hinein und deren Standard-Output wiederum in
  11559. # unsere Pipe hinein geht.
  11560.   { # command ⁿberprⁿfen:
  11561.     var reg4 object command;
  11562.     funcall(L(string),1); # (STRING command)
  11563.     command = string_to_asciz(value1); # als ASCIZ-String
  11564.     pushSTACK(command);
  11565.    {var int in_handles[2]; # zwei Handles fⁿr die Pipe zum Input-Stream
  11566.     var int out_handles[2]; # zwei Handles fⁿr die Pipe zum Output-Stream
  11567.     var reg3 int child;
  11568.     #ifdef EMUNIX
  11569.     { # Stackaufbau: command.
  11570.       var FILE* f_in;
  11571.       var FILE* f_out;
  11572.       begin_system_call();
  11573.       if (popenrw(TheAsciz(STACK_0),&f_in,&f_out) <0) { OS_error(); }
  11574.       child = f_in->pid; # = f_out->pid;
  11575.       in_handles[0] = fileno(f_in);
  11576.       out_handles[1] = fileno(f_out);
  11577.     }
  11578.     #endif
  11579.     #ifdef UNIX
  11580.     { # Als Shell nehmen wir immer die Kommando-Shell.
  11581.       # Stackaufbau: command.
  11582.       # command in den Stack kopieren:
  11583.       var reg5 uintL command_length = TheSstring(command)->length;
  11584.       var DYNAMIC_ARRAY(reg6,command_data,char,command_length);
  11585.       { var reg2 char* ptr1 = TheAsciz(command);
  11586.         var reg1 char* ptr2 = &command_data[0];
  11587.         dotimespL(command_length,command_length, { *ptr2++ = *ptr1++; } );
  11588.       }
  11589.       begin_system_call();
  11590.       # Pipes aufbauen:
  11591.       if (!( pipe(in_handles) ==0))
  11592.         { FREE_DYNAMIC_ARRAY(command_data); OS_error(); }
  11593.       if (!( pipe(out_handles) ==0))
  11594.         { var int saved_errno = errno;
  11595.           CLOSE(in_handles[1]); CLOSE(in_handles[0]);
  11596.           FREE_DYNAMIC_ARRAY(command_data);
  11597.           errno = saved_errno; OS_error();
  11598.         }
  11599.       # Alles, was in handles[1] reingeschoben wird, kommt bei handles[0]
  11600.       # wieder raus. Wir werden dies so benutzen:
  11601.       #
  11602.       #        write                system                read
  11603.       # parent  ->   out_handles[1]   ->   out_handles[0]  ->   child
  11604.       # parent  <-   in_handles[0]    <-   in_handles[1]   <-   child
  11605.       #        read                 system                write
  11606.       #
  11607.       # einen neuen Proze▀ starten:
  11608.       if ((child = vfork()) ==0)
  11609.         # Dieses Programmstⁿck wird vom Child-Proze▀ ausgefⁿhrt:
  11610.         { if ( dup2(out_handles[0],stdin_handle) >=0) # Standard-Input umleiten
  11611.             if ( dup2(in_handles[1],stdout_handle) >=0) # Standard-Output umleiten
  11612.               if ( CLOSE(out_handles[0]) ==0) # Wir wollen nur ⁿber stdin_handle lesen
  11613.                 if ( CLOSE(in_handles[1]) ==0) # Wir wollen nur ⁿber stdout_handle schreiben
  11614.                   if ( CLOSE(out_handles[1]) ==0) # Wir wollen auf die Pipe nicht schreiben
  11615.                     # (Mu▀ das dem Betriebssystem sagen, damit - wenn der Child
  11616.                     # die Pipe geleert hat - der Parent-Proze▀ und nicht etwa der
  11617.                     # Child-Proze▀ aufgerufen wird, um die Pipe zu wieder zu fⁿllen.)
  11618.                     if ( CLOSE(in_handles[0]) ==0) # Wir wollen von der Pipe nicht lesen
  11619.                       # (Mu▀ das dem Betriebssystem sagen, damit - wenn der Child
  11620.                       # die Pipe gefⁿllt hat - der Parent-Proze▀ und nicht etwa der
  11621.                       # Child-Proze▀ aufgerufen wird, um die Pipe zu leeren.)
  11622.                       { # Child-Proze▀ zum Hintergrundproze▀ machen:
  11623.                         SETSID(); # er bekommt eine eigene Process Group
  11624.                         execl(SHELL,            # Shell aufrufen
  11625.                               SHELL,            # =: argv[0]
  11626.                               "-c",             # =: argv[1]
  11627.                               &command_data[0], # =: argv[2]
  11628.                               NULL
  11629.                              );
  11630.                       }
  11631.           _exit(-1); # sollte dies mi▀lingen, Child-Proze▀ beenden
  11632.         }
  11633.       # Dieses Programmstⁿck wird wieder vom Aufrufer ausgefⁿhrt:
  11634.       if (child==-1)
  11635.         # Etwas ist mi▀lungen, entweder beim vfork oder beim execl.
  11636.         # In beiden FΣllen wurde errno gesetzt.
  11637.         { var int saved_errno = errno;
  11638.           CLOSE(in_handles[1]); CLOSE(in_handles[0]);
  11639.           CLOSE(out_handles[1]); CLOSE(out_handles[0]);
  11640.           FREE_DYNAMIC_ARRAY(command_data);
  11641.           errno = saved_errno; OS_error();
  11642.         }
  11643.       # Wir wollen auf die Pipe nur schreiben, nicht lesen:
  11644.       if (!( CLOSE(out_handles[0]) ==0))
  11645.         { var int saved_errno = errno;
  11646.           CLOSE(in_handles[1]); CLOSE(in_handles[0]);
  11647.           CLOSE(out_handles[1]);
  11648.           FREE_DYNAMIC_ARRAY(command_data);
  11649.           errno = saved_errno; OS_error();
  11650.         }
  11651.       # (Mu▀ das dem Betriebssystem sagen, damit - wenn der Parent-Proze▀
  11652.       # die Pipe gefⁿllt hat - der Child-Proze▀ und nicht etwa der
  11653.       # Parent-Proze▀ aufgerufen wird, um die Pipe wieder zu leeren.)
  11654.       # Wir wollen von der Pipe nur lesen, nicht schreiben:
  11655.       if (!( CLOSE(in_handles[1]) ==0))
  11656.         { var int saved_errno = errno;
  11657.           CLOSE(in_handles[0]);
  11658.           CLOSE(out_handles[1]);
  11659.           FREE_DYNAMIC_ARRAY(command_data);
  11660.           errno = saved_errno; OS_error();
  11661.         }
  11662.       # (Mu▀ das dem Betriebssystem sagen, damit - wenn der Parent-Proze▀
  11663.       # die Pipe geleert hat - der Child-Proze▀ und nicht etwa der
  11664.       # Parent-Proze▀ aufgerufen wird, um die Pipe wieder zu fⁿllen.)
  11665.       end_system_call();
  11666.       FREE_DYNAMIC_ARRAY(command_data);
  11667.     }
  11668.     #endif
  11669.     # Input-Stream allozieren:
  11670.     { var reg1 object stream = # neuer Stream, nur READ-CHAR erlaubt
  11671.         allocate_stream(strmflags_rd_ch_B,strmtype_pipe_in,strm_len+4);
  11672.       TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  11673.       TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  11674.       TheStream(stream)->strm_rd_ch = P(rd_ch_pipe_in);
  11675.       TheStream(stream)->strm_rd_ch_last = NIL;
  11676.       TheStream(stream)->strm_wr_ch = P(wr_ch_dummy);
  11677.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  11678.       #ifdef STRM_WR_SS
  11679.       TheStream(stream)->strm_wr_ss = P(wr_ss_dummy);
  11680.       #endif
  11681.       TheStream(stream)->strm_pipe_pid = fixnum(child); # Child-Pid
  11682.       TheStream(stream)->strm_pipe_in_handle = allocate_handle(in_handles[0]); # Read-Handle
  11683.       TheStream(stream)->strm_isatty = NIL;
  11684.       pushSTACK(stream);
  11685.     }
  11686.     # Output-Stream allozieren:
  11687.     { var reg1 object stream = # neuer Stream, nur WRITE-CHAR erlaubt
  11688.         allocate_stream(strmflags_wr_ch_B,strmtype_pipe_out,strm_len+4);
  11689.       TheStream(stream)->strm_rd_by = P(rd_by_dummy);
  11690.       TheStream(stream)->strm_wr_by = P(wr_by_dummy);
  11691.       TheStream(stream)->strm_rd_ch = P(rd_ch_dummy);
  11692.       TheStream(stream)->strm_rd_ch_last = NIL;
  11693.       TheStream(stream)->strm_wr_ch = P(wr_ch_pipe_out);
  11694.       TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  11695.       #ifdef STRM_WR_SS
  11696.       TheStream(stream)->strm_wr_ss = P(wr_ss_pipe_out);
  11697.       #endif
  11698.       TheStream(stream)->strm_pipe_pid = fixnum(child); # Child-Pid
  11699.       TheStream(stream)->strm_pipe_out_handle = allocate_handle(out_handles[1]); # Write-Handle
  11700.       pushSTACK(stream);
  11701.     }
  11702.     #ifdef EMUNIX
  11703.     # Beide Pipes miteinander verknⁿpfen, zum reibungslosen close:
  11704.     TheStream(STACK_1)->strm_pipe_in_other = STACK_0;
  11705.     TheStream(STACK_0)->strm_pipe_out_other = STACK_1;
  11706.     #endif
  11707.     # 3 Werte:
  11708.     # (make-two-way-stream input-stream output-stream), input-stream, output-stream.
  11709.     STACK_2 = make_twoway_stream(STACK_1,STACK_0);
  11710.     funcall(L(values),3);
  11711.   }}
  11712.  
  11713. #endif # PIPES2
  11714.  
  11715. #endif # PIPES
  11716.  
  11717.  
  11718. #ifdef SOCKETS
  11719.  
  11720. # Socket-Stream
  11721. # =============
  11722.  
  11723. # Verwendung: fⁿr X-Windows.
  11724.  
  11725. # ZusΣtzliche Komponenten:
  11726.   # define strm_socket_connect strm_other[3] # Liste (host display)
  11727.  
  11728. # READ-CHAR - Pseudofunktion fⁿr Socket-Streams:
  11729.   #define rd_ch_socket  rd_ch_handle
  11730.  
  11731. # Stellt fest, ob ein Socket-Stream ein Zeichen verfⁿgbar hat.
  11732. # listen_socket(stream)
  11733. # > stream : Socket-Stream
  11734. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  11735. #             -1 falls bei EOF angelangt,
  11736. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  11737. # kann GC ausl÷sen
  11738.   #define listen_socket  listen_handle
  11739.  
  11740. # WRITE-CHAR - Pseudofunktion fⁿr Socket-Streams:
  11741.   #define wr_ch_socket  wr_ch_handle
  11742.  
  11743. #ifdef STRM_WR_SS
  11744. # WRITE-SIMPLE-STRING - Pseudofunktion fⁿr Socket-Streams:
  11745.   #define wr_ss_socket  wr_ss_handle
  11746. #endif
  11747.  
  11748. # READ-BYTE - Pseudofunktion fⁿr Socket-Streams:
  11749.   #define rd_by_socket  rd_by_handle
  11750.  
  11751. # WRITE-BYTE - Pseudofunktion fⁿr Socket-Streams:
  11752.   #define wr_by_socket  wr_by_handle
  11753.  
  11754. # Schlie▀t einen Socket-Stream.
  11755. # close_socket(stream);
  11756. # > stream : Socket-Stream
  11757.   #define close_socket  close_ihandle
  11758.  
  11759. extern int connect_to_server (char* host, int display); # ein Stⁿck X-Source...
  11760.  
  11761. LISPFUNN(make_socket_stream,2)
  11762. # (SYS::MAKE-SOCKET-STREAM host display)
  11763. # liefert einen Socket-Stream fⁿr X-Windows oder NIL.
  11764.   { if (!mstringp(STACK_1))
  11765.       { pushSTACK(STACK_1); # Wert fⁿr Slot DATUM von TYPE-ERROR
  11766.         pushSTACK(S(string)); # Wert fⁿr Slot EXPECTED-TYPE von TYPE-ERROR
  11767.         pushSTACK(STACK_(1+2));
  11768.         fehler(type_error,
  11769.                DEUTSCH ? "Host mu▀ ein String sein, nicht ~" :
  11770.                ENGLISH ? "host should be string, not ~" :
  11771.                FRANCAIS ? "L'h⌠te devrait Ωtre un chaεne et non ~" :
  11772.                ""
  11773.               );
  11774.       }
  11775.     if (!mposfixnump(STACK_0))
  11776.       { pushSTACK(STACK_0); # Wert fⁿr Slot DATUM von TYPE-ERROR
  11777.         pushSTACK(O(type_posfixnum)); # Wert fⁿr Slot EXPECTED-TYPE von TYPE-ERROR
  11778.         pushSTACK(STACK_(0+2));
  11779.         fehler(type_error,
  11780.                DEUTSCH ? "Display sollte ein Fixnum >=0 sein, nicht ~" :
  11781.                ENGLISH ? "display should be a nonnegative fixnum, not ~" :
  11782.                FRANCAIS ? "Le ½display╗ doit Ωtre de type FIXNUM >= 0 et non ~" :
  11783.                ""
  11784.               );
  11785.      }
  11786.   begin_system_call();
  11787.   {var reg2 int handle = connect_to_server(TheAsciz(string_to_asciz(STACK_1)),posfixnum_to_L(STACK_0));
  11788.    end_system_call();
  11789.    if (handle < 0) { OS_error(); }
  11790.    # Liste bilden:
  11791.    {var reg1 object list = listof(2); pushSTACK(list); }
  11792.    # Stream allozieren:
  11793.    {var reg1 object stream = # neuer Stream, nur READ-CHAR und WRITE-CHAR erlaubt
  11794.       allocate_stream(strmflags_ch_B,strmtype_socket,strm_len+4);
  11795.     TheStream(stream)->strm_rd_by = P(rd_by_socket);
  11796.     TheStream(stream)->strm_wr_by = P(wr_by_socket);
  11797.     TheStream(stream)->strm_rd_ch = P(rd_ch_socket);
  11798.     TheStream(stream)->strm_rd_ch_last = NIL;
  11799.     TheStream(stream)->strm_wr_ch = P(wr_ch_socket);
  11800.     TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  11801.     #ifdef STRM_WR_SS
  11802.     TheStream(stream)->strm_wr_ss = P(wr_ss_socket);
  11803.     #endif
  11804.     TheStream(stream)->strm_socket_connect = popSTACK(); # zweielementige Liste
  11805.     TheStream(stream)->strm_ihandle =
  11806.     TheStream(stream)->strm_ohandle = allocate_handle(handle); # Handle eintragen
  11807.     TheStream(stream)->strm_isatty = NIL;
  11808.     value1 = stream; mv_count=1; # stream als Wert
  11809.   }}}
  11810.  
  11811. # Die beiden folgenden Funktionen sollten
  11812. # 1. nicht nur auf Handle- und Socket-Streams, sondern auch auf Synonym-
  11813. #    und Concatenated-Streams funktionieren, idealerweise auch auf File-Streams.
  11814. # 2. das rd_ch_lastchar ebenso verΣndern wie READ-BYTE.
  11815. # 3. auch nicht-simple Byte-Vektoren akzeptieren.
  11816. # Fⁿr CLX reicht aber die vorliegende Implementation.
  11817.  
  11818. # (SYS::READ-N-BYTES stream vector start count)
  11819. # liest n Bytes auf einmal.
  11820. # Quelle:
  11821. #   stream: Handle- oder Socket-Stream
  11822. # Ziel: (aref vector start), ..., (aref vector (+ start (- count 1))), wobei
  11823. #   vector: semi-simpler 8Bit-Byte-Vektor
  11824. #   start: Start-Index in den Vektor
  11825. #   count: Anzahl der Bytes
  11826.  
  11827. # (SYS::WRITE-N-BYTES stream vector start count)
  11828. # schreibt n Bytes auf einmal.
  11829. # Quelle: (aref vector start), ..., (aref vector (+ start (- count 1))), wobei
  11830. #   vector: semi-simpler 8Bit-Byte-Vektor
  11831. #   start: Start-Index in den Vektor
  11832. #   count: Anzahl der Bytes
  11833. # Ziel:
  11834. #   stream: Handle- oder Socket-Stream
  11835.  
  11836. # Argumentⁿberprⁿfungen:
  11837. # Liefert den Index in *index_, den count in *count_, den Datenvektor im
  11838. # Stack statt des Vektors, und rΣumt den Stack um 2 auf.
  11839.   local void test_n_bytes_args (uintL* index_, uintL* count_);
  11840.   local void test_n_bytes_args(index_,count_)
  11841.     var reg3 uintL* index_;
  11842.     var reg2 uintL* count_;
  11843.     { if (!mstreamp(STACK_3)) { fehler_stream(STACK_3); }
  11844.       {var reg1 object stream = STACK_3;
  11845.        if (!(   eq(TheStream(stream)->strm_rd_by,P(rd_by_handle))
  11846.              && eq(TheStream(stream)->strm_wr_by,P(wr_by_handle))
  11847.           ) )
  11848.          { pushSTACK(stream);
  11849.            pushSTACK(TheSubr(subr_self)->name);
  11850.            fehler(error,
  11851.                   DEUTSCH ? "~: Stream mu▀ ein Handle-Stream sein, nicht ~" :
  11852.                   ENGLISH ? "~: stream must be a handle-stream, not ~" :
  11853.                   FRANCAIS ? "~ : Le stream doit Ωtre un ½handle-stream╗ et non ~" :
  11854.                   ""
  11855.                  );
  11856.       }  }
  11857.       {var reg1 object vector = STACK_2;
  11858.        if (!(((typecode(vector)&~imm_array_mask) == bvector_type) # Bit/Byte-Vektor?
  11859.              && ((TheArray(vector)->flags & arrayflags_atype_mask) == Atype_8Bit) # 8Bit
  11860.           ) )
  11861.          { pushSTACK(vector); # Wert fⁿr Slot DATUM von TYPE-ERROR
  11862.            pushSTACK(O(type_uint8_vector)); # Wert fⁿr Slot EXPECTED-TYPE von TYPE-ERROR
  11863.            pushSTACK(vector);
  11864.            pushSTACK(TheSubr(subr_self)->name);
  11865.            fehler(type_error,
  11866.                   DEUTSCH ? "~: Argument ~ sollte ein Vektor vom Typ (ARRAY (UNSIGNED-BYTE 8) (*)) sein." :
  11867.                   ENGLISH ? "~: argument ~ should be a vector of type (ARRAY (UNSIGNED-BYTE 8) (*))" :
  11868.                   FRANCAIS ? "~ : l'argument ~ doit Ωtre un vecteur de type (ARRAY (UNSIGNED-BYTTE 8) (*))." :
  11869.                   ""
  11870.                  );
  11871.          }
  11872.        if (!mposfixnump(STACK_0)) { fehler_bad_lpos(); }
  11873.        *count_ = posfixnum_to_L(popSTACK());
  11874.        if (!mposfixnump(STACK_0)) { fehler_bad_lpos(); }
  11875.        *index_ = posfixnum_to_L(popSTACK());
  11876.        STACK_0 = array1_displace_check(vector,*count_,index_);
  11877.     } }
  11878.  
  11879. LISPFUNN(read_n_bytes,4)
  11880.   { var uintL startindex;
  11881.     var uintL totalcount;
  11882.     test_n_bytes_args(&startindex,&totalcount);
  11883.     if (!(totalcount==0))
  11884.       { var reg4 Handle handle = TheHandle(TheStream(STACK_1)->strm_ihandle);
  11885.         var reg2 uintL remaining = totalcount;
  11886.         restart_it:
  11887.        {var reg1 uintB* ptr = &TheSbvector(TheArray(STACK_0)->data)->data[startindex];
  11888.         begin_system_call();
  11889.         loop
  11890.           { var reg3 int ergebnis = read(handle,ptr,remaining);
  11891.             if (ergebnis<0)
  11892.               { if (errno==EINTR) # Unterbrechung (evtl. durch Ctrl-C) ?
  11893.                   { end_system_call();
  11894.                     interruptp({ pushSTACK(S(read_n_bytes)); tast_break(); }); # Break-Schleife aufrufen
  11895.                     goto restart_it;
  11896.                   }
  11897.                 OS_error(); # Error melden
  11898.               }
  11899.             ptr += ergebnis; startindex += ergebnis; remaining -= ergebnis;
  11900.             if (remaining==0) break; # fertig?
  11901.           }
  11902.         end_system_call();
  11903.       }}
  11904.     skipSTACK(2);
  11905.     value1 = T; mv_count=1; # Wert T
  11906.   }
  11907.  
  11908. LISPFUNN(write_n_bytes,4)
  11909.   { var uintL startindex;
  11910.     var uintL totalcount;
  11911.     test_n_bytes_args(&startindex,&totalcount);
  11912.     if (!(totalcount==0))
  11913.       { var reg4 Handle handle = TheHandle(TheStream(STACK_1)->strm_ihandle);
  11914.         var reg2 uintL remaining = totalcount;
  11915.         restart_it:
  11916.        {var reg1 uintB* ptr = &TheSbvector(TheArray(STACK_0)->data)->data[startindex];
  11917.         begin_system_call();
  11918.         loop
  11919.           { var reg3 int ergebnis = write(handle,ptr,remaining);
  11920.             if (ergebnis<0)
  11921.               { if (errno==EINTR) # Unterbrechung (evtl. durch Ctrl-C) ?
  11922.                   { end_system_call();
  11923.                     interruptp({ pushSTACK(S(write_n_bytes)); tast_break(); }); # Break-Schleife aufrufen
  11924.                     goto restart_it;
  11925.                   }
  11926.                 OS_error(); # Error melden
  11927.               }
  11928.             if (ergebnis==0) # nicht erfolgreich?
  11929.               { fehler_unwritable(S(write_n_bytes),STACK_1); }
  11930.             ptr += ergebnis; startindex += ergebnis; remaining -= ergebnis;
  11931.             if (remaining==0) break; # fertig?
  11932.           }
  11933.         end_system_call();
  11934.       }}
  11935.     skipSTACK(2);
  11936.     value1 = T; mv_count=1; # Wert T
  11937.   }
  11938.  
  11939. #endif
  11940.  
  11941.  
  11942. #ifdef GENERIC_STREAMS
  11943.  
  11944. # Generic Streams
  11945. # ===============
  11946.  
  11947.   # Contains a "controller object".
  11948.   # define strm_controller_object  strm_other[0]  # see lispbibl.d
  11949.  
  11950.   # The function GENERIC-STREAM-CONTROLLER will return some
  11951.   # object c associated with the stream s.
  11952.  
  11953.   #   (GENERIC-STREAM-READ-CHAR c)                      --> character or NIL
  11954.   #   (GENERIC-STREAM-LISTEN c)                         --> {0,1,-1}
  11955.   #   (GENERIC-STREAM-CLEAR-INPUT c)                    --> {T,NIL}
  11956.   #   (GENERIC-STREAM-WRITE-CHAR c ch)                  -->
  11957.   #   (GENERIC-STREAM-WRITE-STRING c string start len)  -->
  11958.   #   (GENERIC-STREAM-FINISH-OUTPUT c)                  -->
  11959.   #   (GENERIC-STREAM-FORCE-OUTPUT c)                   -->
  11960.   #   (GENERIC-STREAM-CLEAR-OUTPUT c)                   -->
  11961.   #   (GENERIC-STREAM-READ-BYTE c)                      --> integer or NIL
  11962.   #   (GENERIC-STREAM-WRITE-BYTE c i)                   -->
  11963.   #   (GENERIC-STREAM-CLOSE c)                          -->
  11964.  
  11965.   # (READ-CHAR s) ==
  11966.   # (GENERIC-STREAM-READ-CHAR c)
  11967.   local object rd_ch_generic (object* stream_);
  11968.   local object rd_ch_generic(stream_)
  11969.     var reg1 object* stream_;
  11970.     { pushSTACK(*stream_); funcall(L(generic_stream_controller),1);
  11971.       pushSTACK(value1); funcall(S(generic_stream_rdch),1);
  11972.       return nullp(value1) ? eof_value : value1;
  11973.     }
  11974.  
  11975.   # (LISTEN s) ==
  11976.   # (GENERIC-STREAM-LISTEN c)
  11977.   local signean listen_generic (object stream);
  11978.   local signean listen_generic(stream)
  11979.     var reg1 object stream;
  11980.     { pushSTACK(stream); funcall(L(generic_stream_controller),1);
  11981.       pushSTACK(value1); funcall(S(generic_stream_listen),1);
  11982.       return I_to_L(value1);
  11983.     }
  11984.  
  11985.   # (CLEAR-INPUT s) ==
  11986.   # (GENERIC-STREAM-CLEAR-INPUT c)
  11987.   local boolean clear_input_generic (object stream);
  11988.   local boolean clear_input_generic(stream)
  11989.     var reg1 object stream;
  11990.     { pushSTACK(stream); funcall(L(generic_stream_controller),1);
  11991.       pushSTACK(value1); funcall(S(generic_stream_clear_input),1);
  11992.       return !nullp(value1);
  11993.     }
  11994.  
  11995.   # (WRITE-CHAR ch s) ==
  11996.   # (GENERIC-STREAM-WRITE-CHAR c ch)
  11997.   local void wr_ch_generic (object* stream_, object ch);
  11998.   local void wr_ch_generic(stream_,ch)
  11999.     var reg1 object* stream_;
  12000.     var reg2 object ch;
  12001.     { # ch is a character, need not save it
  12002.       pushSTACK(*stream_); funcall(L(generic_stream_controller),1);
  12003.       pushSTACK(value1); pushSTACK(ch); funcall(S(generic_stream_wrch),2);
  12004.     }
  12005.  
  12006. #ifdef STRM_WR_SS
  12007.   # (WRITE-SIMPLE-STRING s string start len) ==
  12008.   # (GENERIC-STREAM-WRITE-STRING c string start len)
  12009.   local void wr_ss_generic (object* stream_, object string, uintL start, uintL len);
  12010.   local void wr_ss_generic(stream_,string,start,len)
  12011.     var reg2 object* stream_;
  12012.     var reg1 object string;
  12013.     var reg3 uintL start;
  12014.     var reg4 uintL len;
  12015.     { pushSTACK(string); # save string
  12016.       pushSTACK(*stream_); funcall(L(generic_stream_controller),1);
  12017.       pushSTACK(value1); pushSTACK(STACK_(0+1));
  12018.       pushSTACK(UL_to_I(start)); pushSTACK(UL_to_I(len));
  12019.       funcall(S(generic_stream_wrss),4);
  12020.       string = popSTACK();
  12021.       wr_ss_lpos(*stream_,&TheSstring(string)->data[start],len);
  12022.     }
  12023. #endif
  12024.  
  12025.   # (FINISH-OUTPUT s) ==
  12026.   # (GENERIC-STREAM-FINISH-OUTPUT c)
  12027.   local void finish_output_generic (object stream);
  12028.   local void finish_output_generic(stream)
  12029.     var reg1 object stream;
  12030.     { pushSTACK(stream); funcall(L(generic_stream_controller),1);
  12031.       pushSTACK(value1); funcall(S(generic_stream_finish_output),1);
  12032.     }
  12033.  
  12034.   # (FORCE-OUTPUT s) ==
  12035.   # (GENERIC-STREAM-FORCE-OUTPUT c)
  12036.   local void force_output_generic (object stream);
  12037.   local void force_output_generic(stream)
  12038.     var reg1 object stream;
  12039.     { pushSTACK(stream); funcall(L(generic_stream_controller),1);
  12040.       pushSTACK(value1); funcall(S(generic_stream_force_output),1);
  12041.     }
  12042.  
  12043.   # (CLEAR-OUTPUT s) ==
  12044.   # (GENERIC-STREAM-CLEAR-OUTPUT c)
  12045.   local void clear_output_generic (object stream);
  12046.   local void clear_output_generic(stream)
  12047.     var reg1 object stream;
  12048.     { pushSTACK(stream); funcall(L(generic_stream_controller),1);
  12049.       pushSTACK(value1); funcall(S(generic_stream_clear_output),1);
  12050.     }
  12051.  
  12052.   # (READ-BYTE s) ==
  12053.   # (GENERIC-STREAM-READ-BYTE c)
  12054.   local object rd_by_generic (object stream);
  12055.   local object rd_by_generic(stream)
  12056.     var reg1 object stream;
  12057.     { pushSTACK(stream); funcall(L(generic_stream_controller),1);
  12058.       pushSTACK(value1); funcall(S(generic_stream_rdby),1);
  12059.       return (nullp(value1) ? eof_value : value1);
  12060.     }
  12061.  
  12062.   # (WRITE-BYTE s i) ==
  12063.   # (GENERIC-STREAM-WRITE-BYTE c i)
  12064.   local void wr_by_generic (object stream, object obj);
  12065.   local void wr_by_generic(stream,obj)
  12066.     var reg2 object stream;
  12067.     var reg1 object obj;
  12068.     { pushSTACK(obj); # save obj
  12069.       pushSTACK(stream); funcall(L(generic_stream_controller),1);
  12070.       obj = STACK_0;
  12071.       STACK_0 = value1; pushSTACK(obj); funcall(S(generic_stream_wrby),2);
  12072.     }
  12073.  
  12074.   # (CLOSE s) ==
  12075.   # (GENERIC-STREAM-CLOSE c)
  12076.   local void close_generic(stream)
  12077.     var reg1 object stream;
  12078.     { pushSTACK(stream); funcall(L(generic_stream_controller),1);
  12079.       pushSTACK(value1); funcall(S(generic_stream_close),1);
  12080.     }
  12081.  
  12082. LISPFUNN(generic_stream_controller,1)
  12083.   { var reg1 object stream = popSTACK();
  12084.     if (!streamp(stream)) { fehler_stream(stream); }
  12085.     if (!(   eq(TheStream(stream)->strm_rd_by,P(rd_by_generic))
  12086.           && eq(TheStream(stream)->strm_wr_by,P(wr_by_generic))))
  12087.       { pushSTACK(stream);
  12088.         pushSTACK(TheSubr(subr_self)->name);
  12089.         fehler(error,
  12090.                DEUTSCH ? "~: Stream mu▀ ein Generic-Stream sein, nicht ~" :
  12091.                ENGLISH ? "~: stream must be a generic-stream, not ~" :
  12092.                FRANCAIS ? "~ : Le stream doit Ωtre un ½generic-stream╗ et non ~" :
  12093.                ""
  12094.               );
  12095.       }
  12096.     value1=TheStream(stream)->strm_controller_object;
  12097.     mv_count=1;
  12098.   }
  12099.  
  12100. LISPFUNN(make_generic_stream,1)
  12101.   { var reg1 object stream =
  12102.       allocate_stream(strmflags_open_B,strmtype_generic,strm_len+1);
  12103.     TheStream(stream)->strm_rd_by = P(rd_by_generic);
  12104.     TheStream(stream)->strm_wr_by = P(wr_by_generic);
  12105.     TheStream(stream)->strm_rd_ch = P(rd_ch_generic);
  12106.     TheStream(stream)->strm_rd_ch_last = NIL;
  12107.     TheStream(stream)->strm_wr_ch = P(wr_ch_generic);
  12108.     TheStream(stream)->strm_wr_ch_lpos = Fixnum_0;
  12109.     #ifdef STRM_WR_SS
  12110.     TheStream(stream)->strm_wr_ss = P(wr_ss_generic);
  12111.     #endif
  12112.     TheStream(stream)->strm_controller_object = popSTACK();
  12113.     value1 = stream; mv_count=1; # stream als Wert
  12114.   }
  12115.  
  12116. LISPFUNN(generic_stream_p,1)
  12117.   { var reg1 object stream = popSTACK();
  12118.     if (!streamp(stream)) { fehler_stream(stream); }
  12119.     if ((eq(TheStream(stream)->strm_rd_by,P(rd_by_generic))
  12120.       && eq(TheStream(stream)->strm_wr_by,P(wr_by_generic))))
  12121.       { value1 = T; mv_count=1; }
  12122.     else
  12123.       { value1 = NIL; mv_count=1; }
  12124.   }
  12125.  
  12126. #endif
  12127.  
  12128.  
  12129. # Streams allgemein
  12130. # =================
  12131.  
  12132. # UP: Initialisiert die Stream-Variablen.
  12133. # init_streamvars();
  12134. # kann GC ausl÷sen
  12135.   global void init_streamvars (void);
  12136.   global void init_streamvars()
  12137.     {
  12138.      #ifdef KEYBOARD
  12139.      #ifdef ATARI
  12140.      keytables = *(KEYTAB*)XBIOS_GetKeyTbl(); # Keyboard-Stream initialisieren
  12141.      #endif
  12142.      {var reg1 object stream = make_keyboard_stream();
  12143.       define_variable(S(keyboard_input),stream);   # *KEYBOARD-INPUT*
  12144.      }
  12145.      #endif
  12146.      #ifdef GNU_READLINE
  12147.      rl_readline_name = "Clisp";
  12148.      rl_attempted_completion_function = &lisp_completion;
  12149.      rl_completion_entry_function = &lisp_completion_more;
  12150.      #endif
  12151.      {var reg1 object stream = make_terminal_stream();
  12152.       define_variable(S(terminal_io),stream);      # *TERMINAL-IO*
  12153.      }
  12154.      {var reg1 object stream = make_synonym_stream(S(terminal_io)); # Synonym-Stream auf *TERMINAL-IO*
  12155.       define_variable(S(query_io),stream);         # *QUERY-IO*
  12156.       define_variable(S(debug_io),stream);         # *DEBUG-IO*
  12157.       define_variable(S(standard_input),stream);   # *STANDARD-INPUT*
  12158.       define_variable(S(standard_output),stream);  # *STANDARD-OUTPUT*
  12159.       define_variable(S(error_output),stream);     # *ERROR-OUTPUT*
  12160.       define_variable(S(trace_output),stream);     # *TRACE-OUTPUT*
  12161.      }
  12162.      #ifdef PRINTER_ATARI
  12163.      # *PRINTER-OUTPUT* mit Printer-Stream initialisieren:
  12164.      define_variable(S(printer_output),make_printer_stream());
  12165.      #endif
  12166.     }
  12167.  
  12168. # Liefert Fehlermeldung, wenn der Wert des Symbols sym kein Stream ist.
  12169.   local void fehler_value_stream(sym)
  12170.     var reg1 object sym;
  12171.     { # Vor der Fehlermeldung eventuell noch reparieren
  12172.       # (so wie bei init_streamvars bzw. init_pathnames initialisiert):
  12173.       var reg2 object stream;
  12174.       pushSTACK(sym); # sym retten
  12175.       #ifdef KEYBOARD
  12176.       if (eq(sym,S(keyboard_input)))
  12177.         # Keyboard-Stream als Default
  12178.         { stream = make_keyboard_stream(); }
  12179.       else
  12180.       #endif
  12181.       if (eq(sym,S(terminal_io)))
  12182.         # Terminal-Stream als Default
  12183.         { stream = make_terminal_stream(); }
  12184.       elif (eq(sym,S(query_io)) || eq(sym,S(debug_io)) ||
  12185.             eq(sym,S(standard_input)) || eq(sym,S(standard_output)) ||
  12186.             eq(sym,S(error_output)) || eq(sym,S(trace_output))
  12187.            )
  12188.         # Synonym-Stream auf *TERMINAL-IO* als Default
  12189.         { stream = make_synonym_stream(S(terminal_io)); }
  12190.       #ifdef PRINTER_ATARI
  12191.       elif (eq(sym,S(printer_output)))
  12192.         # Printer-Stream als Default
  12193.         { stream = make_printer_stream(); }
  12194.       #endif
  12195.       else
  12196.         # sonstiges Symbol, nicht reparierbar -> sofort Fehlermeldung:
  12197.         { pushSTACK(Symbol_value(sym)); # Wert fⁿr Slot DATUM von TYPE-ERROR
  12198.           pushSTACK(S(stream)); # Wert fⁿr Slot EXPECTED-TYPE von TYPE-ERROR
  12199.           pushSTACK(Symbol_value(sym)); # Variablenwert
  12200.           pushSTACK(sym); # Variable
  12201.           fehler(type_error,
  12202.                  DEUTSCH ? "Der Wert von ~ ist kein Stream: ~" :
  12203.                  ENGLISH ? "The value of ~ is not a stream: ~" :
  12204.                  FRANCAIS ? "La valeur de ~ n'est pas de type STREAM : ~" :
  12205.                  ""
  12206.                 );
  12207.         }
  12208.       sym = popSTACK();
  12209.       # Reparatur beendet: stream ist der neue Wert von sym.
  12210.      {var reg3 object oldvalue = Symbol_value(sym);
  12211.       Symbol_value(sym) = stream;
  12212.       pushSTACK(oldvalue); # Wert fⁿr Slot DATUM von TYPE-ERROR
  12213.       pushSTACK(S(stream)); # Wert fⁿr Slot EXPECTED-TYPE von TYPE-ERROR
  12214.       pushSTACK(stream); # neuer Variablenwert
  12215.       pushSTACK(oldvalue); # alter Variablenwert
  12216.       pushSTACK(sym); # Variable
  12217.       fehler(type_error,
  12218.              DEUTSCH ? "Der Wert von ~ war kein Stream: ~. Wurde zurⁿckgesetzt auf ~." :
  12219.              ENGLISH ? "The value of ~ was not a stream: ~. It has been changed to ~." :
  12220.              FRANCAIS ? "La valeur de ~ n'Θtait pas de type STREAM : ~. ChangΘ en ~." :
  12221.              ""
  12222.             );
  12223.     }}
  12224.  
  12225. #ifdef GNU_READLINE
  12226.  
  12227. # Hilfsfunktionen fⁿr die GNU readline Library:
  12228.  
  12229. nonreturning_function(local, rl_memory_abort, (void));
  12230. local void rl_memory_abort()
  12231.   { # Wenn fⁿr die Readline-Library der Speicher nicht mehr reicht,
  12232.     # schmei▀en wir sie raus und ersetzen den Terminal-Stream durch einen,
  12233.     # der ohne sie auskommt.
  12234.     rl_deprep_terminal(); # alle komischen ioctl()s rⁿckgΣngig machen
  12235.     begin_callback(); # STACK wieder auf einen vernⁿnftigen Wert setzen
  12236.     rl_present_p = FALSE;
  12237.     Symbol_value(S(terminal_io)) = make_terminal_stream();
  12238.     fehler(storage_condition,
  12239.            DEUTSCH ? "Readline-Library: kein freier Speicher mehr da." :
  12240.            ENGLISH ? "readline library: out of memory." :
  12241.            FRANCAIS ? "BibliothΦque readline: mΘmoire ΘpuisΘe." :
  12242.            ""
  12243.           );
  12244.   }
  12245.  
  12246. global char* xmalloc (int count);
  12247. global char* xmalloc(count)
  12248.   var reg2 int count;
  12249.   { var reg1 char* tmp = (char*)malloc(count);
  12250.     if (tmp) return tmp; else rl_memory_abort();
  12251.   }
  12252.  
  12253. global char* xrealloc (char* ptr, int count);
  12254. global char* xrealloc(ptr,count)
  12255.   var reg2 char* ptr;
  12256.   var reg2 int count;
  12257.   { var reg1 char* tmp = (ptr==NULL ? (char*)malloc(count) : (char*)realloc(ptr,count));
  12258.     if (tmp) return tmp; else rl_memory_abort();
  12259.   }
  12260.  
  12261. #endif
  12262.  
  12263. LISPFUNN(input_stream_p,1)
  12264. # (INPUT-STREAM-P stream), CLTL S. 332
  12265.   { var reg1 object stream = popSTACK();
  12266.     if (!streamp(stream)) { fehler_stream(stream); }
  12267.     if (TheStream(stream)->strmflags & strmflags_rd_B) # READ-BYTE oder READ-CHAR erlaubt ?
  12268.       { value1 = T; mv_count=1; } # Wert T
  12269.       else
  12270.       { value1 = NIL; mv_count=1; } # Wert NIL
  12271.   }
  12272.  
  12273. LISPFUNN(output_stream_p,1)
  12274. # (OUTPUT-STREAM-P stream), CLTL S. 332
  12275.   { var reg1 object stream = popSTACK();
  12276.     if (!streamp(stream)) { fehler_stream(stream); }
  12277.     if (TheStream(stream)->strmflags & strmflags_wr_B) # WRITE-BYTE oder WRITE-CHAR erlaubt ?
  12278.       { value1 = T; mv_count=1; } # Wert T
  12279.       else
  12280.       { value1 = NIL; mv_count=1; } # Wert NIL
  12281.   }
  12282.  
  12283. LISPFUNN(stream_element_type,1)
  12284. # (STREAM-ELEMENT-TYPE stream), CLTL S. 332
  12285. # liefert NIL (fⁿr geschlossene Streams) oder CHARACTER oder INTEGER oder T
  12286. # oder (spezieller) STRING-CHAR oder (UNSIGNED-BYTE n) oder (SIGNED-BYTE n).
  12287.   { var reg1 object stream = popSTACK();
  12288.     if (!streamp(stream)) { fehler_stream(stream); }
  12289.    {var reg2 object eltype;
  12290.     if ((TheStream(stream)->strmflags & strmflags_open_B) == 0)
  12291.       # Stream geschlossen
  12292.       { eltype = NIL; }
  12293.       else
  12294.       # Stream offen
  12295.       { switch (TheStream(stream)->strmtype)
  12296.           { # erst die Streamtypen mit eingeschrΣnkten Element-Typen:
  12297.             #ifdef KEYBOARD
  12298.             case strmtype_keyboard:
  12299.             #endif
  12300.             case strmtype_ch_file:
  12301.               # CHARACTER
  12302.               eltype = S(character); break;
  12303.             case strmtype_terminal:
  12304.             case strmtype_sch_file:
  12305.             case strmtype_str_in:
  12306.             case strmtype_str_out:
  12307.             case strmtype_str_push:
  12308.             case strmtype_pphelp:
  12309.             case strmtype_buff_in:
  12310.             case strmtype_buff_out:
  12311.             #ifdef SCREEN
  12312.             case strmtype_window:
  12313.             #endif
  12314.             #ifdef PRINTER
  12315.             case strmtype_printer:
  12316.             #endif
  12317.             #ifdef PIPES
  12318.             case strmtype_pipe_in:
  12319.             case strmtype_pipe_out:
  12320.             #endif
  12321.               # STRING-CHAR
  12322.               eltype = S(string_char); break;
  12323.             case strmtype_iu_file:
  12324.               # (UNSIGNED-BYTE bitsize)
  12325.               pushSTACK(S(unsigned_byte));
  12326.               pushSTACK(TheStream(stream)->strm_file_bitsize);
  12327.               eltype = listof(2);
  12328.               break;
  12329.             case strmtype_is_file:
  12330.               # (SIGNED-BYTE bitsize)
  12331.               pushSTACK(S(signed_byte));
  12332.               pushSTACK(TheStream(stream)->strm_file_bitsize);
  12333.               eltype = listof(2);
  12334.               break;
  12335.             # dann die allgemeinen Streams:
  12336.             #ifdef HANDLES
  12337.             case strmtype_handle:
  12338.             #endif
  12339.             #ifdef GENERIC_STREAMS
  12340.             case strmtype_generic:
  12341.             #endif
  12342.             default:
  12343.               { var reg1 uintB flags = TheStream(stream)->strmflags;
  12344.                 if (flags & strmflags_by_B)
  12345.                   { if (flags & strmflags_ch_B)
  12346.                       # (OR CHARACTER INTEGER)
  12347.                       { pushSTACK(S(or)); pushSTACK(S(character)); pushSTACK(S(integer));
  12348.                         eltype = listof(3);
  12349.                       }
  12350.                       else
  12351.                       # INTEGER
  12352.                       { eltype = S(integer); }
  12353.                   }
  12354.                   else
  12355.                   { if (flags & strmflags_ch_B)
  12356.                       # CHARACTER
  12357.                       { eltype = S(character); }
  12358.                       else
  12359.                       # NIL
  12360.                       { eltype = NIL; }
  12361.                   }
  12362.                 break;
  12363.               }
  12364.       }   }
  12365.     value1 = eltype; mv_count=1;
  12366.   }}
  12367.  
  12368. # UP: Stellt fest, ob ein Stream "interaktiv" ist, d.h. ob Input vom Stream
  12369. # vermutlich von einem vorher ausgegebenen Prompt abhΣngen wird.
  12370. # interactive_stream_p(stream)
  12371. # > stream: Stream
  12372.   local boolean interactive_stream_p (object stream);
  12373.   local boolean interactive_stream_p(stream)
  12374.     var reg1 object stream;
  12375.     { start:
  12376.       if ((TheStream(stream)->strmflags & strmflags_rd_B) == 0)
  12377.         # Stream fⁿr Input geschlossen
  12378.         { return FALSE; }
  12379.       # Stream offen
  12380.       switch (TheStream(stream)->strmtype)
  12381.         {
  12382.           #if !defined(WINDOWS) || defined(HANDLES)
  12383.           #ifndef WINDOWS
  12384.           case strmtype_terminal:
  12385.           #endif
  12386.           #ifdef HANDLES
  12387.           case strmtype_handle:
  12388.           #endif
  12389.             #if defined(UNIX) || defined(MSDOS) || defined(AMIGAOS) || defined(RISCOS)
  12390.             if (nullp(TheStream(stream)->strm_isatty))
  12391.               # RegulΣre Files sind sicher nicht interaktiv.
  12392.               { var reg2 Handle handle = TheHandle(TheStream(stream)->strm_ihandle);
  12393.                 #if defined(UNIX) || defined(MSDOS) || defined(RISCOS)
  12394.                 var struct stat statbuf;
  12395.                 begin_system_call();
  12396.                 if (!( fstat(handle,&statbuf) ==0)) { OS_error(); }
  12397.                 end_system_call();
  12398.                 if (S_ISREG(statbuf.st_mode))
  12399.                 #endif
  12400.                 #ifdef AMIGAOS
  12401.                 var reg3 LONG interactivep;
  12402.                 begin_system_call();
  12403.                 interactivep = IsInteractive(handle);
  12404.                 end_system_call();
  12405.                 if (!interactivep)
  12406.                 #endif
  12407.                   return FALSE;
  12408.               }
  12409.             #endif
  12410.           #endif
  12411.           #ifdef KEYBOARD
  12412.           case strmtype_keyboard:
  12413.           #endif
  12414.           #ifdef WINDOWS
  12415.           case strmtype_terminal:
  12416.           #endif
  12417.           case strmtype_buff_in:
  12418.           #ifdef PIPES
  12419.           case strmtype_pipe_in:
  12420.           #endif
  12421.           #ifdef GENERIC_STREAMS
  12422.           case strmtype_generic:
  12423.           #endif
  12424.             return TRUE;
  12425.           case strmtype_synonym:
  12426.             # Synonym-Stream: weiterverfolgen
  12427.             { var reg2 object symbol = TheStream(stream)->strm_synonym_symbol;
  12428.               stream = get_synonym_stream(symbol);
  12429.               /* return interactive_stream_p(stream); */ # entrekursiviert:
  12430.               goto start;
  12431.             }
  12432.           case strmtype_concat:
  12433.             # den ersten der Streams abfragen:
  12434.             { var reg2 object streamlist = TheStream(stream)->strm_concat_list; # Liste von Streams
  12435.               if (consp(streamlist))
  12436.                 { stream = Car(streamlist);
  12437.                   /* return interactive_stream_p(stream); */ # entrekursiviert:
  12438.                   goto start;
  12439.                 }
  12440.                 else
  12441.                 { return FALSE; }
  12442.             }
  12443.           case strmtype_twoway:
  12444.           case strmtype_echo:
  12445.             { # Two-Way-Stream oder Echo-Stream: Input-Stream anschauen
  12446.               stream = TheStream(stream)->strm_twoway_input;
  12447.               /* return interactive_stream_p(stream); */ # entrekursiviert:
  12448.               goto start;
  12449.             }
  12450.           case strmtype_sch_file:
  12451.           case strmtype_ch_file:
  12452.           case strmtype_iu_file:
  12453.           case strmtype_is_file:
  12454.           case strmtype_str_in:
  12455.           default:
  12456.             return FALSE;
  12457.     }   }
  12458.  
  12459. LISPFUNN(interactive_stream_p,1)
  12460. # (INTERACTIVE-STREAM-P stream), CLTL2 S. 507/508
  12461. # stellt fest, ob stream interaktiv ist.
  12462.   { value1 = (interactive_stream_p(popSTACK()) ? T : NIL); mv_count=1; }
  12463.  
  12464. # UP: Schlie▀t einen Stream.
  12465. # stream_close(&stream);
  12466. # > stream: Stream
  12467. # < stream: Stream
  12468. # kann GC ausl÷sen
  12469.   global void stream_close (object* stream_);
  12470.   global void stream_close(stream_)
  12471.     var reg2 object* stream_;
  12472.     { var reg1 object stream = *stream_;
  12473.       if ((TheStream(stream)->strmflags & strmflags_open_B) == 0) # Stream schon geschlossen?
  12474.         return;
  12475.       # Typspezifische Routine aufrufen (darf GC ausl÷sen):
  12476.       switch (TheStream(stream)->strmtype)
  12477.         {
  12478.           #ifdef KEYBOARD
  12479.           case strmtype_keyboard: break;
  12480.           #endif
  12481.           case strmtype_terminal: break;
  12482.           case strmtype_sch_file:
  12483.           case strmtype_ch_file:
  12484.           case strmtype_iu_file:
  12485.           case strmtype_is_file:
  12486.             close_file(stream); break;
  12487.           case strmtype_synonym:
  12488.             close_synonym(stream); break;
  12489.           case strmtype_broad: break; # nichtrekursiv
  12490.           case strmtype_concat: break; # nichtrekursiv
  12491.           case strmtype_twoway: break; # nichtrekursiv
  12492.           case strmtype_echo: break; # nichtrekursiv
  12493.           case strmtype_str_in:
  12494.             close_str_in(stream); break;
  12495.           case strmtype_str_out: break;
  12496.           case strmtype_str_push: break;
  12497.           case strmtype_pphelp: break;
  12498.           case strmtype_buff_in:
  12499.             close_buff_in(stream); break;
  12500.           case strmtype_buff_out:
  12501.             close_buff_out(stream); break;
  12502.           #ifdef SCREEN
  12503.           case strmtype_window:
  12504.             close_window(stream); break;
  12505.           #endif
  12506.           #ifdef PRINTER_ATARI
  12507.           case strmtype_printer: break;
  12508.           #endif
  12509.           #ifdef PRINTER_AMIGAOS
  12510.           case strmtype_printer:
  12511.             close_printer(stream); break;
  12512.           #endif
  12513.           #ifdef HANDLES
  12514.           case strmtype_handle:
  12515.             close_handle(stream); break;
  12516.           #endif
  12517.           #ifdef PIPES
  12518.           case strmtype_pipe_in:
  12519.             close_pipe_in(stream); break;
  12520.           case strmtype_pipe_out:
  12521.             close_pipe_out(stream); break;
  12522.           #endif
  12523.           #ifdef SOCKETS
  12524.           case strmtype_socket:
  12525.             close_socket(stream); break;
  12526.           #endif
  12527.           #ifdef GENERIC_STREAMS
  12528.           case strmtype_generic:
  12529.             close_generic(stream); break;
  12530.           #endif
  12531.           default: NOTREACHED
  12532.         }
  12533.       # Dummys eintragen:
  12534.       close_dummys(*stream_);
  12535.     }
  12536.  
  12537. # UP: Schlie▀t eine Liste offener Files.
  12538. # close_some_files(list);
  12539. # > list: Liste von offenen Streams
  12540. # kann GC ausl÷sen
  12541.   global void close_some_files (object list);
  12542.   global void close_some_files(list)
  12543.     var reg2 object list;
  12544.     { pushSTACK(NIL); # dummy
  12545.       pushSTACK(list); # list
  12546.       while (mconsp(STACK_0))
  12547.         { var reg1 object streamlist = STACK_0;
  12548.           STACK_0 = Cdr(streamlist); # restliche Streams
  12549.           STACK_1 = Car(streamlist); # ein Stream aus der Liste
  12550.           stream_close(&STACK_1); # schlie▀en
  12551.         }
  12552.       skipSTACK(2);
  12553.     }
  12554.  
  12555. # UP: Schlie▀t alle offenen Files.
  12556. # close_all_files();
  12557. # kann GC ausl÷sen
  12558.   global void close_all_files (void);
  12559.   global void close_all_files()
  12560.     { close_some_files(O(open_files)); } # Liste aller offenen File-Streams
  12561.  
  12562. # UP: ErklΣrt alle offenen File-Streams fⁿr geschlossen.
  12563. # closed_all_files();
  12564.   global void closed_all_files (void);
  12565.   global void closed_all_files()
  12566.     { var reg2 object streamlist = O(open_files); # Liste aller offenen File-Streams
  12567.       while (consp(streamlist))
  12568.         { var reg1 object stream = Car(streamlist); # ein Stream aus der Liste
  12569.           if_strm_bfile_p(stream, # File-Stream ?
  12570.             { if (!nullp(TheStream(stream)->strm_file_handle)) # mit Handle /= NIL ?
  12571.                 # ja: Stream noch offen
  12572.                 { closed_file(stream); }
  12573.             },
  12574.             ; );
  12575.           close_dummys(stream);
  12576.           streamlist = Cdr(streamlist); # restliche Streams
  12577.         }
  12578.       O(open_files) = NIL; # keine offenen Files mehr
  12579.     }
  12580.  
  12581. LISPFUN(close,1,0,norest,key,1, (kw(abort)) )
  12582. # (CLOSE stream :abort), CLTL S. 332
  12583.   { skipSTACK(1); # :ABORT-Argument ignorieren
  12584.    {var reg1 object stream = STACK_0; # Argument
  12585.     if (!streamp(stream)) { fehler_stream(stream); } # mu▀ ein Stream sein
  12586.     stream_close(&STACK_0); # schlie▀en
  12587.     skipSTACK(1);
  12588.     value1 = T; mv_count=1; # T als Ergebnis
  12589.   }}
  12590.  
  12591. # UP: Stellt fest, ob im Stream stream ein Zeichen sofort verfⁿgbar ist.
  12592. # stream_listen(stream)
  12593. # > stream: Stream
  12594. # < ergebnis:  0 falls Zeichen verfⁿgbar,
  12595. #             -1 falls bei EOF angelangt,
  12596. #             +1 falls kein Zeichen verfⁿgbar, aber nicht wegen EOF
  12597. # kann GC ausl÷sen
  12598.   global signean stream_listen (object stream);
  12599.   global signean stream_listen(stream)
  12600.     var reg1 object stream;
  12601.     { check_SP(); check_STACK();
  12602.       if (mposfixnump(TheStream(stream)->strm_rd_ch_last)) # Char nach UNREAD ?
  12603.         { return signean_null; } # ja -> verfⁿgbar
  12604.         else
  12605.         # sonst nach Streamtyp verzweigen.
  12606.         # Jede Einzelroutine darf GC ausl÷sen. Au▀er beim Keyboard-Stream
  12607.         # oder Terminal-Stream handelt es sich um einen reinen EOF-Test.
  12608.         { switch (TheStream(stream)->strmtype)
  12609.             {
  12610.               #ifdef KEYBOARD
  12611.               case strmtype_keyboard: return listen_keyboard(stream);
  12612.               #endif
  12613.               case strmtype_terminal:
  12614.                 #if defined(ATARI) || defined(WINDOWS)
  12615.                 return listen_terminal(stream);
  12616.                 #endif
  12617.                 #if defined(UNIX) || (defined(MSDOS) && !defined(WINDOWS)) || defined(AMIGAOS) || defined(RISCOS)
  12618.                 terminalcase(stream,
  12619.                              { return listen_terminal1(stream); },
  12620.                              { return listen_terminal2(stream); },
  12621.                              { return listen_terminal3(stream); }
  12622.                             );
  12623.                 #endif
  12624.                 NOTREACHED
  12625.               case strmtype_sch_file:
  12626.                 if (TheStream(stream)->strmflags & strmflags_rd_ch_B)
  12627.                   { return listen_sch_file(stream); }
  12628.                   else
  12629.                   { return signean_minus; } # kein READ-CHAR
  12630.               case strmtype_ch_file:
  12631.                 if (TheStream(stream)->strmflags & strmflags_rd_ch_B)
  12632.                   { return listen_ch_file(stream); }
  12633.                   else
  12634.                   { return signean_minus; } # kein READ-CHAR
  12635.               case strmtype_iu_file:  return signean_minus; # kein READ-CHAR
  12636.               case strmtype_is_file:  return signean_minus; # kein READ-CHAR
  12637.               case strmtype_synonym:  return listen_synonym(stream);
  12638.               case strmtype_broad:    return signean_minus; # kein READ-CHAR
  12639.               case strmtype_concat:   return listen_concat(stream);
  12640.               case strmtype_twoway:   return listen_twoway(stream);
  12641.               case strmtype_echo:     return listen_twoway(stream);
  12642.               case strmtype_str_in:   return listen_str_in(stream);
  12643.               case strmtype_str_out:  return signean_minus; # kein READ-CHAR
  12644.               case strmtype_str_push: return signean_minus; # kein READ-CHAR
  12645.               case strmtype_pphelp:   return signean_minus; # kein READ-CHAR
  12646.               case strmtype_buff_in:  return listen_buff_in(stream);
  12647.               case strmtype_buff_out: return signean_minus; # kein READ-CHAR
  12648.               #ifdef SCREEN
  12649.               case strmtype_window:   return signean_minus; # kein READ-CHAR
  12650.               #endif
  12651.               #ifdef PRINTER
  12652.               case strmtype_printer:  return signean_minus; # kein READ-CHAR
  12653.               #endif
  12654.               #ifdef HANDLES
  12655.               case strmtype_handle:
  12656.                 if (TheStream(stream)->strmflags & strmflags_rd_ch_B)
  12657.                   { return listen_handle(stream); }
  12658.                   else
  12659.                   { return signean_minus; } # kein READ-CHAR
  12660.               #endif
  12661.               #ifdef PIPES
  12662.               case strmtype_pipe_in:  return listen_pipe_in(stream);
  12663.               case strmtype_pipe_out: return signean_minus; # kein READ-CHAR
  12664.               #endif
  12665.               #ifdef SOCKETS
  12666.               case strmtype_socket:   return listen_socket(stream);
  12667.               #endif
  12668.               #ifdef GENERIC_STREAMS
  12669.               case strmtype_generic:  return listen_generic(stream);
  12670.               #endif
  12671.               default: # Allgemein: nur EOF abfragen
  12672.                 if (TheStream(stream)->strmflags & strmflags_rd_ch_B)
  12673.                   { pushSTACK(stream);
  12674.                    {var reg2 object nextchar = peek_char(&STACK_0);
  12675.                     skipSTACK(1);
  12676.                     if (eq(nextchar,eof_value))
  12677.                       { return signean_minus; } # EOF erreicht
  12678.                       else
  12679.                       { return signean_null; }
  12680.                   }}
  12681.                   else
  12682.                   { return signean_minus; } # kein READ-CHAR
  12683.     }   }   }
  12684.  
  12685. # UP: L÷scht bereits eingegebenen interaktiven Input von einem Stream stream.
  12686. # clear_input(stream)
  12687. # > stream: Stream
  12688. # < ergebnis: TRUE falls Input gel÷scht wurde
  12689. # kann GC ausl÷sen
  12690.   global boolean clear_input (object stream);
  12691.   global boolean clear_input(stream)
  12692.     var reg1 object stream;
  12693.     { check_SP(); check_STACK();
  12694.       pushSTACK(stream); # Stream retten
  12695.       # Typspezifische Routine aufrufen (darf GC ausl÷sen).
  12696.       # Nur beim Keyboard-Stream und Terminal-Stream wird etwas getan.
  12697.      {var reg2 boolean ergebnis;
  12698.       switch (TheStream(stream)->strmtype)
  12699.         {
  12700.           #ifdef KEYBOARD
  12701.           case strmtype_keyboard:
  12702.             ergebnis = clear_input_keyboard(stream); break;
  12703.           #endif
  12704.           case strmtype_terminal:
  12705.             #if defined(ATARI) || defined(WINDOWS)
  12706.             ergebnis = clear_input_terminal(stream);
  12707.             #endif
  12708.             #if defined(UNIX) || (defined(MSDOS) && !defined(WINDOWS)) || defined(AMIGAOS) || defined(RISCOS)
  12709.             terminalcase(stream,
  12710.                          { ergebnis = clear_input_terminal1(stream); },
  12711.                          { ergebnis = clear_input_terminal2(stream); },
  12712.                          { ergebnis = clear_input_terminal3(stream); }
  12713.                         );
  12714.             #endif
  12715.             break;
  12716.           case strmtype_synonym:
  12717.             ergebnis = clear_input_synonym(stream); break;
  12718.           case strmtype_concat:
  12719.             ergebnis = clear_input_concat(stream); break;
  12720.           case strmtype_twoway:
  12721.           case strmtype_echo:
  12722.             ergebnis = clear_input_twoway(stream); break;
  12723.           case strmtype_buff_in:
  12724.             ergebnis = clear_input_buff_in(stream); break;
  12725.           #ifdef HANDLES
  12726.           case strmtype_handle:
  12727.             if (TheStream(stream)->strmflags & strmflags_rd_ch_B)
  12728.               { ergebnis = clear_input_handle(stream); }
  12729.               else
  12730.               { ergebnis = FALSE; }
  12731.             break;
  12732.           #endif
  12733.           #ifdef GENERIC_STREAMS
  12734.           case strmtype_generic:
  12735.             ergebnis = clear_input_generic(stream); break;
  12736.           #endif
  12737.           #ifdef PIPES
  12738.           case strmtype_pipe_in: # Pipe: nichts l÷schen??
  12739.           #endif
  12740.           #ifdef SOCKETS
  12741.           case strmtype_socket: # Socket: nichts l÷schen??
  12742.           #endif
  12743.           default:
  12744.             ergebnis = FALSE; break;
  12745.         }
  12746.       stream = popSTACK();
  12747.       if (ergebnis)
  12748.         # Input wurde gel÷scht -> auch das Lastchar mu▀ gel÷scht werden.
  12749.         # Dabei wird auch ein schon gesehenes EOF vergessen.
  12750.         { TheStream(stream)->strm_rd_ch_last = NIL; }
  12751.       return ergebnis;
  12752.     }}
  12753.  
  12754. # UP: Wartenden Output eines Stream stream ans Ziel bringen.
  12755. # finish_output(stream);
  12756. # > stream: Stream
  12757. # kann GC ausl÷sen
  12758.   global void finish_output (object stream);
  12759.   global void finish_output(stream)
  12760.     var reg1 object stream;
  12761.     { if (TheStream(stream)->strmflags & strmflags_wr_B) # Output-Stream?
  12762.         # nein -> fertig, ja -> nach Streamtyp verzweigen:
  12763.         { switch (TheStream(stream)->strmtype)
  12764.             { case strmtype_terminal:
  12765.                 finish_output_terminal(stream); break;
  12766.               case strmtype_sch_file:
  12767.               case strmtype_ch_file:
  12768.               case strmtype_iu_file:
  12769.               case strmtype_is_file:
  12770.                 finish_output_file(stream); break;
  12771.               case strmtype_synonym:
  12772.                 finish_output_synonym(stream); break;
  12773.               case strmtype_broad:
  12774.                 finish_output_broad(stream); break;
  12775.               case strmtype_twoway:
  12776.               case strmtype_echo:
  12777.                 finish_output_twoway(stream); break;
  12778.               case strmtype_buff_out:
  12779.                 finish_output_buff_out(stream); break;
  12780.               #ifdef PRINTER_AMIGAOS
  12781.               case strmtype_printer: # Printer:
  12782.                 # Schlie▀en und neu aufmachen wⁿrde vermutlich einen
  12783.                 # Seitenvorschub ausgeben, und das ist ja wohl nicht erwⁿnscht.
  12784.                 break; # Daher nichts tun.
  12785.               #endif
  12786.               #ifdef HANDLES
  12787.               case strmtype_handle:
  12788.                 finish_output_handle(stream); break;
  12789.               #endif
  12790.               #ifdef GENERIC_STREAMS
  12791.               case strmtype_generic:
  12792.                 finish_output_generic(stream); break;
  12793.               #endif
  12794.               #ifdef PIPES
  12795.               case strmtype_pipe_out: # Pipe: kann nichts tun
  12796.               #endif
  12797.               #ifdef SOCKETS
  12798.               case strmtype_socket: # Socket: kann nichts tun
  12799.               #endif
  12800.               default: # nichts tun
  12801.                 break;
  12802.         }   }
  12803.     }
  12804.  
  12805. # UP: Wartenden Output eines Stream stream ans Ziel bringen.
  12806. # force_output(stream);
  12807. # > stream: Stream
  12808. # kann GC ausl÷sen
  12809.   global void force_output (object stream);
  12810.   global void force_output(stream)
  12811.     var reg1 object stream;
  12812.     { if (TheStream(stream)->strmflags & strmflags_wr_B) # Output-Stream?
  12813.         # nein -> fertig, ja -> nach Streamtyp verzweigen:
  12814.         { switch (TheStream(stream)->strmtype)
  12815.             { case strmtype_terminal:
  12816.                 force_output_terminal(stream); break;
  12817.               case strmtype_sch_file:
  12818.               case strmtype_ch_file:
  12819.               case strmtype_iu_file:
  12820.               case strmtype_is_file:
  12821.                 force_output_file(stream); break;
  12822.               case strmtype_synonym:
  12823.                 force_output_synonym(stream); break;
  12824.               case strmtype_broad:
  12825.                 force_output_broad(stream); break;
  12826.               case strmtype_twoway:
  12827.               case strmtype_echo:
  12828.                 force_output_twoway(stream); break;
  12829.               case strmtype_buff_out:
  12830.                 force_output_buff_out(stream); break;
  12831.               #ifdef PRINTER_AMIGAOS
  12832.               case strmtype_printer: # Printer:
  12833.                 # Schlie▀en und neu aufmachen wⁿrde vermutlich einen
  12834.                 # Seitenvorschub ausgeben, und das ist ja wohl nicht erwⁿnscht.
  12835.                 break; # Daher nichts tun.
  12836.               #endif
  12837.               #ifdef HANDLES
  12838.               case strmtype_handle:
  12839.                 force_output_handle(stream); break;
  12840.               #endif
  12841.               #ifdef GENERIC_STREAMS
  12842.               case strmtype_generic:
  12843.                 force_output_generic(stream); break;
  12844.               #endif
  12845.               #ifdef PIPES
  12846.               case strmtype_pipe_out: # Pipe: kann nichts tun
  12847.               #endif
  12848.               #ifdef SOCKETS
  12849.               case strmtype_socket: # Socket: kann nichts tun
  12850.               #endif
  12851.               default: # nichts tun
  12852.                 break;
  12853.         }   }
  12854.     }
  12855.  
  12856. # UP: Wartenden Output eines Stream stream l÷schen.
  12857. # clear_output(stream);
  12858. # > stream: Stream
  12859. # kann GC ausl÷sen
  12860.   global void clear_output (object stream);
  12861.   global void clear_output(stream)
  12862.     var reg1 object stream;
  12863.     { # Auf dem ATARI oder unter DOS ist zwar bei keinem File- oder
  12864.       # Terminal-Stream etwas zu tun, aber das kann man nicht ausnutzen, denn
  12865.       # clear_output auf Buffered-Output-Streams geht immer.
  12866.       if (TheStream(stream)->strmflags & strmflags_wr_B) # Output-Stream?
  12867.         # nein -> fertig, ja -> nach Streamtyp verzweigen:
  12868.         { switch (TheStream(stream)->strmtype)
  12869.             { case strmtype_terminal:
  12870.                 #if defined(UNIX) || (defined(MSDOS) && !defined(WINDOWS)) || defined(AMIGAOS) || defined(RISCOS)
  12871.                 terminalcase(stream,
  12872.                              { clear_output_terminal1(stream); },
  12873.                              { clear_output_terminal2(stream); },
  12874.                              { clear_output_terminal3(stream); }
  12875.                             );
  12876.                 #endif
  12877.                 break;
  12878.               case strmtype_sch_file:
  12879.               case strmtype_ch_file:
  12880.               case strmtype_iu_file:
  12881.               case strmtype_is_file:
  12882.                 # File: nichts tun (wⁿrde die File-Verwaltung durcheinanderbringen)
  12883.                 break;
  12884.               case strmtype_synonym:
  12885.                 clear_output_synonym(stream); break;
  12886.               case strmtype_broad:
  12887.                 clear_output_broad(stream); break;
  12888.               case strmtype_twoway:
  12889.               case strmtype_echo:
  12890.                 clear_output_twoway(stream); break;
  12891.               case strmtype_buff_out:
  12892.                 clear_output_buff_out(stream); break;
  12893.               #ifdef PRINTER_AMIGAOS
  12894.               case strmtype_printer: # Printer: ungebuffert, also nichts zu tun
  12895.                 break;
  12896.               #endif
  12897.               #ifdef HANDLES
  12898.               case strmtype_handle:
  12899.                 clear_output_handle(stream); break;
  12900.               #endif
  12901.               #ifdef PIPES
  12902.               case strmtype_pipe_out: # Pipe: geht nicht
  12903.                 break;
  12904.               #endif
  12905.               #ifdef SOCKETS
  12906.               case strmtype_socket: # Socket: geht nicht
  12907.                 break;
  12908.               #endif
  12909.               #ifdef GENERIC_STREAMS
  12910.               case strmtype_generic:
  12911.                 clear_output_generic(stream);
  12912.                 break;
  12913.               #endif
  12914.               default: # nichts tun
  12915.                 break;
  12916.         }   }
  12917.     }
  12918.  
  12919. # UP: Liefert die Line-Position eines Streams.
  12920. # get_line_position(stream)
  12921. # > stream: Stream
  12922. # < ergebnis: Line-Position (Fixnum >=0)
  12923.   global object get_line_position (object stream);
  12924.   global object get_line_position(stream)
  12925.     var reg1 object stream;
  12926.     { check_SP();
  12927.       start:
  12928.       switch (TheStream(stream)->strmtype)
  12929.         { case strmtype_synonym:
  12930.             # Synonym-Stream: weiterverfolgen
  12931.             { var reg2 object symbol = TheStream(stream)->strm_synonym_symbol;
  12932.               stream = get_synonym_stream(symbol);
  12933.               /* return get_line_position(stream); */ # entrekursiviert:
  12934.               goto start;
  12935.             }
  12936.           case strmtype_broad:
  12937.             # Broadcast-Stream:
  12938.             # Maximum der Line-Positions der einzelnen Streams
  12939.             { var reg2 object streamlist = TheStream(stream)->strm_broad_list;
  12940.               var reg3 uintL maximum = 0; # bisheriges Maximum := 0
  12941.               while (consp(streamlist))
  12942.                 { var reg4 uintL next = # Line-Position des nΣchsten Teilstreams
  12943.                     posfixnum_to_L(get_line_position(Car(streamlist)));
  12944.                   if (next > maximum) { maximum = next; } # Maximum nehmen
  12945.                   streamlist = Cdr(streamlist);
  12946.                 }
  12947.               return fixnum(maximum); # Maximum als Ergebnis
  12948.             }
  12949.           case strmtype_twoway:
  12950.           case strmtype_echo:
  12951.             { # Two-Way-Stream oder Echo-Stream: Output-Stream anschauen
  12952.               stream = TheStream(stream)->strm_twoway_output;
  12953.               /* return get_line_position(stream); */ # entrekursiviert:
  12954.               goto start;
  12955.             }
  12956.           default: # normaler Stream
  12957.             return TheStream(stream)->strm_wr_ch_lpos;
  12958.     }   }
  12959.  
  12960. # UP: Liest mehrere Bytes von einem Stream.
  12961. # read_byte_array(stream,byteptr,len)
  12962. # > stream: Stream
  12963. # > uintB* byteptr: Adresse der zu fⁿllenden Bytefolge
  12964. # > uintL len: LΣnge der zu fⁿllenden Bytefolge
  12965. # < uintB* ergebnis: Pointer ans Ende des gefⁿllten Bereiches oder NULL
  12966.   global uintB* read_byte_array (object stream, uintB* byteptr, uintL len);
  12967.   global uintB* read_byte_array(stream,byteptr,len)
  12968.     var reg1 object stream;
  12969.     var reg3 uintB* byteptr;
  12970.     var reg2 uintL len;
  12971.     { if (len==0) { return byteptr; }
  12972.       start:
  12973.       if (eq(TheStream(stream)->strm_rd_by,P(rd_by_synonym))) # synonym
  12974.         { stream = get_synonym_stream(TheStream(stream)->strm_synonym_symbol);
  12975.           goto start;
  12976.         }
  12977.       elif (eq(TheStream(stream)->strm_rd_by,P(rd_by_twoway))) # twoway
  12978.         { stream = TheStream(stream)->strm_twoway_input;
  12979.           goto start;
  12980.         }
  12981.       #if defined(HANDLES) || defined(SOCKETS)
  12982.       elif (eq(TheStream(stream)->strm_rd_by,P(rd_by_handle))) # handle, socket
  12983.         { var reg5 Handle handle = TheHandle(TheStream(stream)->strm_ihandle);
  12984.           begin_system_call();
  12985.           loop
  12986.             {
  12987.               #if !defined(AMIGAOS)
  12988.               var reg4 int ergebnis = read(handle,byteptr,len);
  12989.               if (ergebnis<0)
  12990.                 { if (errno==EINTR) # Unterbrechung (evtl. durch Ctrl-C) ?
  12991.                     { end_system_call();
  12992.                       pushSTACK(S(read_byte_sequence));
  12993.                       fehler(serious_condition,
  12994.                              DEUTSCH ? "~: Ctrl-C: Tastatur-Interrupt" :
  12995.                              ENGLISH ? "~: Ctrl-C: User break" :
  12996.                              FRANCAIS ? "~ : Ctrl-C : Interruption clavier" :
  12997.                              ""
  12998.                             );
  12999.                     }
  13000.                   OS_error(); # Error melden
  13001.                 }
  13002.               #else # defined(AMIGAOS)
  13003.               var reg4 long ergebnis = Read(handle,byteptr,len);
  13004.               if (ergebnis<0) { OS_error(); } # Error melden
  13005.               #endif
  13006.               if (ergebnis==0) break; # EOF -> fertig
  13007.               byteptr += ergebnis; len -= ergebnis;
  13008.               if (len==0) break; # fertig?
  13009.             }
  13010.           end_system_call();
  13011.           return byteptr;
  13012.         }
  13013.       #endif
  13014.       elif (eq(TheStream(stream)->strm_rd_by,P(rd_by_iau_file)) # file
  13015.             && eq(TheStream(stream)->strm_file_bitsize,fixnum(8)) # eltype = (UNSIGNED-BYTE 8)
  13016.            )
  13017.         { dotimespL(len,len,
  13018.             { var reg4 uintB* ptr = b_file_nextbyte(stream);
  13019.               if (ptr == (uintB*)NULL) break;
  13020.               *byteptr++ = *ptr;
  13021.               # index und position incrementieren:
  13022.               TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  13023.               TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  13024.             });
  13025.           return byteptr;
  13026.         }
  13027.       else # keine Optimierung m÷glich
  13028.         { return NULL; }
  13029.     }
  13030.  
  13031. # UP: Schreibt mehrere Bytes auf einen Stream.
  13032. # write_byte_array(stream,byteptr,len)
  13033. # > stream: Stream
  13034. # > uintB* byteptr: Adresse der zu schreibenden Bytefolge
  13035. # > uintL len: LΣnge der zu schreibenden Bytefolge
  13036. # < uintB* ergebnis: Pointer ans Ende des geschriebenen Bereiches oder NULL
  13037.   global uintB* write_byte_array (object stream, uintB* byteptr, uintL len);
  13038.   global uintB* write_byte_array(stream,byteptr,len)
  13039.     var reg1 object stream;
  13040.     var reg3 uintB* byteptr;
  13041.     var reg2 uintL len;
  13042.     { if (len==0) { return byteptr; }
  13043.       start:
  13044.       if (eq(TheStream(stream)->strm_wr_by,P(wr_by_synonym))) # synonym
  13045.         { stream = get_synonym_stream(TheStream(stream)->strm_synonym_symbol);
  13046.           goto start;
  13047.         }
  13048.       elif (eq(TheStream(stream)->strm_wr_by,P(wr_by_twoway))) # twoway, echo
  13049.         { stream = TheStream(stream)->strm_twoway_output;
  13050.           goto start;
  13051.         }
  13052.       #if defined(HANDLES) || defined(SOCKETS)
  13053.       elif (eq(TheStream(stream)->strm_wr_by,P(wr_by_handle))) # handle, socket
  13054.         { var reg5 Handle handle = TheHandle(TheStream(stream)->strm_ohandle);
  13055.           begin_system_call();
  13056.           loop
  13057.             {
  13058.               #if !defined(AMIGAOS)
  13059.               var reg4 int ergebnis = write(handle,byteptr,len);
  13060.               if (ergebnis<0)
  13061.                 { if (errno==EINTR) # Unterbrechung (evtl. durch Ctrl-C) ?
  13062.                     { end_system_call();
  13063.                       pushSTACK(S(write_byte_sequence));
  13064.                       fehler(serious_condition,
  13065.                              DEUTSCH ? "~: Ctrl-C: Tastatur-Interrupt" :
  13066.                              ENGLISH ? "~: Ctrl-C: User break" :
  13067.                              FRANCAIS ? "~ : Ctrl-C : Interruption clavier" :
  13068.                              ""
  13069.                             );
  13070.                     }
  13071.                   OS_error(); # Error melden
  13072.                 }
  13073.               #else # defined(AMIGAOS)
  13074.               var reg4 long ergebnis = Write(handle,byteptr,len);
  13075.               if (ergebnis<0) { OS_error(); } # Error melden
  13076.               #endif
  13077.               if (ergebnis==0) # nicht erfolgreich?
  13078.                 { fehler_unwritable(S(write_byte_sequence),stream); }
  13079.               byteptr += ergebnis; len -= ergebnis;
  13080.               if (len==0) break; # fertig?
  13081.             }
  13082.           end_system_call();
  13083.           return byteptr;
  13084.         }
  13085.       #endif
  13086.       elif (eq(TheStream(stream)->strm_wr_by,P(wr_by_iau_file)) # file
  13087.             && eq(TheStream(stream)->strm_file_bitsize,fixnum(8)) # eltype = (UNSIGNED-BYTE 8)
  13088.            )
  13089.         { return write_byte_array_iau8_file(stream,byteptr,len); }
  13090.       else # keine Optimierung m÷glich
  13091.         { return NULL; }
  13092.     }
  13093.  
  13094. # UP: Liest mehrere String-Characters von einem Stream.
  13095. # read_schar_array(stream,charptr,len)
  13096. # > stream: Stream
  13097. # > uintB* charptr: Adresse der zu fⁿllenden Zeichenfolge
  13098. # > uintL len: LΣnge der zu fⁿllenden Zeichenfolge
  13099. # < uintB* ergebnis: Pointer ans Ende des gefⁿllten Bereiches oder NULL
  13100.   global uintB* read_schar_array (object stream, uintB* charptr, uintL len);
  13101.   global uintB* read_schar_array(stream,charptr,len)
  13102.     var reg4 object stream;
  13103.     var reg2 uintB* charptr;
  13104.     var reg3 uintL len;
  13105.     { if (len==0) { return charptr; }
  13106.      {var reg5 object lastchar = TheStream(stream)->strm_rd_ch_last;
  13107.       if (eq(lastchar,eof_value)) # EOF ?
  13108.         { return charptr; }
  13109.       if (posfixnump(lastchar) # Char nach UNREAD ?
  13110.           && !string_char_p(fixnum_to_char(lastchar)) # aber kein String-Char?
  13111.          )
  13112.         { return NULL; }
  13113.       if (eq(TheStream(stream)->strm_rd_ch,P(rd_ch_synonym))) # synonym
  13114.         { var reg6 object substream = get_synonym_stream(TheStream(stream)->strm_synonym_symbol);
  13115.           check_SP();
  13116.          {var reg1 uintB* endptr =
  13117.             (posfixnump(lastchar)
  13118.              ? read_schar_array(substream,charptr+1,len-1)
  13119.              : read_schar_array(substream,charptr,len)
  13120.             );
  13121.           if (endptr==NULL) { return NULL; }
  13122.           if (posfixnump(lastchar))
  13123.             { charptr[0] = char_code(fixnum_to_char(lastchar)); }
  13124.           TheStream(stream)->strm_rd_ch_last =
  13125.             (endptr == charptr+len ? code_char(endptr[-1]) : eof_value);
  13126.           return endptr;
  13127.         }}
  13128.       elif (eq(TheStream(stream)->strm_rd_ch,P(rd_ch_twoway))) # twoway
  13129.         { var reg6 object substream = TheStream(stream)->strm_twoway_input;
  13130.           check_SP();
  13131.          {var reg1 uintB* endptr =
  13132.             (posfixnump(lastchar)
  13133.              ? read_schar_array(substream,charptr+1,len-1)
  13134.              : read_schar_array(substream,charptr,len)
  13135.             );
  13136.           if (endptr==NULL) { return NULL; }
  13137.           if (posfixnump(lastchar))
  13138.             { charptr[0] = char_code(fixnum_to_char(lastchar)); }
  13139.           TheStream(stream)->strm_rd_ch_last =
  13140.             (endptr == charptr+len ? code_char(endptr[-1]) : eof_value);
  13141.           return endptr;
  13142.         }}
  13143.       #ifdef XHANDLES
  13144.       elif (eq(TheStream(stream)->strm_rd_ch,P(rd_ch_handle))) # handle, pipe_in, socket
  13145.         { if (posfixnump(lastchar))
  13146.             { *charptr++ = char_code(fixnum_to_char(lastchar)); len--; }
  13147.           if (len>0)
  13148.             { var reg6 Handle handle = TheHandle(TheStream(stream)->strm_ihandle);
  13149.               run_time_stop(); # Run-Time-Stoppuhr anhalten
  13150.               #ifdef GRAPHICS_SWITCH
  13151.               if (handle == stdin_handle) switch_text_mode();
  13152.               #endif
  13153.               begin_system_call();
  13154.               loop
  13155.                 {
  13156.                   #if !defined(AMIGAOS)
  13157.                   var reg1 int ergebnis = read(handle,charptr,len);
  13158.                   #else
  13159.                   var reg1 long ergebnis = Read(handle,charptr,len);
  13160.                   #endif
  13161.                   if (ergebnis<0)
  13162.                     {
  13163.                       #if !defined(AMIGAOS)
  13164.                       if (errno==EINTR) # Unterbrechung (evtl. durch Ctrl-C) ?
  13165.                         { end_system_call();
  13166.                           run_time_restart();
  13167.                           pushSTACK(S(read_char_sequence));
  13168.                           fehler(serious_condition,
  13169.                                  DEUTSCH ? "~: Ctrl-C: Tastatur-Interrupt" :
  13170.                                  ENGLISH ? "~: Ctrl-C: User break" :
  13171.                                  FRANCAIS ? "~ : Ctrl-C : Interruption clavier" :
  13172.                                  ""
  13173.                                 );
  13174.                         }
  13175.                       #endif
  13176.                       OS_error(); # Error melden
  13177.                     }
  13178.                   if (ergebnis==0) break; # EOF -> fertig
  13179.                   charptr += ergebnis; len -= ergebnis;
  13180.                   if (len==0) break; # fertig?
  13181.                 }
  13182.               end_system_call();
  13183.               run_time_restart();
  13184.             }
  13185.           TheStream(stream)->strm_rd_ch_last =
  13186.             (len==0 ? code_char(charptr[-1]) : eof_value);
  13187.           return charptr;
  13188.         }
  13189.       #endif
  13190.       elif (eq(TheStream(stream)->strm_rd_ch,P(rd_ch_sch_file))) # file
  13191.         { if (posfixnump(lastchar))
  13192.             { *charptr++ = char_code(fixnum_to_char(lastchar)); len--; }
  13193.           while (len>0)
  13194.             { var reg1 uintB* ptr = b_file_nextbyte(stream);
  13195.               if (ptr == (uintB*)NULL) break; # EOF -> fertig
  13196.              {var reg6 uintB ch = *ptr;
  13197.               # index und position incrementieren:
  13198.               TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  13199.               TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  13200.               # CR/LF -> NL umwandeln:
  13201.               if (ch==CR)
  13202.                 { # nΣchstes Zeichen auf LF untersuchen
  13203.                   ptr = b_file_nextbyte(stream);
  13204.                   if (!(ptr == (uintB*)NULL) && (*ptr == LF))
  13205.                     { # index und position incrementieren:
  13206.                       TheStream(stream)->strm_file_index = fixnum_inc(TheStream(stream)->strm_file_index,1);
  13207.                       TheStream(stream)->strm_file_position = fixnum_inc(TheStream(stream)->strm_file_position,1);
  13208.                       ch = NL;
  13209.                 }   }
  13210.               if (ch==NL)
  13211.                 # lineno incrementieren:
  13212.                 { TheStream(stream)->strm_sch_file_lineno = fixnum_inc(TheStream(stream)->strm_sch_file_lineno,1); }
  13213.               *charptr++ = ch; len--;
  13214.             }}
  13215.           TheStream(stream)->strm_rd_ch_last =
  13216.             (len==0 ? code_char(charptr[-1]) : eof_value);
  13217.           return charptr;
  13218.         }
  13219.       elif (eq(TheStream(stream)->strm_rd_ch,P(rd_ch_str_in))) # str_in
  13220.         { if (posfixnump(lastchar))
  13221.             { *charptr++ = char_code(fixnum_to_char(lastchar)); len--; }
  13222.           if (len>0)
  13223.             { var reg7 uintL index = posfixnum_to_L(TheStream(stream)->strm_str_in_index); # Index
  13224.               var reg8 uintL endindex = posfixnum_to_L(TheStream(stream)->strm_str_in_endindex);
  13225.               if (index < endindex)
  13226.                 { var uintL srclen;
  13227.                   var reg1 uintB* srcptr = unpack_string(TheStream(stream)->strm_str_in_string,&srclen);
  13228.                   # Ab srcptr kommen srclen Zeichen.
  13229.                   if (srclen < endindex) { fehler_str_in_adjusted(stream); }
  13230.                   srcptr += index;
  13231.                  {var reg6 uintL count = endindex - index;
  13232.                   if (count > len) { count = len; }
  13233.                   # count = min(len,endindex-index) > 0.
  13234.                   len -= count;
  13235.                   dotimespL(count,count, { *charptr++ = *srcptr++; } );
  13236.             }   }}
  13237.           TheStream(stream)->strm_rd_ch_last =
  13238.             (len==0 ? code_char(charptr[-1]) : eof_value);
  13239.           return charptr;
  13240.         }
  13241.       else # keine Optimierung m÷glich
  13242.         { return NULL; }
  13243.     }}
  13244.  
  13245. # UP: Schreibt mehrere String-Characters auf einen Stream.
  13246. # write_schar_array(stream,charptr,len)
  13247. # > stream: Stream
  13248. # > uintB* charptr: Adresse der zu schreibenden Zeichenfolge
  13249. # > uintL len: LΣnge der zu schreibenden Zeichenfolge
  13250. # < uintB* ergebnis: Pointer ans Ende des geschriebenen Bereiches oder NULL
  13251.   global uintB* write_schar_array (object stream, uintB* charptr, uintL len);
  13252.   global uintB* write_schar_array(stream,charptr,len)
  13253.     var reg4 object stream;
  13254.     var reg2 uintB* charptr;
  13255.     var reg3 uintL len;
  13256.     { if (len==0) { return charptr; }
  13257.       start:
  13258.       if (eq(TheStream(stream)->strm_wr_ch,P(wr_ch_synonym))) # synonym
  13259.         { stream = get_synonym_stream(TheStream(stream)->strm_synonym_symbol);
  13260.           goto start;
  13261.           # Line-Position aktualisieren kann hier entfallen.
  13262.         }
  13263.       elif (eq(TheStream(stream)->strm_wr_ch,P(wr_ch_twoway))) # twoway, echo
  13264.         { stream = TheStream(stream)->strm_twoway_output;
  13265.           goto start;
  13266.           # Line-Position aktualisieren kann hier entfallen.
  13267.         }
  13268.       #ifdef XHANDLES
  13269.       elif (eq(TheStream(stream)->strm_wr_ch,P(wr_ch_handle)) # handle, pipe_out, socket
  13270.             && !(TheStream(stream)->strmtype == strmtype_terminal) # aber nicht terminal1, terminal2 (wg. wr_ss_lpos)
  13271.            )
  13272.         { return write_schar_array_handle(stream,charptr,len); }
  13273.       #endif
  13274.       elif (eq(TheStream(stream)->strm_wr_ch,P(wr_ch_sch_file))) # file
  13275.         { return write_schar_array_sch_file(stream,charptr,len); }
  13276.       else # keine Optimierung m÷glich
  13277.         { return NULL; }
  13278.     }
  13279.  
  13280. LISPFUN(read_byte,1,2,norest,nokey,0,NIL)
  13281. # (READ-BYTE stream [eof-error-p [eof-value]]), CLTL S. 382
  13282.   { # Stream ⁿberprⁿfen:
  13283.     var reg1 object stream = STACK_2;
  13284.     if (!streamp(stream)) { fehler_stream(stream); }
  13285.     # Integer lesen:
  13286.    {var reg2 object obj = read_byte(stream);
  13287.     if (eq(obj,eof_value))
  13288.       # EOF-Behandlung
  13289.       { if (!nullp(STACK_1)) # eof-error-p /= NIL (z.B. = #<UNBOUND>) ?
  13290.           # Error melden:
  13291.           { pushSTACK(STACK_2); # Wert fⁿr Slot STREAM von STREAM-ERROR
  13292.             pushSTACK(STACK_(2+1)); # Stream
  13293.             pushSTACK(S(read_byte));
  13294.             fehler(end_of_file,
  13295.                    DEUTSCH ? "~: Eingabestream ~ ist zu Ende." :
  13296.                    ENGLISH ? "~: input stream ~ has reached its end" :
  13297.                    FRANCAIS ? "~ : ArrivΘe en fin du ½stream╗ d'entrΘe ~." :
  13298.                    ""
  13299.                   );
  13300.           }
  13301.           else
  13302.           # EOF verarzten:
  13303.           { var reg2 object eofval = STACK_0;
  13304.             if (eq(eofval,unbound)) { eofval = eof_value; } # Default ist #<EOF>
  13305.             value1 = eofval; mv_count=1; skipSTACK(3); # eofval als Wert
  13306.           }
  13307.       }
  13308.       else
  13309.       { value1 = obj; mv_count=1; skipSTACK(3); } # obj als Wert
  13310.   }}
  13311.  
  13312. LISPFUNN(write_byte,2)
  13313. # (WRITE-BYTE integer stream), CLTL S. 385
  13314.   { # Stream ⁿberprⁿfen:
  13315.     var reg1 object stream = STACK_0;
  13316.     if (!streamp(stream)) { fehler_stream(stream); }
  13317.    {# Integer ⁿberprⁿfen:
  13318.     var reg2 object obj = STACK_1;
  13319.     if (!integerp(obj)) { fehler_wr_integer(stream,obj); }
  13320.     # Integer schreiben:
  13321.     write_byte(stream,obj);
  13322.     value1 = STACK_1; mv_count=1; skipSTACK(2); # obj als Wert
  13323.   }}
  13324.  
  13325. # UP: ▄berprⁿft, ob ein Argument ein offener File-Stream ist.
  13326. # check_open_file_stream(obj);
  13327. # > obj: Argument
  13328. # > subr_self: Aufrufer (ein SUBR)
  13329.   local void check_open_file_stream (object obj);
  13330.   local void check_open_file_stream(obj)
  13331.     var reg1 object obj;
  13332.     { if (!streamp(obj)) goto fehler_bad_obj; # Stream ?
  13333.       if_strm_bfile_p(obj, ; , goto fehler_bad_obj; ); # Streamtyp File-Stream ?
  13334.       if ((TheStream(obj)->strmflags & strmflags_open_B) == 0) goto fehler_bad_obj; # Stream offen ?
  13335.       if (nullp(TheStream(obj)->strm_file_handle)) goto fehler_bad_obj; # und Handle /= NIL ?
  13336.       return; # ja -> OK
  13337.       fehler_bad_obj:
  13338.         pushSTACK(obj);
  13339.         pushSTACK(TheSubr(subr_self)->name);
  13340.         fehler(error,
  13341.                DEUTSCH ? "~: Argument mu▀ ein offener File-Stream sein, nicht ~" :
  13342.                ENGLISH ? "~: argument ~ is not an open file stream" :
  13343.                FRANCAIS ? "~ : L'argument ~ doit Ωtre un ½stream╗ ouvert de fichier et non ~." :
  13344.                ""
  13345.               );
  13346.     }
  13347.  
  13348. LISPFUN(file_position,1,1,norest,nokey,0,NIL)
  13349. # (FILE-POSITION file-stream [position]), CLTL S. 425
  13350.   { var reg1 object position = popSTACK();
  13351.     var reg2 object stream = popSTACK();
  13352.     check_open_file_stream(stream); # stream ⁿberprⁿfen
  13353.     if (eq(position,unbound))
  13354.       # position nicht angegeben -> Position als Wert:
  13355.       { value1 = TheStream(stream)->strm_file_position; mv_count=1; }
  13356.       else
  13357.       { if (eq(position,S(Kstart)))
  13358.           # :START -> an den Anfang positionieren:
  13359.           { position_file_start(stream); }
  13360.         elif (eq(position,S(Kend)))
  13361.           # :END -> ans Ende positionieren:
  13362.           { position_file_end(stream); }
  13363.         elif (posfixnump(position))
  13364.           # an die angegebene Position positionieren:
  13365.           { position_file(stream,posfixnum_to_L(position)); }
  13366.         else
  13367.           # UnzulΣssiges Position-Argument
  13368.           { pushSTACK(position); # Wert fⁿr Slot DATUM von TYPE-ERROR
  13369.             pushSTACK(O(type_position)); # Wert fⁿr Slot EXPECTED-TYPE von TYPE-ERROR
  13370.             pushSTACK(position); pushSTACK(S(Kend)); pushSTACK(S(Kstart));
  13371.             pushSTACK(TheSubr(subr_self)->name);
  13372.             fehler(type_error,
  13373.                    DEUTSCH ? "~: Position-Argument mu▀ ~ oder ~ oder ein Fixnum >=0 sein, nicht ~" :
  13374.                    ENGLISH ? "~: position argument should be ~ or ~ or a nonnegative fixnum, not ~" :
  13375.                    FRANCAIS ? "~ : L'argument position doit Ωtre ~, ~ ou de type FIXNUM positif ou zΘro, mais non ~." :
  13376.                    ""
  13377.                   );
  13378.           }
  13379.         value1 = T; mv_count=1; # Wert T
  13380.       }
  13381.   }
  13382.  
  13383. LISPFUNN(file_length,1)
  13384. # (FILE-LENGTH file-stream), CLTL S. 425
  13385.   { var reg1 object stream = popSTACK();
  13386.     check_open_file_stream(stream); # stream ⁿberprⁿfen
  13387.     # Position merken:
  13388.    {var reg2 object position = TheStream(stream)->strm_file_position;
  13389.     # ans Ende positionieren:
  13390.     position_file_end(stream);
  13391.     # Ende-Position merken:
  13392.     {var reg3 object endposition = TheStream(stream)->strm_file_position;
  13393.      # an die alte Position zurⁿckpositionieren:
  13394.      position_file(stream,posfixnum_to_L(position));
  13395.      value1 = endposition; mv_count=1; # Ende-Position als Wert
  13396.   }}}
  13397.  
  13398. LISPFUNN(line_number,1)
  13399. # (SYS::LINE-NUMBER stream) liefert die aktuelle Zeilennummer (falls stream
  13400. # ein String-Char-File-Input-Stream ist, von dem nur gelesen wurde).
  13401.   { var reg1 object stream = popSTACK();
  13402.     if (!streamp(stream)) { fehler_stream(stream); } # stream ⁿberprⁿfen
  13403.     value1 = (TheStream(stream)->strmtype == strmtype_sch_file
  13404.               ? TheStream(stream)->strm_sch_file_lineno # aktuelle Zeilennummer
  13405.               : NIL                                     # NIL falls unbekannt
  13406.              );
  13407.     mv_count=1;
  13408.   }
  13409.  
  13410. # Tabelle aller Pseudofunktionen
  13411.   global struct pseudofun_tab_ pseudofun_tab =
  13412.     {
  13413.       #define PSEUDOFUN  PSEUDOFUN_B
  13414.       #include "pseudofun.c"
  13415.       #undef PSEUDOFUN
  13416.     };
  13417.  
  13418. # ==============================================================================
  13419.  
  13420. #ifdef EMUNIX_PORTABEL
  13421.  
  13422. # Eine Hilfsfunktion fⁿr bidirektionale Pipes: popenrw()
  13423. #undef stdin_handle
  13424. #undef stdout_handle
  13425. #include "../os2/popenrw.c"
  13426.  
  13427. #endif
  13428.  
  13429. # ==============================================================================
  13430.  
  13431. # filestatus/if_file_exists, file_datetime durch break_sem_4 schⁿtzen??
  13432. # Signalbehandlung bei EXECUTE, SHELL, MAKE-PIPE-INPUT-STREAM, MAKE-PIPE-OUTPUT-STREAM, MAKE-PIPE-IO-STREAM ??
  13433.  
  13434.