1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header: d:/cvsroot/tads/TADS2/OSGEN.C,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $";
4 #endif
5 
6 /*
7  *   Copyright (c) 1990, 2002 Michael J. Roberts.  All Rights Reserved.
8  *
9  *   Please see the accompanying license file, LICENSE.TXT, for information
10  *   on using and copying this software.
11  */
12 /*
13 Name
14   osgen  - Operating System dependent functions, general implementation
15 Function
16   This module contains certain OS-dependent functions that are common
17   between several systems.  Routines in this file are selectively enabled
18   according to macros defined in os.h:
19 
20     USE_STDIO     - implement os_print, os_flush, os_gets with stdio functions
21     USE_DOSEXT    - implement os_remext, os_defext using MSDOS-like filename
22                     conventions
23     USE_NULLINIT  - implement os_init and os_term as do-nothing routines
24     USE_NULLPAUSE - implement os_expause as a do-nothing routine
25     USE_EXPAUSE   - use an os_expause that prints a 'strike any key' message
26                     and calls os_waitc
27     USE_TIMERAND  - implement os_rand using localtime() as a seed
28     USE_NULLSTAT  - use a do-nothing os_status function
29     USE_NULLSCORE - use a do-nothing os_score function
30     RUNTIME       - enable character-mode console implementation
31     USE_STATLINE  - implement os_status and os_score using character-mode
32                     status line implementation
33     USE_OVWCHK    - implements default saved file overwrite check
34     USE_NULLSTYPE - use a dummy os_settype routine
35     USE_NULL_SET_TITLE - use an empty os_set_title() implementation
36 
37     If USE_STDIO is defined, we'll implicitly define USE_STDIO_INPDLG.
38 
39     If USE_STATLINE is defined, certain subroutines must be provided for
40     your platform that handle the character-mode console:
41         ossclr - clears a portion of the screen
42         ossdsp - displays text in a given color at a given location
43         ossscr - scroll down (i.e., moves a block of screen up)
44         ossscu - scroll up (i.e., moves a block of screen down)
45         ossloc - locate cursor
46 
47     If USE_STATLINE is defined, certain sub-options can be enabled:
48         USE_SCROLLBACK - include output buffer capture in console system
49         USE_HISTORY    - include command editing and history in console system
50 Notes
51 
52 Modified
53   01/01/98 MJRoberts     - moved certain osgen.c routines to osnoui.c
54   04/24/93 JEras         - add os_locate() for locating tads-related files
55   04/12/92 MJRoberts     - add os_strsc (string score) function
56   03/26/92 MJRoberts     - add os_setcolor function
57   09/26/91 MJRoberts     - os/2 user exit support
58   09/04/91 MJRoberts     - stop reading resources if we find '$eof' resource
59   08/28/91 MJRoberts     - debugger bug fix
60   08/01/91 MJRoberts     - make runstat work correctly
61   07/30/91 MJRoberts     - add debug active/inactive visual cue
62   05/23/91 MJRoberts     - add user exit reader
63   04/08/91 MJRoberts     - add full-screen debugging support
64   03/10/91 MJRoberts     - integrate John's qa-scripter mods
65   11/27/90 MJRoberts     - use time() not localtime() in os_rand; cast time_t
66   11/15/90 MJRoberts     - created (split off from os.c)
67 */
68 
69 #define OSGEN_INIT
70 # include "os.h"
71 #undef OSGEN_INIT
72 
73 #include "osgen.h"
74 
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include <stdarg.h>
79 #include <ctype.h>
80 #include <assert.h>
81 
82 #include "run.h"
83 
84 #if defined(TURBO) || defined(DJGPP)
85 #include "io.h"
86 #endif
87 
88 #include "lib.h"
89 #include "tio.h"
90 
91 /* global "plain mode" flag */
92 int os_f_plain = 0;
93 
94 #ifdef RUNTIME
95 # ifdef USE_SCROLLBACK
96 int osssbmode();
97 void scrpgup();
98 void scrpgdn();
99 void scrlnup();
100 void scrlndn();
101 static void ossdosb();
102 
103 /*
104  *   Screen size variables.  The underlying system-specific "oss" code must
105  *   initialize these during startup and must keep them up-to-date if the
106  *   screen size ever changes.
107  */
108 int G_oss_screen_width = 80;
109 int G_oss_screen_height = 24;
110 
111 # endif /* USE_SCROLLBACK */
112 #endif /* RUNTIME */
113 
114 /* forward declare the low-level display routine */
115 void ossdspn(int y, int x, int color, char *p);
116 
117 /*
118  *   The special character codes for controlling color.
119  */
120 
121 /*
122  *   Set text attributes: the next byte has the new text attributes value,
123  *   with 1 added to it to ensure it's never zero.  (A zero in the buffer has
124  *   a special meaning, so we want to ensure we never have an incidental
125  *   zero.  Zero happens to be a valid attribute value, though, so we have to
126  *   encode attributes to avoid this possibility.  Our simple "plus one"
127  *   encoding ensures we satisfy the never-equals-zero rule.)
128  */
129 #define OSGEN_ATTR            1
130 
131 /*
132  *   explicit colored text: this is followed by two bytes giving the
133  *   foreground and background colors as OSGEN_COLOR_xxx codes
134  */
135 #define OSGEN_COLOR           2
136 
137 
138 /*
139  *   If this port is to use the default saved file overwrite check, define
140  *   USE_OVWCHK.  This routine tries to open the file; if successful, the
141  *   file is closed and we ask the user if they're sure they want to overwrite
142  *   the file.
143  */
144 #ifdef USE_OVWCHK
os_chkovw(char * filename)145 int os_chkovw(char *filename)
146 {
147     FILE *fp;
148 
149     if ((fp = fopen( filename, "r" )) != 0)
150     {
151         char buf[128];
152 
153         fclose(fp);
154         os_printz("That file already exists.  Overwrite it? (y/n) >");
155         os_gets((uchar *)buf, sizeof(buf));
156         if (buf[0] != 'y' && buf[0] != 'Y')
157             return 1;
158     }
159     return 0;
160 }
161 #endif /* USE_OVWCHK */
162 
163 /*
164  *   non-stop mode does nothing in character-mode implementations, since the
165  *   portable console layer handles MORE mode
166  */
os_nonstop_mode(int flag)167 void os_nonstop_mode(int flag)
168 {
169 }
170 
171 /* ------------------------------------------------------------------------ */
172 /*
173  *   Ports can implement os_flush and os_gets as calls to the stdio routines
174  *   of the same name, and os_printz and os_print using the stdio routine
175  *   printf, by defining USE_STDIO.  These definitions can be used for any
176  *   port for which the standard C run-time library is available.
177  */
178 
179 #ifdef USE_STDIO
180 
181 /*
182  *   print a null-terminated string the console
183  */
os_printz(const char * str)184 void os_printz(const char *str)
185 {
186     /* write the string to stdout */
187     fputs(str, stdout);
188 }
189 
190 /*
191  *   print a counted-length string, which isn't necessarily null-terminated
192  */
os_print(const char * str,size_t len)193 void os_print(const char *str, size_t len)
194 {
195     /* write the string to stdout, limiting the length */
196     printf("%.*s", (int)len, str);
197 }
198 
199 /*
200  *   os_flush forces output of anything buffered for standard output.  It
201  *   is generally used prior to waiting for a key (so the normal flushing
202  *   may not occur, as it does when asking for a line of input).
203  */
os_flush(void)204 void os_flush(void)
205 {
206     fflush( stdout );
207 }
208 
209 /*
210  *   update the display - since we're using text mode, there's nothing we
211  *   need to do
212  */
os_update_display(void)213 void os_update_display(void)
214 {
215 }
216 
217 /*
218  *   os_gets performs the same function as gets().  It should get a
219  *   string from the keyboard, echoing it and allowing any editing
220  *   appropriate to the system, and return the null-terminated string as
221  *   the function's value.  The closing newline should NOT be included in
222  *   the string.
223  */
os_gets(uchar * s,size_t bufl)224 uchar *os_gets(uchar *s, size_t bufl)
225 {
226     return((uchar *)fgets((char *)s, bufl, stdin));
227 }
228 
229 /*
230  *   The default stdio implementation does not support reading a line of
231  *   text with timeout.
232  */
os_gets_timeout(unsigned char * buf,size_t bufl,unsigned long timeout,int resume_editing)233 int os_gets_timeout(unsigned char *buf, size_t bufl,
234                     unsigned long timeout, int resume_editing)
235 {
236     /* tell the caller this operation is not supported */
237     return OS_EVT_NOTIMEOUT;
238 }
239 
240 /*
241  *   since we don't support os_gets_timeout(), we don't need to do anything
242  *   in the cancel routine
243  */
os_gets_cancel(int reset)244 void os_gets_cancel(int reset)
245 {
246     /* os_gets_timeout doesn't do anything, so neither do we */
247 }
248 
249 /*
250  *   Get an event - stdio version.  This version does not accept a timeout
251  *   value, and can only get a keystroke.
252  */
os_get_event(unsigned long timeout,int use_timeout,os_event_info_t * info)253 int os_get_event(unsigned long timeout, int use_timeout,
254                  os_event_info_t *info)
255 {
256     /* if there's a timeout, return an error indicating we don't allow it */
257     if (use_timeout)
258         return OS_EVT_NOTIMEOUT;
259 
260     /* get a key the normal way */
261     info->key[0] = os_getc();
262 
263     /* if it's an extended key, get the other key */
264     if (info->key[0] == 0)
265     {
266         /* get the extended key code */
267         info->key[1] = os_getc();
268 
269         /* if it's EOF, return an EOF event rather than a key event */
270         if (info->key[1] == CMD_EOF)
271             return OS_EVT_EOF;
272     }
273 
274     /* return the keyboard event */
275     return OS_EVT_KEY;
276 }
277 
278 #endif /* USE_STDIO */
279 
280 /******************************************************************************
281 * Ports without any special initialization/termination requirements can define
282 * USE_NULLINIT to pick up the default definitions below.  These do nothing, so
283 * ports requiring special handling at startup and/or shutdown time must define
284 * their own versions of these routines.
285 ******************************************************************************/
286 
287 #ifdef USE_NULLINIT
288 /* os_init returns 0 for success, 1 for failure.  The arguments are &argc, the
289 *  address of the count of arguments to the program, and argv, the address of
290 *  an array of up to 10 pointers to those arguments.  For systems which don't
291 *  pass a standard command line (such as the Mac Finder), the arguments should
292 *  be read here using some alternate mechanism (an alert box, for instance),
293 *  and the values of argc and argv[] updated accordingly.  Note that a maximum
294 *  of 10 arguments are allowed to be stored in the argv[] array.  The command
295 *  line itself can be stored in buf, which is a buffer passed by the caller
296 *  guaranteed to be bufsiz bytes long.
297 *
298 *  Unix conventions are followed, so argc is 1 when no arguments are present.
299 *  The final argument is a prompt string which can be used to ask the user for
300 *  a command line; its use is not required, but may be desirable for producing
301 *  a relevant prompt message.  See the Mac implementation for a detailed
302 *  example of how this mechanism is used.
303 */
os_init(int * argc,char * argv[],const char * prompt,char * buf,int bufsiz)304 int os_init(int *argc, char *argv[], const char *prompt,
305             char *buf, int bufsiz)
306 {
307     return 0;
308 }
309 
310 /*
311  *   uninitialize
312  */
os_uninit(void)313 void os_uninit(void)
314 {
315 }
316 
317 /*
318  *   os_term should perform any necessary cleaning up, then terminate the
319  *   program.  The int argument is a return code to be passed to the
320  *   caller, generally 0 for success and other for failure.
321  */
os_term(int rc)322 void os_term(int rc)
323 {
324     exit(rc);
325 }
326 #endif /* USE_NULLINIT */
327 
328 /* ------------------------------------------------------------------------ */
329 /*
330  *   Ports can define USE_NULLPAUSE if no pause is required on exit.
331  *
332  *   Ports needing an exit pause, and can simply print a message (with
333  *   os_print) and wait for a key (with os_getc) can define USE_EXPAUSE.
334  */
335 
336 #ifdef USE_NULLPAUSE
os_expause(void)337 void os_expause(void)
338 {
339     /* does nothing */
340 }
341 #endif /* USE_NULLPAUSE */
342 
343 #ifdef USE_EXPAUSE
os_expause(void)344 void os_expause(void)
345 {
346     os_printz("(Strike any key to exit...)");
347     os_flush();
348     os_waitc();
349 }
350 #endif /* USE_EXPAUSE */
351 
352 
353 #ifdef USE_NULLSTAT
354 /*
355  *   USE_NULLSTAT defines a do-nothing version of os_status.
356  */
os_status(int stat)357 void os_status(int stat)
358 {
359     /* ignore the new status */
360 }
361 
os_get_status()362 int os_get_status()
363 {
364     return 0;
365 }
366 #endif /* USE_NULLSTAT */
367 
368 #ifdef USE_NULLSCORE
369 /*
370  *   USE_NULLSCORE defines a do-nothing version of os_score.
371  */
os_score(int cur,int turncount)372 void os_score(int cur, int turncount)
373 {
374     /* ignore the score information */
375 }
376 
os_strsc(const char * p)377 void os_strsc(const char *p)
378 {
379     /* ignore */
380 }
381 #endif /* USE_NULLSCORE */
382 
383 #ifdef USE_STATLINE
384 
385 /* saved main text area column when drawing in status line */
386 static osfar_t int S_statline_savecol;
387 
388 /* saved text column when we're drawing in status line */
389 static osfar_t int text_lastcol;
390 
391 /*
392  *   first line of text area - the status line is always one line high, so
393  *   this is always simply 1
394  */
395 static osfar_t int text_line = 1;
396 
397 /* the column in the status line for the score part of the display */
398 static osfar_t int score_column = 0;
399 
400 /*
401  *   Define some macros in terms of "oss" globals that tell us the size of
402  *   the screen.
403  *
404  *   max_line is the maximum row number of the text area, as a zero-based row
405  *   number.  The text area goes from the second row (row 1) to the bottom of
406  *   the screen, so this is simply the screen height minus one.
407  *
408  *   max_column is the maximum column number of the text area, as a
409  *   zero-based column number.  The text area goes from the first column
410  *   (column 0) to the rightmost column, so this is simply the screen width
411  *   minus one.
412  *
413  *   text_line and text_column are the starting row and column number of the
414  *   text area.  These are always row 1, column 0.
415  *
416  *   sdesc_line and sdesc_column are the starting row and column of the
417  *   status line.  These are always row 0, column 0.
418  */
419 #define max_line ((G_oss_screen_height) - 1)
420 #define max_column ((G_oss_screen_width) - 1)
421 #define text_line 1
422 #define text_column 0
423 #define sdesc_line 0
424 #define sdesc_column 0
425 
426 /*
427  *   Status line buffer.  Each time we display text to the status line,
428  *   we'll keep track of the text here.  This allows us to refresh the
429  *   status line whenever we overwrite it (during scrollback mode, for
430  *   example).
431  */
432 static osfar_t char S_statbuf[OS_MAXWIDTH + 1];
433 
434 /* pointer to the next free character of the status line buffer */
435 static osfar_t char *S_statptr = S_statbuf;
436 
437 
438 /*
439  *   The special run-time version displays a status line and the ldesc before
440  *   each command line is read.  To accomplish these functions, we need to
441  *   redefine os_gets and os_print with our own special functions.
442  *
443  *   Note that os_init may have to modify some of these values to suit, and
444  *   should set up the screen appropriately.
445  */
os_status(int stat)446 void os_status(int stat)
447 {
448     /* if we're leaving the status line, restore the old main text column */
449     if (stat != status_mode && (status_mode == 1 || status_mode == 2))
450     {
451         /*
452          *   we're leaving status-line (or post-status-line) mode - restore
453          *   the main text column that was in effect before we drew the
454          *   status line
455          */
456         text_lastcol = S_statline_savecol;
457     }
458 
459     /* switch to the new mode */
460     status_mode = stat;
461 
462     /* check the mode */
463     if (status_mode == 1)
464     {
465         /*
466          *   we're entering the status line - start writing at the start
467          *   of the status line
468          */
469         S_statptr = S_statbuf;
470 
471         /*
472          *   clear out any previously-saved status line text, since we're
473          *   starting a new status line
474          */
475         *S_statptr = '\0';
476 
477         /*
478          *   remember the current text area display column so that we can
479          *   restore it when we finish with the status line
480          */
481         S_statline_savecol = text_lastcol;
482     }
483     else if (status_mode == 2)
484     {
485         /*
486          *   entering post-status-line mode - remember the text column so
487          *   that we can restore it when we return to normal mode
488          */
489         S_statline_savecol = text_lastcol;
490     }
491 }
492 
os_get_status()493 int os_get_status()
494 {
495     return status_mode;
496 }
497 
498 /* Set score to a string value provided by the caller */
os_strsc(const char * p)499 void os_strsc(const char *p)
500 {
501     static osfar_t char lastbuf[135];
502     int  i;
503     int  x;
504 
505     /* ignore score strings in plain mode, there's no status line */
506     if (os_f_plain)
507         return;
508 
509     /* presume the score column will be ten spaces left of the right edge */
510     score_column = max_column - 10;
511 
512     /* start out in the initial score column */
513     x = score_column;
514 
515     /*
516      *   if we have a string, save the new value; if the string pointer is
517      *   null, it means that we should redraw the string with the previous
518      *   value
519      */
520     if (p != 0)
521         strcpy(lastbuf, p);
522     else
523         p = lastbuf;
524 
525     /* display enough spaces to right-justify the value */
526     for (i = strlen(p) ; i + score_column <= max_column ; ++i)
527         ossdsp(sdesc_line, x++, sdesc_color, " ");
528     if (x + strlen(p) > (size_t)max_column)
529         score_column = x = max_column - strlen(p);
530     ossdsp(sdesc_line, x, sdesc_color, p);
531     ossdsp(sdesc_line, max_column, sdesc_color, " ");
532 }
533 
534 /*
535  *   Set the score.  If cur == -1, the LAST score set with a non-(-1)
536  *   cur is displayed; this is used to refresh the status line without
537  *   providing a new score (for example, after exiting scrollback mode).
538  *   Otherwise, the given current score (cur) and turncount are displayed,
539  *   and saved in case cur==-1 on the next call.
540  */
os_score(int cur,int turncount)541 void os_score(int cur, int turncount)
542 {
543     char buf[20];
544 
545     /* check for the special -1 turn count */
546     if (turncount == -1)
547     {
548         /* it's turn "-1" - we're simply redrawing the score */
549         os_strsc((char *)0);
550     }
551     else
552     {
553         /* format the score */
554         sprintf(buf, "%d/%d", cur, turncount);
555 
556         /* display the score string */
557         os_strsc(buf);
558     }
559 }
560 
561 
562 /* ------------------------------------------------------------------------ */
563 /*
564  *   Scrollback
565  */
566 # ifdef USE_SCROLLBACK
567 
568 /*
569  *   Pointers for scrollback buffers.
570  *
571  *   We store the text that has been displayed to the screen in a fixed
572  *   buffer.  We allocate out of the buffer circularly; when we reach the end
573  *   of the buffer, we wrap around and allocate out of the beginning of the
574  *   buffer, freeing old records as necessary to make space.
575  *
576  *   'scrbuf' is a pointer to the buffer.  'scrbuf_free' is a pointer to the
577  *   next byte of the buffer available to be allocated.
578  */
579 static osfar_t char *scrbuf;
580 static osfar_t unsigned scrbufl;
581 
582 /*
583  *   Head/tail of linked list of scrollback lines.  Lines are separated by
584  *   null bytes.  Note that 'scrbuf_tail' is a pointer to the START of the
585  *   last line in the buffer (which is usually the line still under
586  *   construction).
587  */
588 static char *scrbuf_head;
589 static char *scrbuf_tail;
590 
591 /* allocation head */
592 static osfar_t char *scrbuf_free;
593 
594 /* number of lines in scrollback buffer */
595 static osfar_t int os_line_count;
596 
597 /* line number of top line on the screen */
598 static osfar_t int os_top_line;
599 
600 /* current column in the scrollback buffer */
601 static osfar_t int sb_column;
602 
603 /*
604  *   Current screen color - this is the color to use for the blank areas of
605  *   the screen.  We use this to clear areas of the screen, to fill in blank
606  *   areas left over after scrolling, and as the default background color to
607  *   display for characters with a "transparent" background.
608  *
609  *   Note that osssb_screen_color is an OSGEN_COLOR_xxx value, and
610  *   osssb_oss_screen_color is the corresponding ossxxx color code for a
611  *   character with that background color.  We keep both values for
612  *   efficiency.
613  */
614 static osfar_t int osssb_screen_color;
615 static osfar_t int osssb_oss_screen_color;
616 
617 /* current color/attribute setting */
618 static osfar_t int osssb_cur_fg;
619 static osfar_t int osssb_cur_bg;
620 static osfar_t int osssb_cur_attrs;
621 
622 /* color/attribute settings at start of current scrollback line */
623 static osfar_t int osssb_sol_fg;
624 static osfar_t int osssb_sol_bg;
625 static osfar_t int osssb_sol_attrs;
626 
627 /*
628  *   Receive notification of a screen size change.
629  */
osssb_on_resize_screen()630 void osssb_on_resize_screen()
631 {
632     /*
633      *   Note the page length of the main text area - this is the distance
634      *   between 'more' prompts.  Use the screen height, minus one for the
635      *   status line, minus two extra lines so we keep a line of context at
636      *   the top of the screen while leaving a line for a "more" prompt at
637      *   the bottom.
638      */
639     G_os_pagelength = (G_oss_screen_height > 3
640                        ? G_oss_screen_height - 3
641                        : G_oss_screen_height);
642 
643     /* the main text area gets the full screen width */
644     G_os_linewidth = G_oss_screen_width;
645 }
646 
647 /*
648  *   Initialize the scrollback buffer - allocates memory for the buffer.
649  *
650  *   Important: when this routine is called, the text_color global variable
651  *   MUST be initialized to the ossdsp-style color code to use for the
652  *   default text area.
653  */
osssbini(unsigned int size)654 void osssbini(unsigned int size)
655 {
656     /* calculate the page size and width based on the screen size */
657     osssb_on_resize_screen();
658 
659     /* remember the size of the buffer */
660     scrbufl = size;
661 
662     /* allocate the scrollback buffer */
663     if ((scrbuf = calloc(scrbufl, 1)) != 0)
664     {
665         /* set up the first and only (and thus last) line pointers */
666         scrbuf_head = scrbuf_tail = scrbuf;
667 
668         /* set up the free pointer */
669         scrbuf_free = scrbuf;
670 
671         /* we have one line in the buffer now */
672         os_line_count = 1;
673 
674         /* start out in normal text color */
675         osssb_cur_fg = OSGEN_COLOR_TEXT;
676         osssb_cur_bg = OSGEN_COLOR_TRANSPARENT;
677         osssb_cur_attrs = 0;
678 
679         /* the start of the scrollback buffer line is the same */
680         osssb_sol_fg = OSGEN_COLOR_TEXT;
681         osssb_sol_bg = OSGEN_COLOR_TRANSPARENT;
682         osssb_sol_attrs = 0;
683 
684         /* set the base screen color to the normal text background color */
685         osssb_screen_color = OSGEN_COLOR_TEXTBG;
686         osssb_oss_screen_color = ossgetcolor(OSGEN_COLOR_TEXT,
687                                              OSGEN_COLOR_TEXTBG, 0, 0);
688     }
689     else
690     {
691         /* mention the problem */
692         os_printz("\nSorry, there is not enough memory available "
693                   "for review mode.\n\n");
694 
695         /* we have no line records */
696         scrbuf_head = 0;
697         scrbuf_tail = 0;
698     }
699 
700     /* clear the status line area */
701     ossclr(sdesc_line, sdesc_column, sdesc_line, max_column, sdesc_color);
702 }
703 
704 /*
705  *   delete the scrollback buffer
706  */
osssbdel(void)707 void osssbdel(void)
708 {
709     /* free the screen buffer */
710     if (scrbuf != 0)
711         free(scrbuf);
712 }
713 
714 /*
715  *   advance to the next byte of the scrollback buffer
716  */
ossadvsp(char * p)717 static char *ossadvsp(char *p)
718 {
719     /* move to the next byte */
720     ++p;
721 
722     /* if we've passed the end of the buffer, wrap to the beginning */
723     if (p >= scrbuf + scrbufl)
724         p = scrbuf;
725 
726     /* return the pointer */
727     return p;
728 }
729 
730 /*
731  *   decrement to the previous byte of the buffer
732  */
ossdecsp(char * p)733 static char *ossdecsp(char *p)
734 {
735     /* if we're at the start of the buffer, wrap to just past the end */
736     if (p == scrbuf)
737         p = scrbuf + scrbufl;
738 
739     /* move to the previous byte */
740     --p;
741 
742     /* return the pointer */
743     return p;
744 }
745 
746 /*
747  *   Add a byte to the scrollback buffer
748  */
osssb_add_byte(char c)749 static void osssb_add_byte(char c)
750 {
751     /* add the character to the buffer */
752     *scrbuf_free = c;
753 
754     /* advance the free pointer */
755     scrbuf_free = ossadvsp(scrbuf_free);
756 
757     /*
758      *   if the free pointer has just collided with the start of the oldest
759      *   line, delete the oldest line
760      */
761     if (scrbuf_free == scrbuf_head)
762     {
763         /*
764          *   delete the oldest line to make room for the new data, by
765          *   advancing the head pointer to just after the next null byte
766          */
767         while (*scrbuf_head != '\0')
768             scrbuf_head = ossadvsp(scrbuf_head);
769 
770         /* skip the null pointer that marks the end of the old first line */
771         scrbuf_head = ossadvsp(scrbuf_head);
772 
773         /* note the loss of a line */
774         --os_line_count;
775     }
776 }
777 
778 /*
779  *   Add an appropriate color code to the scrollback buffer to yield the
780  *   current color setting.
781  */
osssb_add_color_code()782 static void osssb_add_color_code()
783 {
784     /* if we have any attributes, add an attribute code */
785     if (osssb_cur_attrs != 0)
786     {
787         /*
788          *   add the attribute, encoded with our plus-one rule (to avoid
789          *   storing zero bytes as attribute arguments)
790          */
791         osssb_add_byte(OSGEN_ATTR);
792         osssb_add_byte((char)(osssb_cur_attrs + 1));
793     }
794 
795     /* if we're using the plain text color, add a color code */
796     if (osssb_cur_fg != OSGEN_COLOR_TEXT
797         || osssb_cur_bg != OSGEN_COLOR_TRANSPARENT)
798     {
799         /* add the explicit color code */
800         osssb_add_byte(OSGEN_COLOR);
801         osssb_add_byte((char)osssb_cur_fg);
802         osssb_add_byte((char)osssb_cur_bg);
803     }
804 }
805 
806 /*
807  *   Scan a character, counting its contribution to the display column in the
808  *   scrollback.
809  */
osssb_scan_char(char ** p,int * col)810 static void osssb_scan_char(char **p, int *col)
811 {
812     /* see what we have */
813     switch(**p)
814     {
815     case '\n':
816     case '\r':
817     case '\0':
818         /*
819          *   these are all one-byte character sequences that don't contribute
820          *   to the display column - simply skip the input byte
821          */
822         ++(*p);
823         break;
824 
825     case OSGEN_ATTR:
826         /* this is a two-byte non-printing escape sequence */
827         *p += 2;
828         break;
829 
830     case OSGEN_COLOR:
831         /* this is a three-byte non-printing escape sequence */
832         *p += 3;
833         break;
834 
835     default:
836         /*
837          *   anything else is a one-byte display character, so count its
838          *   contribution to the display column and skip the byte
839          */
840         ++(*col);
841         ++(*p);
842         break;
843     }
844 }
845 
846 /*
847  *   Start a new line in the scrollback buffer
848  */
osssb_new_line()849 static void osssb_new_line()
850 {
851     /* add a null byte to mark the end of the current line */
852     osssb_add_byte(0);
853 
854     /* remember the new final line - it starts at the free pointer */
855     scrbuf_tail = scrbuf_free;
856 
857     /* count the added line */
858     ++os_line_count;
859 
860     /* the new line starts at the first column */
861     sb_column = 0;
862 
863     /* add a color code to the start of the new line, if necessary */
864     osssb_add_color_code();
865 
866     /* remember the text color at the start of this line */
867     osssb_sol_fg = osssb_cur_fg;
868     osssb_sol_bg = osssb_cur_bg;
869     osssb_sol_attrs = osssb_cur_attrs;
870 }
871 
872 /*
873  *   Add text to the scrollback buffer.  Returns the number of lines that the
874  *   added text spans.
875  */
ossaddsb(const char * p,size_t len)876 static int ossaddsb(const char *p, size_t len)
877 {
878     int line_cnt;
879 
880     /* if there's no scrollback buffer, ignore it */
881     if (scrbuf == 0)
882         return 0;
883 
884     /* presume the text will all fit on one line */
885     line_cnt = 1;
886 
887     /*
888      *   Copy the text into the screen buffer, respecting the circular nature
889      *   of the screen buffer.  If the given text wraps lines, enter an
890      *   explicit carriage return into the text to ensure that users can
891      *   correctly count lines.
892      */
893     while(len != 0)
894     {
895         /* check for color control codes */
896         switch(*p)
897         {
898         case OSGEN_ATTR:
899             /*
900              *   switch to the new attributes (decoding from the plus-one
901              *   storage code)
902              */
903             osssb_cur_attrs = ((unsigned char)*(p+1)) - 1;
904             break;
905 
906         case OSGEN_COLOR:
907             /* switch to the explicit colors */
908             osssb_cur_fg = (unsigned char)*(p+1);
909             osssb_cur_bg = (unsigned char)*(p+2);
910             break;
911         }
912 
913         /* check for special characters */
914         switch(*p)
915         {
916         case '\n':
917             /* add the new line */
918             osssb_new_line();
919 
920             /* skip this character */
921             ++p;
922             --len;
923 
924             /* count the new line */
925             ++line_cnt;
926 
927             /* done */
928             break;
929 
930         case '\r':
931             /*
932              *   We have a plain carriage return, which indicates that we
933              *   should go back to the start of the current line and
934              *   overwrite it.  (This is most likely to occur for the
935              *   "[More]" prompt.)  Go back to the first column.
936              */
937             sb_column = 0;
938 
939             /* set the free pointer back to the start of the current line */
940             scrbuf_free = scrbuf_tail;
941 
942             /* switch back to the color as of the start of the line */
943             osssb_cur_fg = osssb_sol_fg;
944             osssb_cur_bg = osssb_sol_bg;
945             osssb_cur_attrs = osssb_sol_attrs;
946 
947             /*
948              *   since we're back at the start of the line, add a color code
949              *   if necessary
950              */
951             osssb_add_color_code();
952 
953             /* skip this character */
954             ++p;
955             --len;
956 
957             /* done */
958             break;
959 
960         case OSGEN_ATTR:
961             /* it's a two-byte attribute sequence - add the bytes */
962             osssb_add_byte(*p);
963             osssb_add_byte(*(p+1));
964 
965             /* skip the sequence */
966             p += 2;
967             len -= 2;
968 
969             /* done (since this doesn't print) */
970             break;
971 
972         case OSGEN_COLOR:
973             /* it's a three-byte color sequence - add the bytes */
974             osssb_add_byte(*p);
975             osssb_add_byte(*(p+1));
976             osssb_add_byte(*(p+2));
977 
978             /* skip the sequence */
979             p += 3;
980             len -= 3;
981 
982             /*
983              *   done (this doesn't print, so it has no effect on the column
984              *   position or wrapping)
985              */
986             break;
987 
988         default:
989             /* for anything else, simply store the byte in the buffer */
990             osssb_add_byte(*p);
991 
992             /* skip the character */
993             ++p;
994             --len;
995 
996             /* advance to the next column */
997             ++sb_column;
998 
999             /* check for wrapping */
1000             if (sb_column > max_column)
1001             {
1002                 /* start a new line */
1003                 osssb_new_line();
1004 
1005                 /* count the new line */
1006                 ++line_cnt;
1007             }
1008 
1009             /* done */
1010             break;
1011         }
1012     }
1013 
1014     /*
1015      *   Always make sure we have a null terminator after each addition, in
1016      *   case we need to look at the buffer before this line is finished.
1017      *   However, since we're just tentatively writing the null terminator,
1018      *   back up the free pointer to point to it after we write it, so that
1019      *   the next character we write will go here.
1020      */
1021     osssb_add_byte(0);
1022     scrbuf_free = ossdecsp(scrbuf_free);
1023 
1024     /* return the number of lines we added */
1025     return line_cnt;
1026 }
1027 
1028 /*
1029  *   A slight complication: if we're not on the very bottom line, and we
1030  *   don't even have any text displayed on the bottom line (example: the
1031  *   input line grew to over 80 characters, scrolling up a line, but shrunk
1032  *   again due to deletions), we must add blank lines to fill out the unused
1033  *   space, so that the scrollback line counter doesn't get confused (it
1034  *   counts backwards from the bottom of the screen, where it assumes we are
1035  *   right now).  Figure out if the current buffer will go to the end of the
1036  *   screen.
1037  *
1038  *   This routine adds the given text to the screen buffer, then adds as many
1039  *   blank lines as are necessary to fill the screen to the bottom line.  buf
1040  *   is the buffer to add; p is a pointer into the buffer corresponding to
1041  *   screen position (x,y).
1042  */
ossaddsbe(char * buf,char * p,int x,int y)1043 static void ossaddsbe(char *buf, char *p, int x, int y)
1044 {
1045     int extra_lines;
1046     int actual_lines;
1047 
1048     /*
1049      *   compute the number of blank lines we need to add - we need one blank
1050      *   line for each line on the screen beyond the display position
1051      */
1052     extra_lines = max_line - y;
1053 
1054     /* add the text */
1055     actual_lines = ossaddsb(buf, strlen(buf));
1056 
1057     /*
1058      *   if we added more than one line already, those count against the
1059      *   extra lines we need to add
1060      */
1061     extra_lines -= (actual_lines - 1);
1062 
1063     /* add the extra blank lines */
1064     for ( ; extra_lines > 0 ; --extra_lines)
1065         osssb_new_line();
1066 
1067     /* add a tentative null terminator */
1068     osssb_add_byte(0);
1069     scrbuf_free = ossdecsp(scrbuf_free);
1070 }
1071 
1072 /*
1073  *   Scrollback mode variables.  These track the current position while
1074  *   in scrollback mode.
1075  */
1076 char osfar_t *scrtop;                 /* first line displayed on the screen */
1077 static osfar_t char *scrbot;                /* last line displayed on sreen */
1078 static osfar_t char *scrlast;               /* first line of last screenful */
1079 
1080 /*
1081  *   scrdsp  - display a line of text from the scrollback buffer on the
1082  *   screen.  It is the responsibility of the caller to ensure that the
1083  *   line on the screen has been cleared beforehand.
1084  */
scrdsp(char * p,int y)1085 static void scrdsp(char *p, int y)
1086 {
1087     char  buf[127];
1088     char *q = buf;
1089     int   x = text_column;
1090     int   fg;
1091     int   bg;
1092     int   attrs;
1093     int   oss_color;
1094 
1095     /* start out in normal text color */
1096     fg = OSGEN_COLOR_TEXT;
1097     bg = OSGEN_COLOR_TRANSPARENT;
1098     attrs = 0;
1099     oss_color = ossgetcolor(fg, bg, attrs, osssb_screen_color);
1100 
1101     /* keep going until we reach the end of the line */
1102     while (*p != '\0')
1103     {
1104         /* check the character */
1105         switch(*p)
1106         {
1107         case OSGEN_ATTR:
1108             /* set the new attributes (decoding from the plus-one value) */
1109             attrs = (unsigned char)*(p = ossadvsp(p)) - 1;
1110 
1111             /* handle it via the common color-switching code */
1112             goto change_color;
1113 
1114         case OSGEN_COLOR:
1115             /* explicit color code - read the two extra bytes */
1116             fg = *(p = ossadvsp(p));
1117             bg = *(p = ossadvsp(p));
1118 
1119             /* handle it via the common color-switching code */
1120             goto change_color;
1121 
1122         change_color:
1123             /* null-terminate the buffer and display it in the old color */
1124             *q = '\0';
1125             ossdsp(y, x, oss_color, buf);
1126 
1127             /* switch to the new color */
1128             oss_color = ossgetcolor(fg, bg, attrs, osssb_screen_color);
1129 
1130             /* advance the column counter */
1131             x += strlen(buf);
1132 
1133             /* go back to start of buffer */
1134             q = buf;
1135             break;
1136 
1137         default:
1138             /* add this byte to our buffer */
1139             *q++ = *p;
1140             break;
1141         }
1142 
1143         /* advance to next character no matter what happened */
1144         p = ossadvsp(p);
1145 
1146         /* if the buffer's full, flush it */
1147         if (q == buf + sizeof(buf) - 1)
1148         {
1149             /* flush out the buffer */
1150             *q = '\0';
1151             ossdsp(y, x, oss_color, buf);
1152 
1153             /* advance the column counter */
1154             x += strlen(buf);
1155 
1156             /* go back to start of buffer */
1157             q = buf;
1158         }
1159     }
1160 
1161     /* flush out what's left in the buffer */
1162     *q = '\0';
1163     ossdsp(y, x, oss_color, buf);
1164 }
1165 
1166 /*
1167  *   scrdspscr - display a screenful of text starting at a given location
1168  *   in the scrollback buffer.
1169  */
scrdspscr(char * p)1170 static void scrdspscr(char *p)
1171 {
1172     int y;
1173 
1174     /* clear the screen area of our display */
1175     ossclr(text_line, text_column, max_line, max_column,
1176            osssb_oss_screen_color);
1177 
1178     /* display each line of this area */
1179     for (y = text_line ; y <= max_line ; ++y)
1180     {
1181         /* display this line */
1182         scrdsp(p, y);
1183 
1184         /* if we just displayed the last line, we're done */
1185         if (p == scrbuf_tail)
1186             break;
1187 
1188         /* advance to the next line */
1189         while (*p != '\0')
1190             p = ossadvsp(p);
1191 
1192         /* skip the line separator */
1193         p = ossadvsp(p);
1194     }
1195 }
1196 
1197 /*
1198  *   Advance a buffer pointer until it's pointing to the start of the next
1199  *   line.  Returns true if we advanced the pointer, false if we were already
1200  *   at the end of the buffer.
1201  */
osssb_next_line(char ** p)1202 static int osssb_next_line(char **p)
1203 {
1204     /* if we're at the last line already, there's nothing to do */
1205     if (*p == scrbuf_tail)
1206         return FALSE;
1207 
1208     /* advance until we find the current line's terminating null byte */
1209     while (**p != '\0')
1210         *p = ossadvsp(*p);
1211 
1212     /* skip the null byte */
1213     *p = ossadvsp(*p);
1214 
1215     /* we did advance by a line */
1216     return TRUE;
1217 }
1218 
1219 /*
1220  *   Move a buffer pointer backward to the start of the current line.
1221  */
osssb_to_start(char * p)1222 static char *osssb_to_start(char *p)
1223 {
1224     /* keep going as long as we don't reach the start of the first line */
1225     while (p != scrbuf_head)
1226     {
1227         /* move back a byte */
1228         p = ossdecsp(p);
1229 
1230         /* if it's a null byte, we've found the end of the previous line */
1231         if (*p == '\0')
1232             return ossadvsp(p);
1233     }
1234 
1235     /*
1236      *   return the pointer (which is at the start of the first line if we
1237      *   got here)
1238      */
1239     return p;
1240 }
1241 
1242 /*
1243  *   Move a buffer pointer backward to the start of the previous line.
1244  *   Returns true if we moved the pointer, false if we were already in the
1245  *   first line.
1246  */
osssb_prev_line(char ** p)1247 static int osssb_prev_line(char **p)
1248 {
1249     /* first, make sure we're at the start of the current line */
1250     *p = osssb_to_start(*p);
1251 
1252     /* if we're at the start of the first line, we can't go back */
1253     if (*p == scrbuf_head)
1254         return FALSE;
1255 
1256     /* move back to the null byte at the end of the previous line */
1257     *p = ossdecsp(*p);
1258 
1259     /* go back to the start of this line */
1260     *p = osssb_to_start(*p);
1261 
1262     /* we moved the pointer */
1263     return TRUE;
1264 }
1265 
1266 /*
1267  *   move forward by a number of lines, and redisplays the sreen
1268  */
scrfwd(int n)1269 static void scrfwd(int n)
1270 {
1271     /* if we're already on the last screen, there's nothing to do */
1272     if (scrtop == scrlast)
1273         return;
1274 
1275     /*
1276      *   Move forward by the requested amount.  Stop if we reach the top of
1277      *   the bottom-most screen.
1278      */
1279     while (n != 0)
1280     {
1281         /* move to the next line at the bottom */
1282         if (!osssb_next_line(&scrbot))
1283             break;
1284 
1285         /* move to the next line at the top as well */
1286         osssb_next_line(&scrtop);
1287 
1288         /* update our display counter */
1289         ++os_top_line;
1290 
1291         /* that's one less line to advance by */
1292         --n;
1293 
1294     }
1295 
1296     /* re-display the screen with the new top line */
1297     scrdspscr(scrtop);
1298 }
1299 
1300 /*
1301  *   move back by a number of lines, redisplaying the screen
1302  */
scrback(int n)1303 static void scrback(int n)
1304 {
1305     /* if we can't go back any further, we're done */
1306     if (scrtop == scrbuf_head)
1307         return;
1308 
1309     /* keep going until we satisfy the request */
1310     while (n != 0 && scrtop != scrbuf_head)
1311     {
1312         /*
1313          *   back up one line - if we can't (because we're already at the
1314          *   first line), stop trying
1315          */
1316         if (!osssb_prev_line(&scrtop))
1317             break;
1318 
1319         /* back up one at the bottom */
1320         osssb_prev_line(&scrbot);
1321 
1322         /* that's one less to look for */
1323         --n;
1324 
1325         /* adjust our line position */
1326         --os_top_line;
1327     }
1328 
1329     /* redisplay the screen */
1330     scrdspscr(scrtop);
1331 }
1332 
1333 /*
1334  *   scrto moves to a selected line
1335  */
scrto(int lin)1336 void scrto(int lin)
1337 {
1338     if (lin > os_top_line)
1339         scrfwd(lin - os_top_line);
1340     else if (lin < os_top_line)
1341         scrback(os_top_line - lin);
1342 }
1343 
1344 /*
1345  *   scrpgup scrolls back a page
1346  */
scrpgup(void)1347 void scrpgup(void)
1348 {
1349     scrback(max_line - text_line);
1350 }
1351 
1352 /*
1353  *   scrpgdn scrolls forward a page
1354  */
scrpgdn(void)1355 void scrpgdn(void)
1356 {
1357     scrfwd(max_line - text_line);
1358 }
1359 
1360 /*
1361  *   scrlnup scrolls up one line
1362  */
scrlnup(void)1363 void scrlnup(void)
1364 {
1365     /* move the top pointer back a line */
1366     if (osssb_prev_line(&scrtop))
1367     {
1368         /* success - move the bottom pointer back a line as well */
1369         osssb_prev_line(&scrbot);
1370 
1371         /* adjust our line counter */
1372         --os_top_line;
1373 
1374         /* scroll the display area */
1375         ossscu(text_line, text_column, max_line, max_column,
1376                osssb_oss_screen_color);
1377 
1378         /* redisplay the top line */
1379         scrdsp(scrtop, text_line);
1380     }
1381 }
1382 
1383 /*
1384  *   scrlndn scrolls down one line
1385  */
scrlndn(void)1386 void scrlndn(void)
1387 {
1388     /* if we're already at the last screenful, there's nothing to do */
1389     if (scrtop == scrlast)
1390         return;
1391 
1392     /* move the bottom pointer up a line */
1393     if (osssb_next_line(&scrbot))
1394     {
1395         /* success - move the top pointer up a line as well */
1396         osssb_next_line(&scrtop);
1397 
1398         /* adjust our line counter */
1399         ++os_top_line;
1400 
1401         /* scroll the display area */
1402         ossscr(text_line, text_column, max_line, max_column,
1403                osssb_oss_screen_color);
1404 
1405         /* redisplay the bottom line */
1406         scrdsp(scrbot, max_line);
1407     }
1408 }
1409 
1410 /* redraw the screen's text region */
os_redraw(void)1411 void os_redraw(void)
1412 {
1413     int y;
1414     char *p;
1415 
1416     /* clear the area */
1417     ossclr(text_line, text_column, max_line, max_column,
1418            osssb_oss_screen_color);
1419 
1420     /* find the top line in the display area */
1421     p = scrbuf_tail;
1422     for (y = max_line ; y >= text_line ; --y)
1423     {
1424         /* display the current line at the current position */
1425         scrdsp(p, y);
1426 
1427         /* move back a line if possible */
1428         if (!osssb_prev_line(&p))
1429             break;
1430     }
1431 }
1432 
1433 /*
1434  *   osssbmode toggles scrollback mode.  Once in scrollback mode, calls
1435  *   can be made to osssbup, osssbdn, osssbpgup, and osspgdn to scroll
1436  *   through the saved text.  We also put up a scrollback-mode version of
1437  *   the status line, showing keys that should be used for scrollback.
1438  *   When the user wants to exit scrollback mode, this routine should
1439  *   be called again.
1440  *
1441  *   Returns:  0 if scrollback mode is entered/exited successfully, 1 if
1442  *   an error occurs.  Note that it may not be possible to enter scrollback
1443  *   mode, since insufficient saved text may have been accumulated.
1444  */
osssbmode(int mode_line)1445 int osssbmode(int mode_line)
1446 {
1447     /* if there's no buffer, we can't enter scrollback mode */
1448     if (scrbuf == 0)
1449         return 1;
1450 
1451     /*
1452      *   if we're not in scrollback mode, enter scrollback mode; otherwise,
1453      *   return to normal mode
1454      */
1455     if (scrtop == 0)
1456     {
1457         int y;
1458         int i;
1459         char buf[135];
1460 
1461         /*
1462          *   Enter scrollback mode.  Figure out what scrtop should be, and
1463          *   put up the scrollback status line.  If insufficient saved text
1464          *   is around for scrollback mode, return 1.
1465          */
1466         for (os_top_line = os_line_count, scrtop = scrbuf_tail,
1467              y = max_line ; y > text_line ; --y, --os_top_line)
1468         {
1469             /* back up one line */
1470             if (!osssb_prev_line(&scrtop))
1471             {
1472                 /* there wasn't enough text, so abort and return failure */
1473                 scrtop = 0;
1474                 return 1;
1475             }
1476         }
1477 
1478         /*
1479          *   if the top of the screen is the very first line in the buffer,
1480          *   there's still not enough scrollback information
1481          */
1482         if (scrtop == scrbuf_head)
1483         {
1484             /* no point in scrollback when we have exactly one screen */
1485             scrtop = 0;
1486             return 1;
1487         }
1488 
1489         /* remember where current screen starts and ends */
1490         scrlast = scrtop;
1491         scrbot = scrbuf_tail;
1492 
1493         /* display instructions if we have a status line */
1494         if (mode_line)
1495         {
1496             strcpy(buf, OS_SBSTAT);
1497             for (i = strlen(buf) ; i < max_column ; buf[i++] = ' ') ;
1498             buf[i] = '\0';
1499             ossdsp(sdesc_line, sdesc_column+1, sdesc_color, buf);
1500         }
1501 
1502         /* successfully entered scrollback mode */
1503         return 0;
1504     }
1505     else
1506     {
1507         /*
1508          *   Exit scrollback mode.  Show the last page of text, and put
1509          *   up the normal status bar.  Also clear our scrollback mode
1510          *   variables.
1511          */
1512         if (scrlast != scrtop)
1513             scrdspscr(scrlast);
1514 
1515         /* re-display the status line, if appropriate */
1516         if (mode_line)
1517         {
1518             /* display the last status line buffer */
1519             ossdspn(sdesc_line, sdesc_column, sdesc_color, " ");
1520             ossdspn(sdesc_line, sdesc_column + 1, sdesc_color, S_statbuf);
1521 
1522             /*
1523              *   refresh the right-hand side with the score part - do this
1524              *   by drawing the score with a special turn counter of -1 to
1525              *   indicate that we just want to refresh the previous score
1526              *   value
1527              */
1528             os_score(-1, -1);
1529         }
1530         scrtop = 0;
1531         return( 0 );
1532     }
1533 }
1534 
1535 /*
1536  *   ossdosb runs a scrollback session.  When entered, osssbmode()
1537  *   must already have been called.  When we're done, we'll be out
1538  *   of scrollback mode.
1539  */
ossdosb(void)1540 static void ossdosb(void)
1541 {
1542     for ( ;; )
1543     {
1544         if (!os_getc())
1545         {
1546             switch(os_getc())
1547             {
1548             case CMD_SCR:
1549             case CMD_KILL:         /* leave scrollback via 'escape' as well */
1550             case CMD_EOF:                            /* stop on end of file */
1551                 osssbmode(1);
1552                 return;
1553             case CMD_UP:
1554                 scrlnup();
1555                 break;
1556             case CMD_DOWN:
1557                 scrlndn();
1558                 break;
1559             case CMD_PGUP:
1560                 scrpgup();
1561                 break;
1562             case CMD_PGDN:
1563                 scrpgdn();
1564                 break;
1565             }
1566         }
1567     }
1568 }
1569 
1570 # else /* USE_SCROLLBACK */
ossaddsb(char * p,size_t len)1571 static void ossaddsb(char *p, size_t len)
1572 {
1573     /* We're not saving output - do nothing */
1574 }
1575 # endif /* USE_SCROLLBACK */
1576 
1577 /* display with no highlighting - intercept and ignore highlight codes */
ossdspn(int y,int x,int color,char * p)1578 void ossdspn(int y, int x, int color, char *p)
1579 {
1580     char *q;
1581     int esc_len;
1582 
1583     /* scan the string */
1584     for (q = p ; *q ; ++q)
1585     {
1586         switch(*q)
1587         {
1588         case OSGEN_COLOR:
1589             /* three-byte escape sequence */
1590             esc_len = 3;
1591             goto skip_esc;
1592 
1593         case OSGEN_ATTR:
1594             /* two-byte escape sequence */
1595             esc_len = 2;
1596             goto skip_esc;
1597 
1598         skip_esc:
1599             /* end the buffer here and display what we have so far */
1600             *q = '\0';
1601             ossdsp(y, x, color, p);
1602 
1603             /* adjust the column position for the display */
1604             x += strlen(p);
1605 
1606             /* advance past what we've displayed plus the escape sequence */
1607             p = q + esc_len;
1608             break;
1609 
1610         default:
1611             /* ordinary character - keep going */
1612             break;
1613         }
1614     }
1615 
1616     /* display the remainder of the buffer, if there's anything left */
1617     if (q != p)
1618         ossdsp(y, x, color, p);
1619 }
1620 
1621 /* display with highlighting enabled */
ossdsph(int y,int x,int color,char * p)1622 int ossdsph(int y, int x, int color, char *p)
1623 {
1624     char *q;
1625     int len;
1626     int esc_len;
1627 
1628     /* get length of entire string */
1629     len = strlen(p);
1630 
1631     /* scan the input string */
1632     for (q = p ; *q != '\0' ; ++q)
1633     {
1634         /* check for special escape codes */
1635         switch(*q)
1636         {
1637         case OSGEN_ATTR:
1638             /* set text attributes (decoding from the plus-one code) */
1639             osssb_cur_attrs = (unsigned char)*(q+1) - 1;
1640             esc_len = 2;
1641             goto change_color;
1642 
1643         case OSGEN_COLOR:
1644             /* set explicit color */
1645             osssb_cur_fg = *(q+1);
1646             osssb_cur_bg = *(q+2);
1647             esc_len = 3;
1648             goto change_color;
1649 
1650         change_color:
1651             /* display the part up to the escape code in the old color */
1652             *q = '\0';
1653             ossdsp(y, x, text_color, p);
1654 
1655             /* adjust the column position for the display */
1656             x += strlen(p);
1657 
1658             /* note the new text color */
1659             text_color = ossgetcolor(osssb_cur_fg, osssb_cur_bg,
1660                                      osssb_cur_attrs, osssb_screen_color);
1661 
1662             /* move past the escape sequence */
1663             p = q + esc_len;
1664             q += esc_len - 1;
1665 
1666             /* don't count the escape character in the length displayed */
1667             len -= esc_len;
1668             break;
1669 
1670         default:
1671             break;
1672         }
1673     }
1674 
1675     /* display the last portion of the line if anything's left */
1676     if (q != p)
1677         ossdsp(y, x, text_color, p);
1678 
1679     /* return the length */
1680     return len;
1681 }
1682 
1683 /*
1684  *   Set the terminal into 'plain' mode: disables status line,
1685  *   scrollback, command editing.
1686  */
os_plain(void)1687 void os_plain(void)
1688 {
1689     /* set the 'plain' mode flag */
1690     os_f_plain = 1;
1691 }
1692 
os_printz(const char * str)1693 void os_printz(const char *str)
1694 {
1695     /* use our base counted-length routine */
1696     os_print(str, strlen(str));
1697 }
1698 
os_print(const char * str,size_t len)1699 void os_print(const char *str, size_t len)
1700 {
1701     /*
1702      *   save the output in the scrollback buffer if we're displaying to
1703      *   the main text area (status_mode == 0) and we're not in plain
1704      *   stdio mode (in which case there's no scrollback support)
1705      */
1706     if (status_mode == 0 && !os_f_plain)
1707         ossaddsb(str, len);
1708 
1709     /* determine what to do based on the status mode */
1710     switch(status_mode)
1711     {
1712     case 2:
1713         /* we're in the post-status-line mode - suppress all output */
1714         break;
1715 
1716     case 0:
1717         /* normal main text area mode */
1718         {
1719             const char *p;
1720             size_t rem;
1721             char *dst;
1722             char buf[128];
1723 
1724             /* scan the buffer */
1725             for (p = str, rem = len, dst = buf ; ; ++p, --rem)
1726             {
1727                 /*
1728                  *   if we're out of text, or the buffer is full, or we're at
1729                  *   a newline or carriage return, flush the buffer
1730                  */
1731                 if (rem == 0
1732                     || dst == buf + sizeof(buf) - 2
1733                     || *p == '\n' || *p == '\r')
1734                 {
1735                     /* write out the buffer */
1736                     if (os_f_plain)
1737                     {
1738                         /* write the buffer, INCLUDING the LF/CR */
1739                         if (*p == '\n' || *p == '\r')
1740                             *dst++ = *p;
1741                         *dst = '\0';
1742                         fputs(buf, stdout);
1743                     }
1744                     else
1745                     {
1746                         size_t len;
1747 
1748                         /* display the buffer */
1749                         *dst = '\0';
1750                         len = ossdsph(max_line, text_lastcol,
1751                                       text_color, buf);
1752 
1753                         /* move the cursor right */
1754                         text_lastcol += len;
1755 
1756                         /* scroll on a newline */
1757                         if (*p == '\n')
1758                         {
1759                             /* scroll up a line */
1760                             ossscr(text_line, text_column,
1761                                    max_line, max_column,
1762                                    osssb_oss_screen_color);
1763                         }
1764 
1765                         /* move the cursor to the left column on LF/CR */
1766                         if (*p == '\n' || *p == '\r')
1767                             text_lastcol = text_column;
1768                     }
1769 
1770                     /* reset the buffer */
1771                     dst = buf;
1772 
1773                     /* if we're out of source text, we're done */
1774                     if (rem == 0)
1775                         break;
1776                 }
1777 
1778                 /* see what we have */
1779                 switch(*p)
1780                 {
1781                 case '\n':
1782                 case '\r':
1783                     /* don't buffer newlines/carriage returns */
1784                     break;
1785 
1786                 default:
1787                     /* buffer this character */
1788                     *dst++ = *p;
1789                     break;
1790                 }
1791             }
1792 
1793             /* put the cursor at the end of the text */
1794             if (status_mode == 0 && !os_f_plain)
1795                 ossloc(max_line, text_lastcol);
1796         }
1797 
1798         /* done */
1799         break;
1800 
1801     case 1:
1802         /* status-line mode - ignore in 'plain' mode */
1803         if (!os_f_plain)
1804         {
1805             size_t rem;
1806             const char *p;
1807             int i;
1808 
1809             /*
1810              *   Skip leading newlines at the start of the statusline output.
1811              *   Only do this if we don't already have anything buffered,
1812              *   since a newline after some other text indicates the end of
1813              *   the status line and thus can't be ignored.
1814              */
1815             p = str;
1816             rem = len;
1817             if (S_statptr == S_statbuf)
1818             {
1819                 /* the buffer is empty, so skip leading newlines */
1820                 for ( ; rem != 0 && *p == '\n' ; ++p, --rem) ;
1821             }
1822 
1823             /*
1824              *   Add this text to our private copy, so that we can refresh
1825              *   the display later if necessary.  If we reach a newline,
1826              *   stop.
1827              */
1828             for ( ; (rem != 0 && *p != '\n'
1829                      && S_statptr < S_statbuf + sizeof(S_statbuf)) ;
1830                   ++p, --rem)
1831             {
1832                 /* omit special characters from the status buffer */
1833                 if (*p == OSGEN_ATTR)
1834                 {
1835                     /* skip an extra byte for the attribute code */
1836                     ++p;
1837                     continue;
1838                 }
1839                 else if (*p == OSGEN_COLOR)
1840                 {
1841                     /* skip two extra bytes for the color codes */
1842                     p += 2;
1843                     continue;
1844                 }
1845 
1846                 /* copy this character */
1847                 *S_statptr++ = *p;
1848             }
1849 
1850             /* if we reached a newline, we're done with the status line */
1851             if (rem != 0 && *p == '\n')
1852                 os_status(2);
1853 
1854             /* add spaces up to the score location */
1855             for (i = S_statptr - S_statbuf ; i < score_column - 1 ; ++i)
1856                 S_statbuf[i] = ' ';
1857 
1858             /* null-terminate the buffer */
1859             S_statbuf[i] = '\0';
1860 
1861             /* display a leading space */
1862             ossdspn(sdesc_line, sdesc_column, sdesc_color, " ");
1863 
1864             /* display the string */
1865             ossdspn(sdesc_line, sdesc_column + 1, sdesc_color, S_statbuf);
1866         }
1867 
1868         /* done */
1869         break;
1870     }
1871 }
1872 
os_flush(void)1873 void os_flush(void)
1874 {
1875     /* we don't buffer our output, so there's nothing to do here */
1876 }
1877 
os_update_display(void)1878 void os_update_display(void)
1879 {
1880     /* there's nothing we need to do */
1881 }
1882 
1883 # ifdef USE_HISTORY
1884 /*
1885  *   For command line history, we must have some buffer space to store
1886  *   past command lines.  We will use a circular buffer:  when we move
1887  *   the pointer past the end of the buffer, it wraps back to the start
1888  *   of the buffer.  A "tail" indicates the oldest line in the buffer;
1889  *   when we need more room for new text, we advance the tail and thereby
1890  *   lose the oldest text in the buffer.
1891  */
1892 static osfar_t unsigned char *histbuf = 0;
1893 static osfar_t unsigned char *histhead = 0;
1894 static osfar_t unsigned char *histtail = 0;
1895 
1896 /*
1897  *   ossadvhp advances a history pointer, and returns the new pointer.
1898  *   This function takes the circular nature of the buffer into account
1899  *   by wrapping back to the start of the buffer when it hits the end.
1900  */
ossadvhp(uchar * p)1901 uchar *ossadvhp(uchar *p)
1902 {
1903     if (++p >= histbuf + HISTBUFSIZE)
1904         p = histbuf;
1905     return p;
1906 }
1907 
1908 /*
1909  *   ossdechp decrements a history pointer, wrapping the pointer back
1910  *   to the top of the buffer when it reaches the bottom.
1911  */
ossdechp(uchar * p)1912 uchar *ossdechp(uchar *p)
1913 {
1914     if (p == histbuf)
1915         p = histbuf + HISTBUFSIZE;
1916     return p - 1;
1917 }
1918 
1919 /*
1920  *  osshstcpy copies from a history buffer into a contiguous destination
1921  *  buffer, wrapping the history pointer if need be.  One null-terminated
1922  *  string is copied.
1923  */
osshstcpy(uchar * dst,uchar * hst)1924 void osshstcpy(uchar *dst, uchar *hst)
1925 {
1926     while( *hst )
1927     {
1928         *dst++ = *hst;
1929         hst = ossadvhp( hst );
1930     }
1931     *dst = '\0';
1932 }
1933 
1934 /*
1935  *   ossprvcmd returns a pointer to the previous history command, given
1936  *   a pointer to a history command.  It returns a null pointer if the
1937  *   given history command is the first in the buffer.
1938  */
ossprvcmd(uchar * hst)1939 uchar *ossprvcmd(uchar *hst)
1940 {
1941     if (hst == histtail)
1942         return 0;                              /* no more previous commands */
1943     hst = ossdechp( hst );                                  /* back onto nul */
1944     do
1945     {
1946         hst = ossdechp( hst );                        /* back one character */
1947     } while (*hst && hst != histtail);         /* go until previous command */
1948     if (*hst == 0)
1949         hst = ossadvhp(hst);                         /* step over null byte */
1950     return hst;
1951 }
1952 
1953 /*
1954  *   ossnxtcmd returns a pointer to the next history command, given
1955  *   a pointer to a history command.  It returns a null pointer if the
1956  *   given command is already past the last command.
1957  */
ossnxtcmd(uchar * hst)1958 uchar *ossnxtcmd(uchar *hst)
1959 {
1960     if ( hst == histhead ) return( 0 );         /* past the last one already */
1961     while( *hst ) hst = ossadvhp( hst );             /* scan forward to null */
1962     hst = ossadvhp( hst );                /* scan past null onto new command */
1963     return( hst );
1964 }
1965 # endif /* USE_HISTORY */
1966 
1967 /* ------------------------------------------------------------------------ */
1968 /*
1969  *   Input buffer state.  This information is defined statically because
1970  *   os_gets_timeout() can carry the information from invocation to
1971  *   invocation when input editing is interrupted by a tmieout.
1972  */
1973 static osfar_t char S_gets_internal_buf[256];       /* internal save buffer */
1974 static osfar_t char *S_gets_buf = S_gets_internal_buf;  /* current save buf */
1975 static osfar_t size_t S_gets_buf_siz = sizeof(S_gets_internal_buf); /* size */
1976 static osfar_t int S_gets_ofs;       /* offset in buffer of insertion point */
1977 static osfar_t unsigned char *S_gets_curhist;    /* current history pointer */
1978 static osfar_t int S_gets_x, S_gets_y;             /* saved cursor position */
1979 
1980 # ifdef USE_HISTORY
1981 /* save buffer for line being edited before history recall began */
1982 static osfar_t char S_hist_sav_internal[256];
1983 static osfar_t char *S_hist_sav = S_hist_sav_internal;
1984 static osfar_t size_t S_hist_sav_siz = sizeof(S_hist_sav_internal);
1985 # endif /* USE_HISTORY */
1986 
1987 /*
1988  *   Flag: input is already in progress.  When os_gets_timeout() returns
1989  *   with OS_EVT_TIMEOUT, it sets this flag to true.  os_gets_cancel() sets
1990  *   this flag to false.
1991  *
1992  *   When os_gets_timeout() is called again, it checks this flag to see if
1993  *   the input session was cancelled; if not, the routine knows that the
1994  *   partially-edited input line is already displayed where it left off,
1995  *   because the display has not been modified since the interrupted call to
1996  *   os_gets_timeout() returned.
1997  */
1998 static osfar_t int S_gets_in_progress = FALSE;
1999 
2000 /* ------------------------------------------------------------------------ */
2001 /*
2002  *   Display a string from the given character position.  This does NOT
2003  *   scroll the screen.
2004  */
ossdsp_str(int y,int x,int color,unsigned char * str,size_t len)2005 static void ossdsp_str(int y, int x, int color,
2006                        unsigned char *str, size_t len)
2007 {
2008     /* keep going until we exhaust the string */
2009     while (len != 0)
2010     {
2011         size_t cur;
2012         unsigned char oldc;
2013 
2014         /* display as much as will fit on the current line */
2015         cur = max_column - x + 1;
2016         if (cur > len)
2017             cur = len;
2018 
2019         /* null-terminate the chunk, but save the original character */
2020         oldc = str[cur];
2021         str[cur] = '\0';
2022 
2023         /* display this chunk */
2024         ossdsp(y, x, color, (char *)str);
2025 
2026         /* restore the character where we put our null */
2027         str[cur] = oldc;
2028 
2029         /* move our string counters past this chunk */
2030         str += cur;
2031         len -= cur;
2032 
2033         /* advance the x position */
2034         x += cur;
2035         if (x > max_column)
2036         {
2037             x = 0;
2038             ++y;
2039         }
2040     }
2041 }
2042 
2043 /*
2044  *   Display a string from the given character position, scrolling the
2045  *   screen if necessary.
2046  */
ossdsp_str_scr(int * y,int * x,int color,unsigned char * str,size_t len)2047 static void ossdsp_str_scr(int *y, int *x, int color,
2048                            unsigned char *str, size_t len)
2049 {
2050     /* keep going until we exhaust the string */
2051     while (len != 0)
2052     {
2053         size_t cur;
2054         unsigned char oldc;
2055 
2056         /* display as much as will fit on the current line */
2057         cur = max_column - *x + 1;
2058         if (cur > len)
2059             cur = len;
2060 
2061         /* null-terminate the chunk, but save the original character */
2062         oldc = str[cur];
2063         str[cur] = '\0';
2064 
2065         /* display this chunk */
2066         ossdsp(*y, *x, color, (char *)str);
2067 
2068         /* restore the character where we put our null */
2069         str[cur] = oldc;
2070 
2071         /* move our string counters past this chunk */
2072         str += cur;
2073         len -= cur;
2074 
2075         /* advance the x position */
2076         *x += cur;
2077         if (*x > max_column)
2078         {
2079             /* reset to the first column */
2080             *x = 0;
2081 
2082             /* scroll the screen if necessary */
2083             if (*y == max_line)
2084                 ossscr(text_line, text_column,
2085                        max_line, max_column, osssb_oss_screen_color);
2086             else
2087                 ++*y;
2088         }
2089     }
2090 }
2091 
2092 /*
2093  *   Delete a character in the buffer, updating the display.
2094  */
oss_gets_delchar(unsigned char * buf,unsigned char * p,unsigned char ** eol,int x,int y)2095 static void oss_gets_delchar(unsigned char *buf, unsigned char *p,
2096                              unsigned char **eol, int x, int y)
2097 {
2098     if (p < *eol)
2099     {
2100         /* delete the character and close the gap */
2101         --*eol;
2102         if (p != *eol)
2103             memmove(p, p + 1, *eol - p);
2104 
2105         /*
2106          *   replace the last character with a blank, so that we overwrite
2107          *   its presence on the display
2108          */
2109         **eol = ' ';
2110 
2111         /* re-display the changed part of the string */
2112         ossdsp_str(y, x, text_color, p, *eol - p + 1);
2113 
2114         /* null-terminate the shortened buffer */
2115         **eol = '\0';
2116     }
2117 }
2118 
2119 /*
2120  *   Backspace in the buffer, updating the display and adjusting the cursor
2121  *   position.
2122  */
oss_gets_backsp(unsigned char * buf,unsigned char ** p,unsigned char ** eol,int * x,int * y)2123 static void oss_gets_backsp(unsigned char *buf, unsigned char **p,
2124                             unsigned char **eol, int *x, int *y)
2125 {
2126     /* if we can back up, do so */
2127     if (*p > buf)
2128     {
2129         /* move our insertion point back one position */
2130         --*p;
2131 
2132         /* the line is now one character shorter */
2133         --*eol;
2134 
2135         /* shift all of the characters down one position */
2136         if (*p != *eol)
2137             memmove(*p, *p + 1, *eol - *p);
2138 
2139         /* move the cursor back, wrapping if at the first column */
2140         if (--*x < 0)
2141         {
2142             *x = max_column;
2143             --*y;
2144         }
2145 
2146         /*
2147          *   replace the trailing character with a space, so that we
2148          *   overwrite its screen position with a blank
2149          */
2150         **eol = ' ';
2151 
2152         /*
2153          *   display the string from the current position, so that we update
2154          *   the display for the moved characters
2155          */
2156         ossdsp_str(*y, *x, text_color, *p, *eol - *p + 1);
2157 
2158         /* null-terminate the shortened buffer */
2159         **eol = '\0';
2160     }
2161 }
2162 
2163 /*
2164  *   Move the cursor left by the number of characters.
2165  */
oss_gets_csrleft(int * y,int * x,size_t len)2166 static void oss_gets_csrleft(int *y, int *x, size_t len)
2167 {
2168     for ( ; len != 0 ; --len)
2169     {
2170         /* move left one character, wrapping at the end of the line */
2171         if (--*x < 0)
2172         {
2173             /* move up a line */
2174             --*y;
2175 
2176             /* move to the end of the line */
2177             *x = max_column;
2178         }
2179     }
2180 }
2181 
2182 /*
2183  *   Move the cursor right by the number of characters.
2184  */
oss_gets_csrright(int * y,int * x,size_t len)2185 static void oss_gets_csrright(int *y, int *x, size_t len)
2186 {
2187     for ( ; len != 0 ; --len)
2188     {
2189         /* move right one character, wrapping at the end of the line */
2190         if (++*x > max_column)
2191         {
2192             /* move down a line */
2193             ++*y;
2194 
2195             /* move to the left column */
2196             *x = 0;
2197         }
2198     }
2199 }
2200 
2201 /* ------------------------------------------------------------------------ */
2202 /*
2203  *   cancel interrupted input
2204  */
os_gets_cancel(int reset)2205 void os_gets_cancel(int reset)
2206 {
2207     int x, y;
2208 
2209     /*
2210      *   if we interrupted a previous line, apply display effects as though
2211      *   the user had pressed return
2212      */
2213     if (S_gets_in_progress)
2214     {
2215         /* move to the end of the input line */
2216         x = S_gets_x;
2217         y = S_gets_y;
2218         oss_gets_csrright(&y, &x, strlen(S_gets_buf + S_gets_ofs));
2219 
2220         /* add a newline, scrolling if necessary */
2221         if (y == max_line)
2222             ossscr(text_line, text_column, max_line, max_column,
2223                    osssb_oss_screen_color);
2224 
2225         /* set the cursor to the new position */
2226         ossloc(y, x);
2227 
2228         /* move to the left column for the next display */
2229         text_lastcol = 0;
2230 
2231         /* copy the buffer to the screen save buffer, adding a newline */
2232         ossaddsbe(S_gets_buf, S_gets_buf + strlen(S_gets_buf), x, y);
2233         if (y == max_line)
2234             ossaddsb("\n", 1);
2235 
2236         /* we no longer have an input in progress */
2237         S_gets_in_progress = FALSE;
2238     }
2239 
2240     /* if we're resetting, clear our saved buffer */
2241     if (reset)
2242         S_gets_buf[0] = '\0';
2243 }
2244 
2245 /* ------------------------------------------------------------------------ */
2246 /*
2247  *   Common routine to read a command from the keyboard.  This
2248  *   implementation provides command editing and history, as well as timeout
2249  *   capabilities.
2250  */
os_gets_timeout(unsigned char * buf,size_t bufl,unsigned long timeout,int use_timeout)2251 int os_gets_timeout(unsigned char *buf, size_t bufl,
2252                     unsigned long timeout, int use_timeout)
2253 {
2254     unsigned char *p;
2255     unsigned char *eol;
2256     unsigned char *eob = buf + bufl - 1;
2257     int x;
2258     int y;
2259     int origx;
2260     long end_time;
2261 
2262     /* if we're in 'plain' mode, simply use stdio input */
2263     if (os_f_plain)
2264     {
2265         size_t len;
2266 
2267         /* we don't support the timeout feature in plain mode */
2268         if (use_timeout)
2269             return OS_EVT_NOTIMEOUT;
2270 
2271         /*
2272          *   get input from stdio, and translate the result code - if gets()
2273          *   returns null, it indicates an error of some kind, so return an
2274          *   end-of-file indication
2275          */
2276         if (fgets((char *)buf, bufl, stdin) == 0)
2277             return OS_EVT_EOF;
2278 
2279         /* remove the trailing newline from the buffer, if present */
2280         if ((len = strlen((char *)buf)) != 0 && buf[len-1] == '\n')
2281             buf[len-1] = '\0';
2282 
2283         /* indicate that we read a line */
2284         return OS_EVT_LINE;
2285     }
2286 
2287 # ifdef USE_HISTORY
2288     /* allocate the history buffer if it's not already allocated */
2289     if (histbuf == 0)
2290     {
2291         histbuf = (unsigned char *)osmalloc(HISTBUFSIZE);
2292         histhead = histtail = histbuf;
2293         S_gets_curhist = histhead;
2294     }
2295 # endif /* USE_HISTORY */
2296 
2297     /*
2298      *   If we have a timeout, calculate the system clock time at which the
2299      *   timeout expires.  This is simply the current system clock time plus
2300      *   the timeout interval.  Since we might need to process a series of
2301      *   events, we'll need to know how much time remains at each point we
2302      *   get a new event.
2303      */
2304     end_time = os_get_sys_clock_ms() + timeout;
2305 
2306     /* set up at the last output position, after the prompt */
2307     x = text_lastcol;
2308     y = max_line;
2309     origx = x;
2310 
2311     /*
2312      *   If we have saved input state from a previous interrupted call,
2313      *   restore it now.  Otherwise, initialize everything.
2314      */
2315     if (S_gets_buf[0] != '\0' || S_gets_in_progress)
2316     {
2317         size_t len;
2318 
2319         /* limit the restoration length to the new buffer length */
2320         len = strlen((char *)buf);
2321         if (len > bufl - 1)
2322             len = bufl - 1;
2323 
2324         /* copy the saved buffer to the caller's buffer */
2325         memcpy(buf, S_gets_buf, len);
2326 
2327         /* set up our pointer to the end of the text */
2328         eol = buf + len;
2329 
2330         /* null-terminate the text */
2331         *eol = '\0';
2332 
2333         /*
2334          *   if we cancelled the previous input, we must re-display the
2335          *   buffer under construction, since we have displayed something
2336          *   else in between and have re-displayed the prompt
2337          */
2338         if (!S_gets_in_progress)
2339         {
2340             /* re-display the buffer */
2341             ossdsp_str_scr(&y, &x, text_color, buf, len);
2342 
2343             /*
2344              *   move back to the original insertion point, limiting the
2345              *   move to the available buffer size for the new caller
2346              */
2347             if (S_gets_ofs < (int)len)
2348                 oss_gets_csrleft(&y, &x, len - S_gets_ofs);
2349             else
2350                 S_gets_ofs = len;
2351         }
2352         else
2353         {
2354             /*
2355              *   input is still in progress, so the old text is still on the
2356              *   screen - simply move to the original cursor position
2357              */
2358             x = S_gets_x;
2359             y = S_gets_y;
2360         }
2361 
2362         /* get our pointer into the buffer */
2363         p = buf + S_gets_ofs;
2364     }
2365     else
2366     {
2367         /* set up our buffer pointers */
2368         p = buf;
2369         eol = buf;
2370 
2371         /* initialize our history recall pointer */
2372         S_gets_curhist = histhead;
2373 
2374         /* clear out the buffer */
2375         buf[0] = '\0';
2376     }
2377 
2378     /* process keystrokes until we're done entering the command */
2379     for ( ;; )
2380     {
2381         unsigned char c;
2382         os_event_info_t event_info;
2383         int event_type;
2384 
2385         /* move to the proper position on the screen */
2386         ossloc(y, x);
2387 
2388         /* if we're using a timeout, check for expiration */
2389         if (use_timeout)
2390         {
2391             long cur_clock;
2392 
2393             /* note the current system clock time */
2394             cur_clock = os_get_sys_clock_ms();
2395 
2396             /*
2397              *   if we're past the timeout expiration time already,
2398              *   interrupt with timeout now
2399              */
2400             if (cur_clock >= end_time)
2401                 goto timeout_expired;
2402 
2403             /* note the interval remaining to the timeout expiration */
2404             timeout = end_time - cur_clock;
2405         }
2406 
2407         /* get an event */
2408         event_type = os_get_event(timeout, use_timeout, &event_info);
2409 
2410         /* handle the event according to the event type */
2411         switch(event_type)
2412         {
2413         case OS_EVT_KEY:
2414             /*
2415              *   Keystroke event.  First, translate the key from the "raw"
2416              *   keystroke that os_get_event() returns to the "processed"
2417              *   (CMD_xxx) representation.  This lets the oss layer map
2418              *   special keystrokes to generic editing commands in a
2419              *   system-specific manner.
2420              */
2421             oss_raw_key_to_cmd(&event_info);
2422 
2423             /* get the primary character from the event */
2424             c = event_info.key[0];
2425             break;
2426 
2427         case OS_EVT_TIMEOUT:
2428         timeout_expired:
2429             /*
2430              *   The timeout expired.  Copy the current input state into the
2431              *   save area so that we can resume the input later if desired.
2432              */
2433             strcpy(S_gets_buf, (char *)buf);
2434             S_gets_ofs = p - buf;
2435             S_gets_x = x;
2436             S_gets_y = y;
2437 
2438             /* note that input was interrupted */
2439             S_gets_in_progress = TRUE;
2440 
2441             /* return the timeout status to the caller */
2442             return OS_EVT_TIMEOUT;
2443 
2444         case OS_EVT_NOTIMEOUT:
2445             /*
2446              *   we can't handle events with timeouts, so we can't provide
2447              *   line reading with timeouts, either
2448              */
2449             return OS_EVT_NOTIMEOUT;
2450 
2451         case OS_EVT_EOF:
2452             /* end of file - return the same error to our caller */
2453             return OS_EVT_EOF;
2454 
2455         default:
2456             /* ignore any other events */
2457             continue;
2458         }
2459 
2460         /*
2461          *   Check the character we got.  Note that we must interpret
2462          *   certain control characters explicitly, because os_get_event()
2463          *   returns raw keycodes (untranslated into CMD_xxx codes) for
2464          *   control characters.
2465          */
2466         switch(c)
2467         {
2468         case 8:
2469             /* backspace one character */
2470             oss_gets_backsp(buf, &p, &eol, &x, &y);
2471             break;
2472 
2473         case 13:
2474             /* null-terminate the input */
2475             *eol = '\0';
2476 
2477             /* move to the end of the line */
2478             oss_gets_csrright(&y, &x, eol - p);
2479             p = eol;
2480 
2481             /*
2482              *   Scroll the screen to account for the carriage return,
2483              *   position the cursor at the end of the new line, and
2484              *   null-terminate the line.
2485              */
2486             ossloc(y, x);
2487             if (y == max_line)
2488                 ossscr(text_line, text_column, max_line, max_column,
2489                        osssb_oss_screen_color);
2490 
2491             /* move to the left column for the next display */
2492             text_lastcol = 0;
2493 
2494 # ifdef USE_HISTORY
2495             /*
2496              *   Save the line in our history buffer.  If we don't have
2497              *   enough room, lose some old text by advancing the tail
2498              *   pointer far enough.  Don't save it if it's a blank line,
2499              *   though, or if it duplicates the most recent previous
2500              *   command.
2501              */
2502             if (strlen((char *)buf) != 0)
2503             {
2504                 uchar *q;
2505                 int    advtail;
2506                 int    saveflag = 1;         /* assume we will be saving it */
2507 
2508                 if (q = ossprvcmd(histhead))
2509                 {
2510                     uchar *p = buf;
2511 
2512                     while (*p == *q && *p != '\0' && *q != '\0')
2513                     {
2514                         ++p;
2515                         q = ossadvhp(q);
2516                     }
2517                     if (*p == *q)           /* is this a duplicate command? */
2518                         saveflag = 0;               /* if so, don't save it */
2519                 }
2520 
2521                 if (saveflag)
2522                 {
2523                     for (q = buf, advtail = 0 ; q <= eol ; ++q)
2524                     {
2525                         *histhead = *q;
2526                         histhead = ossadvhp(histhead);
2527                         if (histhead == histtail)
2528                         {
2529                             histtail = ossadvhp(histtail);
2530                             advtail = 1;
2531                         }
2532                     }
2533 
2534                     /*
2535                      *   If we have encroached on space that was already
2536                      *   occupied, throw away the entire command we have
2537                      *   partially trashed; to do so, advance the tail
2538                      *   pointer to the next null byte.
2539                      */
2540                     if (advtail)
2541                     {
2542                         while(*histtail)
2543                             histtail = ossadvhp(histtail);
2544                         histtail = ossadvhp(histtail);
2545                     }
2546                 }
2547             }
2548 # endif /* USE_HISTORY */
2549 
2550             /*
2551              *   Finally, copy the buffer to the screen save buffer (if
2552              *   applicable), and return the contents of the buffer.  Note
2553              *   that we add an extra carriage return if we were already
2554              *   on the max_line, since we scrolled the screen in this
2555              *   case; otherwise, ossaddsbe will add all the blank lines
2556              *   that are necessary.
2557              */
2558             ossaddsbe((char *)buf, (char *)p, x, y);
2559             if (y == max_line)
2560                 ossaddsb("\n", 1);
2561 
2562             /* input is no longer in progress */
2563             S_gets_in_progress = FALSE;
2564             S_gets_buf[0] = '\0';
2565 
2566             /* return success */
2567             return OS_EVT_LINE;
2568 
2569         case 0:
2570             /* extended key code - get the second half of the code */
2571             c = event_info.key[1];
2572 
2573             /* handle the command key code */
2574             switch(c)
2575             {
2576 # ifdef USE_SCROLLBACK
2577             case CMD_SCR:
2578                 {
2579                     char *old_scrbuf_free;
2580 
2581                     /*
2582                      *   Add the contents of the line buffer, plus any blank
2583                      *   lines, to the screen buffer, filling the screen to
2584                      *   the bottom.  Before we do, though, save the current
2585                      *   scrollback buffer free pointer, so we can take our
2586                      *   buffer back out of the scrollback buffer when we're
2587                      *   done - this is just temporary so that the current
2588                      *   command shows up in the buffer while we're in
2589                      *   scrollback mode and is redrawn when we're done.
2590                      */
2591                     old_scrbuf_free = scrbuf_free;
2592                     ossaddsbe((char *)buf, (char *)p, x, y);
2593 
2594                     /* run scrollback mode */
2595                     if (!osssbmode(1))
2596                         ossdosb();
2597 
2598                     /* restore the old free pointer */
2599                     scrbuf_free = old_scrbuf_free;
2600                     *scrbuf_free = '\0';
2601 
2602                     /* go back to our original column */
2603                     sb_column = origx;
2604                     break;
2605                 }
2606 # endif /* USE_SCROLLBACK */
2607 
2608             case CMD_LEFT:
2609                 if (p > buf)
2610                 {
2611                     --p;
2612                     --x;
2613                     if (x < 0)
2614                     {
2615                         x = max_column;
2616                         --y;
2617                     }
2618                 }
2619                 break;
2620 
2621             case CMD_WORD_LEFT:
2622                 if (p > buf)
2623                 {
2624                     --p;
2625                     --x;
2626                     if (x < 0)
2627                         x = max_column, --y;
2628                     while (p > buf && t_isspace(*p))
2629                     {
2630                         --p, --x;
2631                         if (x < 0)
2632                             x = max_column, --y;
2633                     }
2634                     while (p > buf && !t_isspace(*(p-1)))
2635                     {
2636                         --p, --x;
2637                         if (x < 0)
2638                             x = max_column, --y;
2639                     }
2640                 }
2641                 break;
2642 
2643             case CMD_RIGHT:
2644                 if (p < eol)
2645                 {
2646                     ++p;
2647                     ++x;
2648                     if (x > max_column)
2649                     {
2650                         x = 0;
2651                         ++y;
2652                     }
2653                 }
2654                 break;
2655 
2656             case CMD_WORD_RIGHT:
2657                 while (p < eol && !t_isspace(*p))
2658                 {
2659                     ++p, ++x;
2660                     if (x > max_column)
2661                         x = 0, ++y;
2662                 }
2663                 while (p < eol && t_isspace(*p))
2664                 {
2665                     ++p, ++x;
2666                     if (x > max_column)
2667                         x = 0, ++y;
2668                 }
2669                 break;
2670 
2671             case CMD_DEL:
2672                 /* delete a character */
2673                 oss_gets_delchar(buf, p, &eol, x, y);
2674                 break;
2675 
2676 #ifdef UNIX
2677             case CMD_WORDKILL:
2678                 {
2679                     /* remove spaces preceding word */
2680                     while (p >= buf && *p <= ' ')
2681                         oss_gets_backsp(buf, &p, &eol, &x, &y);
2682 
2683                     /* remove previous word (i.e., until we get a space) */
2684                     while (p >= buf && *p > ' ')
2685                         oss_gets_backsp(buf, &p, &eol, &x, &y);
2686 
2687                     /* that's it */
2688                     break;
2689                 }
2690 #endif /* UNIX */
2691 
2692             case CMD_KILL:
2693             case CMD_HOME:
2694 # ifdef USE_HISTORY
2695             case CMD_UP:
2696             case CMD_DOWN:
2697                 if (c == CMD_UP && !ossprvcmd(S_gets_curhist))
2698                     break;                /* no more history - ignore arrow */
2699                 if (c == CMD_DOWN && !ossnxtcmd(S_gets_curhist))
2700                     break;                /* no more history - ignore arrow */
2701                 if (c == CMD_UP && !ossnxtcmd(S_gets_curhist))
2702                 {
2703                     /* first Up arrow - save current buffer */
2704                     strcpy(S_hist_sav, (char *)buf);
2705                 }
2706 # endif /* USE_HISTORY */
2707                 while(p > buf)
2708                 {
2709                     --p;
2710                     if (--x < 0)
2711                     {
2712                         x = max_column;
2713                         --y;
2714                     }
2715                 }
2716                 if (c == CMD_HOME)
2717                     break;
2718 
2719                 /*
2720                  *   We're at the start of the line now; fall through for
2721                  *   KILL, UP, and DOWN to the code which deletes to the
2722                  *   end of the line.
2723                  */
2724             case CMD_DEOL:
2725                 if (p < eol)
2726                 {
2727                     /*
2728                      *   write spaces to the end of the line, to clear out
2729                      *   the screen display of the old characters
2730                      */
2731                     if (p != eol)
2732                     {
2733                         memset(p, ' ', eol - p);
2734                         ossdsp_str(y, x, text_color, p, eol - p);
2735                     }
2736 
2737                     /* truncate the buffer at the insertion point */
2738                     eol = p;
2739                     *p = '\0';
2740                 }
2741 # ifdef USE_HISTORY
2742                 if (c == CMD_UP)
2743                 {
2744                     S_gets_curhist = ossprvcmd(S_gets_curhist);
2745                     osshstcpy(buf, S_gets_curhist);
2746                 }
2747                 else if (c == CMD_DOWN)
2748                 {
2749                     if (!ossnxtcmd(S_gets_curhist))
2750                         break;                                   /* no more */
2751                     S_gets_curhist = ossnxtcmd(S_gets_curhist);
2752                     if (ossnxtcmd(S_gets_curhist))    /* on a valid command */
2753                         osshstcpy(buf, S_gets_curhist);    /* ... so use it */
2754                     else
2755                     {
2756                         /* no more history - restore original line */
2757                         strcpy((char *)buf, S_hist_sav);
2758                     }
2759                 }
2760                 if ((c == CMD_UP || c == CMD_DOWN)
2761                     && strlen((char *)buf) != 0)
2762                 {
2763                     /* get the end pointer based on null termination */
2764                     eol = buf + strlen((char *)buf);
2765 
2766                     /* display the string */
2767                     ossdsp_str_scr(&y, &x, text_color, p, eol - p);
2768 
2769                     /* move to the end of the line */
2770                     p = eol;
2771                 }
2772 # endif /* USE_HISTORY */
2773                 break;
2774             case CMD_END:
2775                 while (p < eol)
2776                 {
2777                     ++p;
2778                     if (++x > max_column)
2779                     {
2780                         x = 0;
2781                         ++y;
2782                     }
2783                 }
2784                 break;
2785 
2786             case CMD_EOF:
2787                 /* on end of file, return null */
2788                 return OS_EVT_EOF;
2789             }
2790             break;
2791 
2792         default:
2793             if (c >= ' ' && eol < eob)
2794             {
2795                 /* open up the line and insert the character */
2796                 if (p != eol)
2797                     memmove(p + 1, p, eol - p);
2798                 ++eol;
2799                 *p = c;
2800                 *eol = '\0';
2801 
2802                 /* write the updated part of the line */
2803                 ossdsp_str_scr(&y, &x, text_color, p, eol - p);
2804 
2805                 /* move the cursor back to the insertion point */
2806                 ++p;
2807                 oss_gets_csrleft(&y, &x, eol - p);
2808             }
2809             break;
2810         }
2811     }
2812 }
2813 
2814 /*
2815  *   Read a line of input.  We implement this in terms of the timeout input
2816  *   line reader, passing an infinite timeout to that routine.
2817  */
os_gets(unsigned char * buf,size_t bufl)2818 uchar *os_gets(unsigned char *buf, size_t bufl)
2819 {
2820     int evt;
2821 
2822     /* cancel any previous input, clearing the buffer */
2823     os_gets_cancel(TRUE);
2824 
2825     /* get a line of input, with no timeout */
2826     evt = os_gets_timeout(buf, bufl, 0, FALSE);
2827 
2828     /* translate the event code to the appropriate return value */
2829     switch(evt)
2830     {
2831     case OS_EVT_LINE:
2832         /* we got a line of input - return a pointer to our buffer */
2833         return buf;
2834 
2835     case OS_EVT_EOF:
2836         /* end of file - return null */
2837         return 0;
2838 
2839     default:
2840         /* we don't expect any other results */
2841         assert(FALSE);
2842         return 0;
2843     }
2844 }
2845 
2846 #else /* USE_STATLINE */
2847 
2848 #endif /* USE_STATLINE */
2849 
2850 /* ------------------------------------------------------------------------ */
2851 /*
2852  *   Highlighting and colors
2853  */
2854 
2855 #ifdef STD_OS_HILITE
2856 
2857 #ifdef RUNTIME
2858 /*
2859  *   Set text attributes
2860  */
os_set_text_attr(int attr)2861 void os_set_text_attr(int attr)
2862 {
2863     char buf[3];
2864 
2865     /* if we're in plain mode, ignore it */
2866     if (os_f_plain)
2867         return;
2868 
2869     /* if the attributes aren't changing, do nothing */
2870     if (osssb_cur_attrs == attr)
2871         return;
2872 
2873     /*
2874      *   add the attribute sequence to the scrollback buffer (encoding with
2875      *   our plus-one code, to avoid storing null bytes)
2876      */
2877     buf[0] = OSGEN_ATTR;
2878     buf[1] = (char)(attr + 1);
2879     buf[2] = '\0';
2880     ossaddsb(buf, 2);
2881 
2882     /* translate the codes to an internal ossdsp color */
2883     text_color = ossgetcolor(osssb_cur_fg, osssb_cur_bg, attr,
2884                              osssb_screen_color);
2885 }
2886 
2887 /*
2888  *   Translate a color from the os_color_t encoding to an OSGEN_xxx color.
2889  */
osgen_xlat_color_t(os_color_t color)2890 static char osgen_xlat_color_t(os_color_t color)
2891 {
2892     size_t i;
2893     struct color_map_t
2894     {
2895         /* the OSGEN_COLOR_xxx value */
2896         char id;
2897 
2898         /* the RGB components for the color */
2899         unsigned char rgb[3];
2900     };
2901     struct color_map_t *p;
2902     struct color_map_t *bestp;
2903     static struct color_map_t color_map[] =
2904     {
2905         { OSGEN_COLOR_BLACK,   { 0x00, 0x00, 0x00 } },
2906         { OSGEN_COLOR_WHITE,   { 0xFF, 0xFF, 0xFF } },
2907         { OSGEN_COLOR_RED,     { 0xFF, 0x00, 0x00 } },
2908         { OSGEN_COLOR_BLUE,    { 0x00, 0x00, 0xFF } },
2909         { OSGEN_COLOR_GREEN,   { 0x00, 0x80, 0x00 } },
2910         { OSGEN_COLOR_YELLOW,  { 0xFF, 0xFF, 0x00 } },
2911         { OSGEN_COLOR_CYAN,    { 0x00, 0xFF, 0xFF } },
2912         { OSGEN_COLOR_SILVER,  { 0xC0, 0xC0, 0xC0 } },
2913         { OSGEN_COLOR_GRAY,    { 0x80, 0x80, 0x80 } },
2914         { OSGEN_COLOR_MAROON,  { 0x80, 0x00, 0x00 } },
2915         { OSGEN_COLOR_PURPLE,  { 0x80, 0x00, 0x80 } },
2916         { OSGEN_COLOR_MAGENTA, { 0xFF, 0x00, 0xFF } },
2917         { OSGEN_COLOR_LIME,    { 0x00, 0xFF, 0x00 } },
2918         { OSGEN_COLOR_OLIVE,   { 0x80, 0x80, 0x00 } },
2919         { OSGEN_COLOR_NAVY,    { 0x00, 0x00, 0x80 } },
2920         { OSGEN_COLOR_TEAL,    { 0x00, 0x80, 0x80 } }
2921     };
2922     unsigned char r, g, b;
2923     unsigned long best_dist;
2924 
2925     /*
2926      *   If it's parameterized, map it by shifting the parameter code (in
2927      *   the high-order 8 bits of the os_color_t) to our single-byte code,
2928      *   which is defined as exactly the same code as the os_color_t values
2929      *   but shifted into the low-order 8 bits.
2930      */
2931     if (os_color_is_param(color))
2932         return (char)((color >> 24) & 0xFF);
2933 
2934     /* break the color into its components */
2935     r = os_color_get_r(color);
2936     g = os_color_get_g(color);
2937     b = os_color_get_b(color);
2938 
2939     /* search for the closest match among our 16 ANSI colors */
2940     for (i = 0, p = color_map, bestp = 0, best_dist = 0xFFFFFFFF ;
2941          i < sizeof(color_map)/sizeof(color_map[0]) ; ++i, ++p)
2942     {
2943         unsigned long dist;
2944         int rd, gd, bd;
2945 
2946         /* calculate the delta for each component */
2947         rd = r - p->rgb[0];
2948         gd = g - p->rgb[1];
2949         bd = b - p->rgb[2];
2950 
2951         /* calculate the "distance" in RGB space */
2952         dist = rd*rd + gd*gd + bd*bd;
2953 
2954         /* if it's an exact match, we need look no further */
2955         if (dist == 0)
2956             return p->id;
2957 
2958         /* if it's the smallest distance so far, note it */
2959         if (dist < best_dist)
2960         {
2961             best_dist = dist;
2962             bestp = p;
2963         }
2964     }
2965 
2966     /* return the OSGEN_COLOR_xxx ID of the best match we found */
2967     return bestp->id;
2968 }
2969 
2970 /*
2971  *   Set the text colors.
2972  *
2973  *   The foreground and background colors apply to subsequent characters
2974  *   displayed via os_print() (etc).  If the background color is set to zero,
2975  *   it indicates "transparent" drawing: subsequent text is displayed with
2976  *   the "screen" color.
2977  */
os_set_text_color(os_color_t fg,os_color_t bg)2978 void os_set_text_color(os_color_t fg, os_color_t bg)
2979 {
2980     char buf[4];
2981 
2982     /* if we're in plain mode, ignore it */
2983     if (os_f_plain)
2984         return;
2985 
2986     /* add the color sequence to the scrollback buffer */
2987     buf[0] = OSGEN_COLOR;
2988     buf[1] = osgen_xlat_color_t(fg);
2989     buf[2] = osgen_xlat_color_t(bg);
2990     buf[3] = '\0';
2991     ossaddsb(buf, 3);
2992 
2993     /* translate the codes to an internal ossdsp color */
2994     text_color = ossgetcolor(fg, bg, osssb_cur_attrs, osssb_screen_color);
2995 }
2996 
2997 /*
2998  *   Set the screen color
2999  */
os_set_screen_color(os_color_t color)3000 void os_set_screen_color(os_color_t color)
3001 {
3002     /* if we're in plain mode, ignore it */
3003     if (os_f_plain)
3004         return;
3005 
3006     /* set the new background color */
3007     osssb_screen_color = color;
3008     osssb_oss_screen_color = ossgetcolor(OSGEN_COLOR_TEXT,
3009                                          osgen_xlat_color_t(color), 0, 0);
3010 
3011     /*
3012      *   recalculate the current text color, since it will be affected by the
3013      *   background change if its background is transparent
3014      */
3015     text_color = ossgetcolor(osssb_cur_fg, osssb_cur_bg, osssb_cur_attrs,
3016                              osssb_screen_color);
3017 
3018     /* redraw the screen in the new background color */
3019     os_redraw();
3020 }
3021 
3022 /* redraw the screen if necessary */
osssb_redraw_if_needed()3023 void osssb_redraw_if_needed()
3024 {
3025     /* this implementation never defers redrawing - do nothing */
3026 }
3027 
3028 /* set the default cursor position */
osssb_cursor_to_default_pos()3029 void osssb_cursor_to_default_pos()
3030 {
3031     /* we don't need to do anything special here */
3032 }
3033 
3034 #else /* RUNTIME */
3035 
os_set_text_attr(int attr)3036 void os_set_text_attr(int attr)
3037 {
3038     /* attributes are not supported in non-RUNTIME mode */
3039 }
3040 
os_set_text_color(os_color_t fg,os_color_t bg)3041 void os_set_text_color(os_color_t fg, os_color_t bg)
3042 {
3043     /* colors aren't supported in non-RUNTIME mode - ignore it */
3044 }
3045 
os_set_screen_color(os_color_t color)3046 void os_set_screen_color(os_color_t color)
3047 {
3048     /* colors aren't supported in non-RUNTIME mode - ignore it */
3049 }
3050 
3051 #endif /* RUNTIME */
3052 
3053 #endif /* STD_OS_HILITE */
3054 
3055 /* ------------------------------------------------------------------------ */
3056 /*
3057  *   clear the screen, deleting all scrollback information
3058  */
3059 #ifdef STD_OSCLS
3060 
oscls(void)3061 void oscls(void)
3062 {
3063 #ifdef RUNTIME
3064     /* do nothing in 'plain' mode */
3065     if (os_f_plain)
3066         return;
3067 
3068     /* forget the scrollback buffer's contents */
3069     scrbuf_free = scrbuf;
3070     scrbuf_head = scrbuf_tail = scrbuf;
3071     scrtop = scrbot = scrlast = 0;
3072     os_line_count = 1;
3073     memset(scrbuf, 0, (size_t)scrbufl);
3074     os_redraw();
3075 #endif
3076 }
3077 
3078 #endif /* STD_OSCLS */
3079 
3080 /* ------------------------------------------------------------------------ */
3081 /*
3082  *   Simple implementation of os_get_sysinfo.  This can be used for any
3083  *   non-HTML version of the system, since all sysinfo codes currently
3084  *   pertain to HTML features.  Note that new sysinfo codes may be added
3085  *   in the future which may be relevant to non-html versions, so the
3086  *   sysinfo codes should be checked from time to time to ensure that new
3087  *   codes relevant to this system version are handled correctly here.
3088  */
os_get_sysinfo(int code,void * param,long * result)3089 int os_get_sysinfo(int code, void *param, long *result)
3090 {
3091 #ifdef RUNTIME
3092     /* if the oss layer recognizes the code, defer to its judgment */
3093     if (oss_get_sysinfo(code, param, result))
3094         return TRUE;
3095 #endif
3096 
3097     /* check the type of information they're requesting */
3098     switch(code)
3099     {
3100     case SYSINFO_INTERP_CLASS:
3101         /* we're a character-mode text-only interpreter */
3102         *result = SYSINFO_ICLASS_TEXT;
3103         return TRUE;
3104 
3105     case SYSINFO_HTML:
3106     case SYSINFO_JPEG:
3107     case SYSINFO_PNG:
3108     case SYSINFO_WAV:
3109     case SYSINFO_MIDI:
3110     case SYSINFO_WAV_MIDI_OVL:
3111     case SYSINFO_WAV_OVL:
3112     case SYSINFO_MPEG:
3113     case SYSINFO_MPEG1:
3114     case SYSINFO_MPEG2:
3115     case SYSINFO_MPEG3:
3116     case SYSINFO_PREF_IMAGES:
3117     case SYSINFO_PREF_SOUNDS:
3118     case SYSINFO_PREF_MUSIC:
3119     case SYSINFO_PREF_LINKS:
3120     case SYSINFO_LINKS_HTTP:
3121     case SYSINFO_LINKS_FTP:
3122     case SYSINFO_LINKS_NEWS:
3123     case SYSINFO_LINKS_MAILTO:
3124     case SYSINFO_LINKS_TELNET:
3125     case SYSINFO_PNG_TRANS:
3126     case SYSINFO_PNG_ALPHA:
3127     case SYSINFO_OGG:
3128     case SYSINFO_MNG:
3129     case SYSINFO_MNG_TRANS:
3130     case SYSINFO_MNG_ALPHA:
3131     case SYSINFO_BANNERS:
3132         /*
3133          *   we don't support any of these features - set the result to 0
3134          *   to indicate this
3135          */
3136         *result = 0;
3137 
3138         /* return true to indicate that we recognized the code */
3139         return TRUE;
3140 
3141     default:
3142         /* not recognized */
3143         return FALSE;
3144     }
3145 }
3146 
3147 /* ------------------------------------------------------------------------ */
3148 /*
3149  *   Set the saved-game extension.  Most platforms don't need to do
3150  *   anything with this information, and in fact most platforms won't even
3151  *   have a way of letting the game author set the saved game extension,
3152  *   so this trivial implementation is suitable for most systems.
3153  *
3154  *   The purpose of setting a saved game extension is to support platforms
3155  *   (such as Windows) where the filename suffix is used to associate
3156  *   document files with applications.  Each stand-alone executable
3157  *   generated on such platforms must have a unique saved game extension,
3158  *   so that the system can associate each game's saved position files
3159  *   with that game's executable.
3160  */
os_set_save_ext(const char * ext)3161 void os_set_save_ext(const char *ext)
3162 {
3163     /* ignore the setting */
3164 }
3165 
3166 
3167 /* ------------------------------------------------------------------------ */
3168 /*
3169  *   Set the game title.  Most platforms have no use for this information,
3170  *   so they'll just ignore it.  This trivial implementation simply
3171  *   ignores the title.
3172  */
3173 #ifdef USE_NULL_SET_TITLE
3174 
os_set_title(const char * title)3175 void os_set_title(const char *title)
3176 {
3177     /* ignore the information */
3178 }
3179 
3180 #endif /* USE_NULL_SET_TITLE */
3181 
3182 /* ------------------------------------------------------------------------ */
3183 /*
3184  *   The "banner" window functions are not supported in this implementation.
3185  */
os_banner_create(void * parent,int where,void * other,int wintype,int align,int siz,int siz_units,unsigned long style)3186 void *os_banner_create(void *parent, int where, void *other, int wintype,
3187                        int align, int siz, int siz_units, unsigned long style)
3188 {
3189     return 0;
3190 }
3191 
os_banner_delete(void * banner_handle)3192 void os_banner_delete(void *banner_handle)
3193 {
3194 }
3195 
os_banner_orphan(void * banner_handle)3196 void os_banner_orphan(void *banner_handle)
3197 {
3198 }
3199 
os_banner_disp(void * banner_handle,const char * txt,size_t len)3200 void os_banner_disp(void *banner_handle, const char *txt, size_t len)
3201 {
3202 }
3203 
os_banner_set_attr(void * banner_handle,int attr)3204 void os_banner_set_attr(void *banner_handle, int attr)
3205 {
3206 }
3207 
os_banner_set_color(void * banner_handle,os_color_t fg,os_color_t bg)3208 void os_banner_set_color(void *banner_handle, os_color_t fg, os_color_t bg)
3209 {
3210 }
3211 
os_banner_set_screen_color(void * banner_handle,os_color_t color)3212 void os_banner_set_screen_color(void *banner_handle, os_color_t color)
3213 {
3214 }
3215 
os_banner_flush(void * banner_handle)3216 void os_banner_flush(void *banner_handle)
3217 {
3218 }
3219 
os_banner_set_size(void * banner_handle,int siz,int siz_units,int is_advisory)3220 void os_banner_set_size(void *banner_handle, int siz, int siz_units,
3221                         int is_advisory)
3222 {
3223 }
3224 
os_banner_size_to_contents(void * banner_handle)3225 void os_banner_size_to_contents(void *banner_handle)
3226 {
3227 }
3228 
os_banner_start_html(void * banner_handle)3229 void os_banner_start_html(void *banner_handle)
3230 {
3231 }
3232 
os_banner_end_html(void * banner_handle)3233 void os_banner_end_html(void *banner_handle)
3234 {
3235 }
3236 
os_banner_goto(void * banner_handle,int row,int col)3237 void os_banner_goto(void *banner_handle, int row, int col)
3238 {
3239 }
3240