1 /*  Copyright (C) 1987, 1988 by Michael J. Roberts.  All Rights Reserved. */
2 
3 /*
4  * OS-dependent module for Unix and Termcap/Termio library.
5  *
6  * 29-Jan-93
7  * Dave Baggett
8  */
9 
10 /*
11  * Fri Jan 29 16:14:39 EST 1993
12  * dmb@ai.mit.edu   Termcap version works.
13  *
14  * Thu Apr 22 01:58:07 EDT 1993
15  * dmb@ai.mit.edu   Lots of little changes.  Incorporated NeXT, Linux,
16  *          and DJGCC changes.
17  *
18  * Sat Jul 31 02:04:31 EDT 1993
19  * dmb@ai.mit.edu   Many changes to improve screen update efficiency.
20  *          Added unix_tc flag, set by compiler mainline,
21  *          to disable termcap stuff and just run using stdio.
22  *
23  * Fri Oct 15 15:50:09 EDT 1993
24  * dmb@ai.mit.edu   Changed punt() code to save game state.
25  *
26  * Mon Oct 25 16:39:57 EDT 1993
27  * dmb@ai.mit.edu   Now use our own os_defext and os_remext.
28  *
29  */
30 #include <stdio.h>
31 #if !defined(SUN3)
32 #include <stddef.h>
33 #endif
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <unistd.h>          /* SRG added for usleep() and select() */
39 #include <sys/ioctl.h>       /* tril@igs.net added for TIOCGWINSZ */
40 #if defined(LINUX_386) || defined(FREEBSD_386)   /* tril@igs.net 2000/Aug/20 */
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #endif
44 
45 #ifdef  DJGCC_386
46 #include <pc.h>
47 #include <dos.h>
48 #include "biosvid.h"
49 #endif
50 
51 #include "os.h"
52 #include "osgen.h"
53 #ifndef USE_STDIO
54 #include "osdbg.h"
55 #endif
56 #include "voc.h"
57 
58 /*
59  * ^C pressed?
60  */
61 static int  break_set = 0;
62 
63 /*
64  * Run-time voc context for save-on-punt
65  */
66 extern voccxdef *main_voc_ctx;
67 
68 /*
69  * Running as compiler?  If so, we shouldn't do any term handling.
70  */
71 int unix_tc = 0;
72 
73 /*
74  * Private terminal handling stuff
75  */
76 int unix_max_line;
77 int unix_max_column;
78 
79 /*
80  *   Stuff for the timing functions. Added by SRG
81  */
82 static long timeZero = 0;
83 
84 /*
85  * "Colors"
86  * Note that cNORMAL *must* be zero for other parts of TADS to work.
87  */
88 #define cNORMAL     (1 << 0)
89 #define cSTANDOUT   (1 << 1)
90 #define cREVERSE    (1 << 2)
91 #define cBOLD       (1 << 3)
92 #define cBLINK      (1 << 4)
93 #define cUNDERSCORE (1 << 5)
94 
95 #ifndef USE_STDIO
96 
97 #ifndef DJGCC_386
98 #include <signal.h>
99 #ifdef  USE_SGTTY
100 #include <sgtty.h>
101 #else
102 #ifdef  TERMIOS_IS_NOT_IN_SYS
103 #include <termios.h>
104 #else
105 #include <sys/termios.h>
106 #endif  /* TERMIOS_IS_NOT_IN_SYS */
107 #endif  /* USE_SGTTY */
108 #endif  /* DJGCC_386 */
109 
110 #ifdef  HAVE_TPARM
111 /*
112  * Use the standard library routine if we have it.
113  */
114 #define Tparm tparm
115 #else
116 char *Tparm();
117 #endif
118 
119 /*
120  * The following flag determines whether or not the output routines
121  * will attempt to use hardware features of the controlling terminal.
122  * If it is NOT on, ALL screen updates will go through t_redraw.
123  * This is useful for debugging problems with a termcap entry or
124  * getting TADS running on a new environment quickly, but IS INCREDIBLY
125  * SLOW and hence should not be used for production releases.
126  */
127 #define T_OPTIMIZE
128 
129 /*
130  * The following flag determines whether or not ossdsp will just send
131  * its string out to t_puts or whether it will redraw the entire affected
132  * line.  The latter is slower (perhaps painfully so over slow modem
133  * connections), but fixes a problem with the way TADS currently updates
134  * the status line.
135  *
136  * Sat Jul 31 02:06:42 EDT 1993  dmb@ai.mit.edu
137  * This problem seems to be fixed now, but I'll leave the flag anyway.
138  *
139  */
140 #define FAST_OSSDSP
141 
142 
143 #define INIT 0
144 #define RESTORE 1
145 
146 /*
147  * We store an image of the screen in a private array.
148  * The maximum dimensions of this array are given here.
149  */
150 #define MAXCOLS (OS_MAXWIDTH-1)
151 #define MAXLINES 256
152 
153 /*
154  * Running as debugger?  If so, split the screen into the debugging
155  * window and the execution window.
156  */
157 int unix_tdb = 0;
158 int breakpoint_color;   /* for debugger */
159 int watchpoint_color;   /* for debugger */
160 int filename_color;     /* for debugger */
161 int usebios = 0;  /* for dbgu.c */
162 
163 /*
164  * Our private notion of what the screen looks like.
165  * We keep one array of characters and another array of "colors."
166  * Under termcap/termio handling we don't actually do color (at the
167  * moment, at least), but can do character attributes like standout,
168  * reverse video, blinking, etc.
169  *
170  * We keep track of the current cursor location and text attributes
171  * so that we don't have to send redundant codes out to the terminal.
172  */
173 static int  cw = 0;     /* current window (for debugger) */
174 static int  COLS, LINES;
175 static char COLOR = -1;
176 static int  cursorX = 0, cursorY = 0;
177 static int  inputX = 0, inputY = 0;
178 static char screen[MAXLINES][MAXCOLS];
179 static char colors[MAXLINES][MAXCOLS];
180 
181 #ifdef  DJGCC_386
182 static int  plaincolor, revcolor, boldcolor;
183 #endif
184 
185 /*
186  * Flags indicating whether our controlling terminal has various
187  * capabilities.
188  */
189 static int  standout_ok, rev_ok, bold_ok, blink_ok, underscore_ok;
190 static int  hideshow_ok;
191 
192 static int  k_erase, k_kill, k_word_erase, k_reprint;
193 
194 /*
195  * Terminal capabilities from termcap.
196  * Certainly more than you ever wanted to know about.
197  */
198 
199 /*
200  * Booleans
201  */
202 static int
203     Txs,    /* Standout not erased by overwriting (Hewlett-Packard) */
204     Tms,    /* Safe to move in standout modes */
205     Tos;    /* Terminal overstrikes on hard-copy terminal */
206 
207 /*
208  * Numbers
209  */
210 static int
211     Tsg;    /* Number for characters left by standout */
212 /*
213  * Strings
214  */
215 static char
216     *Tcs,   /* Change scroll region to lines #1 through #2 (VT100) */
217     *TcS,   /* Change scroll region: #1 = total lines, #2 =  lines
218            above region, #3 = lines below region, #4 = total lines */
219     *Tcl,   /* Clear screen and home cursor */
220     *Tce,   /* Clear to end of line */
221     *Tcd,   /* Clear to end of display */
222     *Tcm,   /* Cursor motion to row #1 col #2 */
223     *Tdo,   /* Down one line */
224     *Tho,   /* Home cursor */
225     *Tvi,   /* Make cursor invisible */
226     *Tle,   /* Move cursor left one SPACE */
227     *Tve,   /* Make cursor appear normal (undo cvvis/civis) */
228     *Tnd,   /* Non-destructive space (cursor right) */
229     *Tup,   /* Upline (cursor up) */
230     *Tvs,   /* Make cursor very visible */
231     *Tdl,   /* Delete line */
232     *Tmb,   /* Turn on blinking */
233     *Tmd,   /* Turn on bold (extra bright) mode */
234     *Tme,   /* Turn off attributes (blinking, reverse, bold, etc.) */
235     *Tti,   /* String to begin programs that use cursor addresing */
236     *Tmr,   /* Turn on reverse video mode */
237     *Tso,   /* Begin standout mode */
238     *Tus,   /* Start underscore mode */
239     *Tte,   /* String to end programs that use cursor addressing */
240     *Tse,   /* End standout mode */
241     *Tue,   /* End underscore mode */
242     *Tal,   /* Add new blank line (insert line) */
243     *Tpc,   /* Pad character (rather than null) */
244     *TDL,   /* Delete #1 lines */
245     *TDO,   /* Move cursor down #1 lines. */
246     *TSF,   /* Scroll forward #1 lines. */
247     *TAL,   /* Add #1 new blank lines */
248     *TLE,   /* Move cursor left #1 spaces */
249     *TRI,   /* Move cursor right #1 spaces. */
250     *TSR,   /* Scroll backward #1 lines. */
251     *TUP,   /* Move cursor up #1 lines. */
252     *Tsf,   /* Scroll forward one line. */
253     *Tsr,   /* Scroll backward one line. */
254     *Twi;   /* Current window is lines #1-#2 cols #3-#4 */
255 
256 #ifndef DJGCC_386
257 /*
258  * We must import the following three variables from the termcap
259  * library and set the appropriately so that output padding will
260  * work properly.
261  */
262 extern char PC;
263 extern short ospeed;
264 #endif  /* DJGCC_386 */
265 
266 static void punt(int sig);
267 static void susp(int sig);
268 static void cont(int sig);
269 static void get_screen_size(int *w, int *h);
270 static void resize(void);
271 static void t_init(void);
272 static void t_tty(int action);
273 static void winch(int sig);
274 static void set_win_size(void);
275 static void t_color_init(void);
276 static void t_term(void);
277 static void t_hide(void);
278 static void t_show(void);
279 static void t_putc(char c);
280 static void t_outs(char *s);
281 static void t_puts(char *s);
282 static void t_refresh(void);
283 static void t_update(int x, int y);
284 static void t_loc(int y, int x, int force);
285 static void t_color(char c);
286 static void t_redraw(int top, int bottom);
287 static void t_scroll(int top, int bottom, int lines);
288 static void t_set_scroll_region(int top, int bot);
289 static void break_handler(int sig);
290 
291 void ossclr(int top, int left, int bottom, int right, int blank_color);
292 void ossdbgloc(int y, int x);
293 
294 /*
295  * Set up our controlling terminal and find out what it's capable
296  * of doing.  Punt if we have no ability to position the cursor,
297  * scroll, etc.
298  */
299 static void
t_init(void)300 t_init(void)
301 {
302 #ifdef  DJGCC_386
303 
304     bios_video_init();
305     rev_ok = 1;
306     bold_ok = 1;
307     standout_ok = 0;
308     blink_ok = 0;
309     underscore_ok = 0;
310     hideshow_ok = 0;
311     bios_video_get_screen_dimensions(&COLS, &LINES);
312     LINES++; COLS++; set_win_size(); LINES--; COLS--;
313     t_color_init();
314 
315 #else   /* DJGCC_386*/
316 
317 #define Tgetstr(key) (tgetstr(key, &tbufptr))
318     extern char *tgetstr();
319 
320     static char tbuf[4096];
321     register char   *term;
322     register char   *tptr;
323     char        *tbufptr;
324     int     scroll_ok, curs_ok;
325 
326     if (unix_tc)
327         return;
328 
329     /*
330      * Set tty up the way we need it.
331      */
332     t_tty(INIT);
333 
334     /*
335      * Set all string capabilities to NULL for safety's sake.
336      */
337     Tcs = TcS = Tcl = Tce = Tcd = Tcm = Tdo = Tho = Tvi = Tle = Tve =
338     Tnd = Tup = Tvs = Tdl = Tme = Tmb = Tmd = Tti = Tmr = Tso = Tus =
339     Tte = Tse = Tue = Tal = Tpc = TDL = TDO = TSF = TAL = TLE = TRI =
340     TSR = TUP = Twi = NULL;
341 
342     /*
343      * Find the name of the user's terminal.
344      */
345     term = getenv("TERM");
346     if(!term) {
347         printf("Can't find TERM, the name of your terminal.\n");
348         exit(-1);
349     }
350 
351     /*
352      * Allocate a buffer for the termcap entry.  Make it big to be safe.
353      */
354     tptr = (char *) malloc(4096);
355     if (!tptr) {
356         printf("Out of memory allocating termcap entry buffer.\n");
357         exit(-1);
358     }
359 
360     /*
361      * Get the termcap entry.
362      */
363     tbufptr = tbuf;
364     if(tgetent(tptr, term) < 1) {
365         printf("Unknown terminal type: %s", term);
366         exit(-1);
367     }
368 
369     /*
370      * Check for fatal conditions:
371      *
372      *  - Tos (terminal overstrikes)
373      *  - Does not have any of:
374      *      - Add line and delete line (AL/DL, al/dl)
375      *      - Set scrolling region (cs, cS) *and* scroll up and
376      *        down (sf and sr).
377      *      - Set window (wi) *and* scroll up and scroll down
378      *        (sf and sr).
379      *  - Does not have cursor addressing *or* home *and* down and right
380      */
381     if (tgetflag("os")) {
382         printf("Can't run on a hard-copy terminal (termcap os).\n");
383         exit(-1);
384     }
385     TAL = Tgetstr("AL");
386     TDL = Tgetstr("DL");
387     Tal = Tgetstr("al");
388     Tdl = Tgetstr("dl");
389     Tcs = Tgetstr("cs");
390     TcS = Tgetstr("cS");
391     Twi = Tgetstr("wi");
392     Tsf = Tgetstr("sf");
393     Tsr = Tgetstr("sr");
394     TSF = Tgetstr("SF");
395     TSR = Tgetstr("SR");
396 
397     if (!Tsf)
398         Tsf = "\n";
399 
400     scroll_ok = 0;
401     if ((Tal || TAL) && (Tdl || TDL))
402         scroll_ok = 1;
403     else if ((Tcs || TcS || Twi) && (Tsf || TSF) && (Tsr || TSR))
404         scroll_ok = 1;
405     if (!scroll_ok) {
406         printf("Can't run without window scrolling capability.\n");
407         exit(-1);
408     }
409     Tcm = Tgetstr("cm");
410     Tho = Tgetstr("ho");
411     Tle = Tgetstr("le");
412     if (!Tle)
413         Tle = Tgetstr("bs");    /* obsolete alternative to "le" */
414     TLE = Tgetstr("LE");
415     Tnd = Tgetstr("nd");
416     TRI = Tgetstr("RI");
417     Tup = Tgetstr("up");
418     TUP = Tgetstr("UP");
419     Tdo = Tgetstr("do");
420     if (!Tdo)
421         Tdo = Tgetstr("nl");    /* obsolete alternative to "do" */
422     TDO = Tgetstr("DO");
423     Tti = Tgetstr("ti");
424     Tte = Tgetstr("te");
425     curs_ok = 0;
426     if (Tcm)
427         curs_ok = 1;
428     else if (Tho) {
429         if ((Tnd || TRI) && (Tdo || TDO))
430             curs_ok = 1;
431     }
432     if (!curs_ok) {
433         printf("Can't run without cursor positioning capability.\n");
434         exit(-1);
435     }
436 
437     /*
438      * Get region clearing capabilities, if any.
439      */
440     Tcl = Tgetstr("cl");
441     Tce = Tgetstr("ce");
442     Tcd = Tgetstr("cd");
443 
444     /*
445      * See if we have standout mode.
446      * Don't use it if term has standout erase bug (xs) or it's not
447      * safe to move in standout mode (ms), or standout generates spaces
448      * (sg > 0).
449      */
450     standout_ok = 0;
451     if (!tgetflag("xs") && !tgetflag("ms") && tgetnum("sg") <= 0) {
452         Tso = Tgetstr("so");
453         Tse = Tgetstr("se");
454         if (Tso && Tse)
455             standout_ok = 1;
456     }
457 
458     /*
459      * See if we can turn off special modes
460      */
461     Tme = Tgetstr("me");
462 
463     /*
464      * See if we have reverse video mode.
465      */
466     rev_ok = 0;
467     if (Tme) {
468         Tmr = Tgetstr("mr");
469         if (Tmr)
470             rev_ok = 1;;
471     }
472 
473     /*
474      * See if we have bold mode.
475      */
476     bold_ok = 0;
477     if (Tme) {
478         Tmd = Tgetstr("md");
479         if (Tmd)
480             bold_ok = 1;
481     }
482 
483     /*
484      * See if we have blink mode.
485      */
486     blink_ok = 0;
487     if (Tme) {
488         Tmb = Tgetstr("mb");
489         if (Tmb)
490             blink_ok = 1;
491     }
492 
493     /*
494      * See if we have underscore mode.
495      */
496     underscore_ok = 0;
497     Tus = Tgetstr("us");
498     Tue = Tgetstr("ue");
499     if (Tus && Tue)
500         underscore_ok = 1;
501 
502     /*
503      * See if we can hide/show the cursor.
504      */
505     hideshow_ok = 0;
506     Tvi = Tgetstr("vi");
507     Tve = Tgetstr("ve");
508     Tvs = Tgetstr("vs");
509     if (Tvi && Tve)
510         hideshow_ok = 1;
511 
512     /*
513      * Get padding character (if there is one for this term).
514      */
515     Tpc = Tgetstr("pc");
516     if (Tpc)
517         PC = *Tpc;
518     else
519         PC = 0;
520 
521     free(tptr);
522 
523     /*
524      * Do the right thing when window's resized.
525      */
526     signal(SIGWINCH, winch);
527 
528     /*
529      * Handle ^C.
530      */
531     signal(SIGINT, break_handler);
532     /* signal(SIGINT, SIG_IGN); */
533 
534     /*
535      * Handle ^Z.
536      */
537     signal(SIGTSTP, susp);
538     signal(SIGCONT, cont);
539 
540     /*
541      * Restore terminal sanity when signals nuke us.
542      */
543     signal(SIGHUP, punt);
544     signal(SIGQUIT, punt);
545     signal(SIGILL, punt);
546     signal(SIGTRAP, punt);
547     signal(SIGABRT, punt);
548 #if !defined(LINUX_386)
549     signal(SIGEMT, punt);
550 #endif
551     signal(SIGFPE, punt);
552 #if !defined(LINUX_386)
553     signal(SIGBUS, punt);
554 #endif
555     signal(SIGSEGV, punt);
556 #if !defined(LINUX_386)
557     signal(SIGSYS, punt);
558 #endif
559 
560     /*
561      * Determine screen size.  Ensure sanity of text formatter.
562      */
563     resize();
564 
565     /*
566      * Init "colors"
567      */
568     t_color_init();
569 
570     /*
571      * Init screen
572      */
573     t_puts(Tti);
574     t_puts(Tvs);
575 
576 #endif  /* DJGCC_386 */
577 }
578 
579 /*
580  * Clean up when we get a request to suspend from the terminal.
581  */
582 static void
susp(int sig)583 susp(int sig)
584 {
585 #ifndef DJGCC_386
586     t_term();
587     signal(SIGTSTP, SIG_DFL);
588     kill(getpid(), SIGTSTP);
589 #endif
590 }
591 
592 /*
593  * Restore term state upon resuming from suspension.
594  */
595 static void
cont(int sig)596 cont(int sig)
597 {
598 #ifndef DJGCC_386
599     t_init();
600     t_update(-1, -1);
601     COLOR = -1;
602     t_refresh();
603 #endif
604 }
605 
606 /*
607  * Die gracefully when we get a fatal signal.
608  * We try to save the game before we exit.
609  */
610 static void
punt(int sig)611 punt(int sig)
612 {
613     char s[15];
614 
615     if (!unix_tc && main_voc_ctx != 0) {
616         sprintf(s, "fatal%d.sav", getpid());
617         printf("Caught a fatal signal!  Attempting to save game in %s...\n", s);
618         fiosav(main_voc_ctx, s, 0);
619     }
620     else {
621         printf("Caught a fatal signal!\n");
622     }
623 
624     os_term(-1);
625 }
626 
627 /*
628  * Get terminal size.
629  * Negative and zero values mean the system has no clue.
630  */
631 static void
get_screen_size(int * w,int * h)632 get_screen_size(int *w, int *h)
633 {
634 #ifndef DJGCC_386
635     /*
636      * Define the 4.3 names in terms of the Sun names
637      * if the latter exist and the former do not.
638      */
639 #ifdef TIOCGSIZE
640 #ifndef TIOCGWINSZ
641 #define TIOCGWINSZ TIOCGSIZE
642 #define winsize ttysize
643 #define ws_row ts_lines
644 #define ws_col ts_cols
645 #endif
646 #endif
647 
648     /*
649      * Use the 4.3 names if possible.
650      */
651 #ifdef TIOCGWINSZ
652     struct winsize size;
653     *w = 0;
654     *h = 0;
655     if (ioctl(0, TIOCGWINSZ, &size) < 0)
656         return;
657 
658     *w = size.ws_col;
659     *h = size.ws_row;
660 
661 #else /* not TIOCGWNSIZ */
662 
663     /*
664      * System has no clue
665      */
666     *w = 0;
667     *h = 0;
668 
669 #endif /* not TIOCGWINSZ */
670 
671     /*
672      * If running as debugger, halve the height
673      * since we want two windows.
674      */
675     if (unix_tdb)
676         *h /= 2;
677 
678 #endif  /* DJGCC_386 */
679 }
680 
681 static void
resize(void)682 resize(void)
683 {
684 #ifndef DJGCC_386
685     get_screen_size(&COLS, &LINES);
686 
687     if (COLS <= 0 || LINES <= 0) {
688         COLS = tgetnum("co");
689         LINES = tgetnum("li");
690     }
691 
692     set_win_size();
693 #endif
694 }
695 
696 static void
winch(int sig)697 winch(int sig)
698 {
699 #ifndef DJGCC_386
700     resize();
701 
702     /*
703      * Clear screen
704      */
705     ossclr(0, 0, LINES - 1, COLS - 1, cNORMAL);
706     t_refresh();
707 #endif
708 }
709 
710 /*
711  * When window size changes, we have to notify the rest of the system
712  * so text will be formatted properly.
713  */
714 static void
set_win_size(void)715 set_win_size(void)
716 {
717     /*
718      * Ensure output formatter happiness.
719      */
720     if (LINES < 5)
721         LINES = 5;
722     if (COLS < 20)
723         COLS = 20;
724 
725     /*
726      * Ensure our own personal happiness.
727      */
728     if (LINES >= MAXLINES)
729         LINES = MAXLINES - 1;
730     if (COLS >= MAXCOLS)
731         COLS = MAXCOLS - 1;
732 
733     G_oss_screen_height = LINES - 1;
734     G_oss_screen_width = COLS;
735     unix_max_line = LINES - 2;
736     unix_max_column = COLS - 2;
737     osssb_on_resize_screen();
738 }
739 
740 /*
741  * Tell the rest of the system what "colors" to use for various things.
742  */
743 static void
t_color_init(void)744 t_color_init(void)
745 {
746     if (rev_ok)
747         sdesc_color = cREVERSE;
748     else if (standout_ok)
749         sdesc_color = cSTANDOUT;
750     else if (bold_ok)
751         sdesc_color = cBOLD;
752     else if (underscore_ok)
753         sdesc_color = cUNDERSCORE;
754     else
755         sdesc_color = cNORMAL;
756 
757     ldesc_color = sdesc_color;
758     debug_color = sdesc_color;
759 
760     text_color  = cNORMAL;
761     if (bold_ok)
762         text_bold_color = cBOLD;
763     else if (standout_ok)
764         text_bold_color = cSTANDOUT;
765     else if (rev_ok)
766         text_bold_color = cREVERSE;
767     else if (underscore_ok)
768         text_bold_color = cUNDERSCORE;
769     else
770         text_bold_color = cNORMAL;
771 
772     text_normal_color = text_color;
773     breakpoint_color = sdesc_color;
774     watchpoint_color = sdesc_color;
775 
776     if (rev_ok)
777         filename_color = cREVERSE;
778     else if (underscore_ok)
779         filename_color = cUNDERSCORE;
780     else if (bold_ok)
781         filename_color = cBOLD;
782     else
783         filename_color = cNORMAL;
784 }
785 
786 static void
t_term(void)787 t_term(void)
788 {
789 #ifndef DJGCC_386
790 
791     if (unix_tc)
792         return;
793 
794     /*
795      * Restore scroll window if term has that capability.
796      */
797     if (Tcs || TcS || Twi)
798         if (unix_tdb)
799             t_set_scroll_region(0, LINES*2 - 1);
800         else
801             t_set_scroll_region(0, LINES - 1);
802 
803     /*
804      * Disable all attributes (if possible), including standout.
805      */
806     t_puts(Tme);
807     t_puts(Tse);
808     t_puts(Tue);
809 
810     /*
811      * Restore tty
812      */
813     t_tty(RESTORE);
814 
815     /*
816      * End visual mode
817      */
818     t_puts(Tte);
819 
820     /*
821      * Make cursor visible
822      */
823     t_puts(Tve);
824 
825 #endif  /* DJGCC_386 */
826 }
827 
828 /*
829  * Hide cursor
830  */
831 static void
t_hide(void)832 t_hide(void)
833 {
834 #ifdef  DJGCC_386
835     void bios_video_cursor_hide();
836 #else   /* DJGCC_386 */
837 
838     if (hideshow_ok)
839         t_puts(Tvi);
840 
841 #endif  /* DJGCC_386 */
842 }
843 
844 /*
845  * Show cursor
846  */
847 static void
t_show(void)848 t_show(void)
849 {
850 #ifdef  DJGCC_386
851     void bios_video_cursor_hide();
852 #else   /* DJGCC_386 */
853 
854     if (hideshow_ok)
855         t_puts(Tve);
856 
857 #endif  /* DJGCC_386 */
858 }
859 
860 /*
861  * Write a character to the screen (without padding)
862  */
863 static void
t_putc(char c)864 t_putc(char c)
865 {
866 #ifndef DJGCC_386
867     fputc(c, stdout);
868 #endif
869 }
870 
871 /*
872  * Write a string to the screen (without padding)
873  */
874 static void
t_outs(char * s)875 t_outs(char *s)
876 {
877 #ifdef  DJGCC_386
878 
879     bios_video_write_string(s);
880 
881 #else   /* DJGCC_386 */
882     if (s) {
883         printf("%s", s);
884         fflush(stdout);
885     }
886 #endif
887 }
888 
889 /*
890  * Write an escape sequence to the string to the screen with padding
891  */
892 static void
t_puts(char * s)893 t_puts(char *s)
894 {
895 #ifndef DJGCC_386
896 
897 #if 0
898     if (s)
899         printf("%s", s);
900     return;
901 #endif
902 
903     if (s) {
904         Tputs(s, 1, (int (*)())t_putc);
905         fflush(stdout);
906     }
907 #endif
908 }
909 
910 /*
911  * Init or restore terminal.
912  */
913 static void
t_tty(int action)914 t_tty(int action)
915 {
916 #ifndef DJGCC_386
917 
918 #ifdef  USE_SGTTY
919     struct sgttyb       t;
920     struct ltchars      t1;
921     static struct sgttyb    orig;
922 #else   /* USE_SGTTY */
923     struct termios      t;
924     static struct termios   orig;
925 #endif  /* USE_SGTTY */
926 
927     if (action == RESTORE) {
928 
929 #ifdef  USE_IOCTL_INSTEAD_OF_TERMIOS
930 #ifdef  USE_SGTTY
931         ioctl(0, TIOCSETP, &orig);
932 #else
933         ioctl(0, TCSETSW, &orig);
934 #endif /* USE_SGTTY */
935 #else
936         tcsetattr(0, TCSANOW, &orig);
937 #endif
938     }
939     else {
940 
941 #ifdef  USE_IOCTL_INSTEAD_OF_TERMIOS
942 #ifdef  USE_SGTTY
943         ioctl(0, TIOCGETP, &orig);
944         ioctl(0, TIOCGETP, &t);
945 #else
946         ioctl(0, TCGETS, &orig);
947         ioctl(0, TCGETS, &t);
948 #endif /* USE_SGTTY */
949 #else
950         tcgetattr(0, &orig);
951         tcgetattr(0, &t);
952 #endif
953 
954 #ifdef  USE_SGTTY
955         t.sg_flags &= ~(XTABS | ECHO | CRMOD);
956         t.sg_flags |= CBREAK;
957 #else
958         t.c_lflag &= ~(ICANON | ECHO | ICRNL);
959         t.c_lflag |= INLCR;
960         t.c_cc[VMIN] = 1;
961         t.c_cc[VTIME] = 0;
962 #if !defined(SGI_IRIX)
963         t.c_oflag &= (~OXTABS);
964 #else
965         t.c_oflag &= (~TAB3);
966 #endif
967 #endif  /* USE_SGTTY */
968 
969 #ifdef  USE_IOCTL_INSTEAD_OF_TERMIOS
970 #ifdef  USE_SGTTY
971         ioctl(0, TIOCSETP, &t);
972 #else
973         ioctl(0, TCSETSW, &t);
974 #endif  /* USE_SGTTY */
975 #else
976         tcsetattr(0, TCSANOW, &t);
977 #endif
978     }
979 
980     /*
981      * Determine baud rate for Tputs.
982      * Look for it in termios struct; if not found, assume 2400 baud.
983      */
984 #ifdef  USE_SGTTY
985     ospeed = t.sg_ospeed;
986 #else
987 	ospeed = cfgetospeed(&t);
988 #endif
989     if (ospeed == 0)
990         ospeed = 11;
991 
992     /*
993      * Get special keys
994      */
995 #ifdef  USE_SGTTY
996     k_erase = t.sg_erase;
997     k_kill = t.sg_kill;
998     ioctl(0, TIOCGLTC, &t1);
999     k_word_erase = t1.t_werasc;
1000     k_reprint = t1.t_rprntc;
1001 #else   /* USE_SGTTY */
1002     k_erase = t.c_cc[VERASE];
1003     k_kill = t.c_cc[VKILL];
1004     k_word_erase = t.c_cc[VWERASE];
1005 
1006 #if defined(SGI_IRIX) || defined(ULTRIX_MIPS)
1007     k_reprint = t.c_cc[VRPRNT];
1008 #else
1009     k_reprint = t.c_cc[VREPRINT];
1010 #endif
1011 #endif  /* USE_SGTTY */
1012 
1013 #endif  /* DJGCC_386 */
1014 }
1015 
1016 int
os_init(int * argc,char * argv[],const char * prompt,char * buf,int bufsiz)1017 os_init( int *argc, char *argv[], const char *prompt, char *buf, int bufsiz )
1018 {
1019 #ifdef  DJGCC_386
1020     FILE    *fp;
1021     char    nbuf[128];
1022 
1023     /*
1024      * Monochrome monitor?
1025      */
1026     if (bios_video_monochrome()) {
1027         revcolor = 0x70;
1028         plaincolor = 7;
1029         boldcolor = 0x70;
1030     }
1031     else if ( os_locate("trcolor.dat", 11, argv[0], nbuf, sizeof(nbuf))
1032          && (fp = fopen(nbuf, "r")) ) {
1033         int     i;
1034         static int *colorptr[] = { &plaincolor, &boldcolor, &revcolor };
1035 
1036         for (i = 0 ; i < sizeof(colorptr)/sizeof(colorptr[0]) ; ++i)
1037         {
1038             fgets(nbuf, sizeof(nbuf), fp);
1039             *colorptr[i] = atoi(nbuf);
1040         }
1041         fclose(fp);
1042     }
1043     else {
1044         plaincolor = 7;
1045         revcolor = 0x70;
1046         boldcolor = 15;
1047     }
1048     text_color = text_normal_color;
1049 #endif  /* DJGCC_386 */
1050 
1051     t_init();
1052 
1053     /*
1054      * Clear screen
1055      */
1056     if (unix_tdb) {
1057         cw = 0;
1058         ossclr(0, 0, LINES - 1, COLS - 1, cNORMAL);
1059         cw = 1;
1060         ossclr(0, 0, LINES - 1, COLS - 1, cNORMAL);
1061     }
1062     else
1063         ossclr(0, 0, LINES - 1, COLS - 1, cNORMAL);
1064 
1065 #ifdef USE_SCROLLBACK
1066 
1067     osssbini(32767*8);      /* allocate scrollback buffer */
1068 
1069 #endif /* USE_SCROLLBACK */
1070 
1071     if (!unix_tc) {
1072         status_mode = 1;
1073 #ifdef  DJGCC_386
1074         os_printz( "TADS (GO32 version)" );
1075 #else
1076         os_printz( "TADS" );
1077 #endif
1078         status_mode = 0;
1079         os_score(0, 0);
1080     }
1081 
1082     return 0;
1083 }
1084 
1085 void
os_term(int rc)1086 os_term(int rc)
1087 {
1088     if (!unix_tc) {
1089         os_printz("[strike a key to exit]");
1090         os_waitc();
1091         os_printz("\n");
1092     }
1093 
1094 #ifdef USE_SCROLLBACK
1095     osssbdel();
1096 #endif /* USE_SCROLLBACK */
1097 
1098     exit(0); /* always return zero since mainline doesn't set it right */
1099 }
1100 
os_uninit()1101 void os_uninit()
1102 {
1103     t_term();
1104 }
1105 
1106 /*
1107  *   Check for control-break.  Returns status of break flag, and clears
1108  *   the flag.
1109  */
1110 int
os_break(void)1111 os_break(void)
1112 {
1113     int ret;
1114 
1115     ret = break_set;
1116     break_set = 0;
1117     return ret;
1118 }
1119 
1120 /*
1121  * Handle ^C.
1122  */
1123 static void
break_handler(int sig)1124 break_handler(int sig)
1125 {
1126     break_set = 1;
1127 }
1128 
1129 /*
1130  *   Get an event--a key press, a timeout, etc. Added by SRG.
1131  */
os_get_event(unsigned long timeout,int use_timeout,os_event_info_t * info)1132 int os_get_event(unsigned long timeout, int use_timeout,
1133          os_event_info_t *info)
1134 {
1135     fd_set readfds;
1136     struct timeval tv;
1137     int    retval;
1138 
1139     /* We're watching stdin (fd 0) for input */
1140     FD_ZERO(&readfds);
1141     FD_SET(0, &readfds);
1142 
1143     /* Wait for the specified time, if we're supposed to */
1144     tv.tv_sec = (int)(timeout / 1000);
1145     tv.tv_usec = timeout % 1000;
1146 
1147     /* Redraw the screen, if necessary */
1148     osssb_redraw_if_needed();
1149 
1150     if (use_timeout)
1151         retval = select(1, &readfds, NULL, NULL, &tv);
1152     else
1153         retval = select(1, &readfds, NULL, NULL, NULL);
1154 
1155     if (!retval)
1156         return OS_EVT_TIMEOUT;
1157 
1158     (*info).key[0] = os_getc_raw();
1159     return OS_EVT_KEY;
1160 }
1161 
1162 #ifdef  DJGCC_386
1163 
os_getch(void)1164 int os_getch(void)
1165 {
1166     return bios_getchar();
1167 }
1168 
os_waitc(void)1169 void os_waitc(void)
1170 {
1171     int c = os_getch();
1172 
1173     if ((c == 0 || c == 0340)) {
1174         os_getch();
1175     }
1176 }
1177 
os_getc(void)1178 int os_getc(void)
1179 {
1180     static char altkeys[] = {
1181         30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50,
1182         49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44
1183     };
1184 
1185     int     c;
1186     static int  cmdval;
1187 
1188     if (cmdval) {
1189         int tmpcmd = cmdval;            /* save command for a moment */
1190         cmdval = 0;       /* clear the flag - next call gets another key */
1191         return tmpcmd;            /* return the command that we saved */
1192     }
1193 
1194 tryAgain:
1195     c = os_getch();
1196 
1197     if ((c == 0 || c == 0340))  {               /* Extended keycode? */
1198         int   i;
1199         char *p;
1200 
1201         c = os_getch();
1202 
1203         /* check for alt keys */
1204         cmdval = 0;
1205         for (i = 0, p = altkeys ; i < 26 ; ++p, ++i)
1206         {
1207             if (c == *p)
1208             {
1209                 cmdval = CMD_ALT + i;
1210                 break;
1211             }
1212         }
1213 
1214         if (!cmdval) switch(c) {
1215             case 0110:  /* Up Arrow */
1216                 cmdval = CMD_UP;
1217                 break;
1218             case 0120:  /* Down Arrow */
1219                 cmdval = CMD_DOWN;
1220                 break;
1221             case 0115:  /* Right Arrow */
1222                 cmdval = CMD_RIGHT;
1223                 break;
1224             case 0113:  /* Left Arrow */
1225                 cmdval = CMD_LEFT;
1226                 break;
1227             case 0117:  /* End */
1228                 cmdval = CMD_END;
1229                 break;
1230             case 0107:  /* Home */
1231                 cmdval = CMD_HOME;
1232                 break;
1233             case 0165:  /* Ctrl-End */
1234                 cmdval = CMD_DEOL;
1235                 break;
1236             case 0123:  /* Del */
1237                 cmdval = CMD_DEL;
1238                 break;
1239             case 073:   /* F1 - change this? */
1240                 cmdval = CMD_SCR;
1241                 break;
1242             case 0111:  /* PgUp */
1243                 cmdval = CMD_PGUP;
1244                 break;
1245             case 0121:  /* PgDn */
1246                 cmdval = CMD_PGDN;
1247                 break;
1248             case 132:   /* control-PgUp */
1249                 cmdval = CMD_TOP;
1250                 break;
1251             case 118:   /* control-PgDn */
1252                 cmdval = CMD_BOT;
1253                 break;
1254             case 119:   /* control home */
1255                 cmdval = CMD_CHOME;
1256                 break;
1257             case 115:   /* control left arrow */
1258                 cmdval = CMD_WORD_LEFT;
1259                 break;
1260             case 116:   /* control right arrow */
1261                 cmdval = CMD_WORD_RIGHT;
1262                 break;
1263 
1264             case 60: cmdval = CMD_F2; break;
1265             case 61: cmdval = CMD_F3; break;
1266             case 62: cmdval = CMD_F4; break;
1267             case 63: cmdval = CMD_F5; break;
1268             case 64: cmdval = CMD_F6; break;
1269             case 65: cmdval = CMD_F7; break;
1270             case 66: cmdval = CMD_F8; break;
1271             case 67: cmdval = CMD_F9; break;
1272             case 68: cmdval = CMD_F10; break;
1273 
1274             case 85: cmdval = CMD_SF2; break;             /* shifted F2 */
1275 
1276             default:    /* Unrecognized function key */
1277                 cmdval = 0;
1278                 break;
1279         }
1280     }
1281     else
1282     {
1283         switch( c )
1284         {
1285             case 1:     /* ^A */
1286                 cmdval = CMD_HOME;
1287                 break;
1288             case 2:     /* ^B */
1289                 cmdval = CMD_LEFT;
1290                 break;
1291             case 4:     /* ^D */
1292                 cmdval = CMD_DEL;
1293                 break;
1294             case 5:     /* ^E */
1295                 cmdval = CMD_END;
1296                 break;
1297             case 6:     /* ^F */
1298                 cmdval = CMD_RIGHT;
1299                 break;
1300             case '\t':
1301                 cmdval = CMD_TAB;
1302                 break;
1303             case 11:    /* ^K */
1304                 cmdval = CMD_DEOL;
1305                 break;
1306             case 14:    /* ^N */
1307                 cmdval = CMD_DOWN;
1308                 break;
1309             case 16:    /* ^P */
1310                 cmdval = CMD_UP;
1311                 break;
1312             case 21:    /* ^U */
1313             case 27:    /* Escape */
1314                 cmdval = CMD_KILL;
1315                 break;
1316         }
1317     }
1318 
1319     if (cmdval)
1320         return 0;
1321     else if (c & 0xff)
1322         return c & 0xff;
1323     else
1324         goto tryAgain;
1325 }
1326 
os_getc_raw(void)1327 int os_getc_raw(void)
1328 {
1329     static char altkeys[] = {
1330         30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50,
1331         49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44
1332     };
1333 
1334     int     c;
1335     static int  cmdval;
1336 
1337     if (cmdval) {
1338         int tmpcmd = cmdval;                   /* save command for a moment */
1339         cmdval = 0;          /* clear the flag - next call gets another key */
1340         return tmpcmd;                  /* return the command that we saved */
1341     }
1342 
1343 tryAgain:
1344     c = os_getch();
1345 
1346     if ((c == 0 || c == 0340))  {                      /* Extended keycode? */
1347         int   i;
1348         char *p;
1349 
1350         c = os_getch();
1351 
1352         /* check for alt keys */
1353         cmdval = 0;
1354         for (i = 0, p = altkeys ; i < 26 ; ++p, ++i)
1355         {
1356             if (c == *p)
1357             {
1358                 cmdval = CMD_ALT + i;
1359                 break;
1360             }
1361         }
1362 
1363         if (!cmdval) switch(c) {
1364         case 0110:                                              /* Up Arrow */
1365             cmdval = CMD_UP;
1366             break;
1367         case 0120:                                            /* Down Arrow */
1368             cmdval = CMD_DOWN;
1369             break;
1370         case 0115:                                           /* Right Arrow */
1371             cmdval = CMD_RIGHT;
1372             break;
1373         case 0113:                                            /* Left Arrow */
1374             cmdval = CMD_LEFT;
1375             break;
1376         case 0117:                                                   /* End */
1377             cmdval = CMD_END;
1378             break;
1379         case 0107:                                                  /* Home */
1380             cmdval = CMD_HOME;
1381             break;
1382         case 0165:                                              /* Ctrl-End */
1383             cmdval = CMD_DEOL;
1384             break;
1385         case 0123:                                                   /* Del */
1386             cmdval = CMD_DEL;
1387             break;
1388         case 0111:                                                  /* PgUp */
1389             cmdval = CMD_PGUP;
1390             break;
1391         case 0121:                                                  /* PgDn */
1392             cmdval = CMD_PGDN;
1393             break;
1394         case 132:                                           /* control-PgUp */
1395             cmdval = CMD_TOP;
1396             break;
1397         case 118:                                           /* control-PgDn */
1398             cmdval = CMD_BOT;
1399             break;
1400         case 119:                                           /* control home */
1401             cmdval = CMD_CHOME;
1402             break;
1403         case 115:                                     /* control left arrow */
1404             cmdval = CMD_WORD_LEFT;
1405             break;
1406         case 116:                                    /* control right arrow */
1407             cmdval = CMD_WORD_RIGHT;
1408             break;
1409 
1410         case 59: cmdval = CMD_F1; break;
1411         case 60: cmdval = CMD_F2; break;
1412         case 61: cmdval = CMD_F3; break;
1413         case 62: cmdval = CMD_F4; break;
1414         case 63: cmdval = CMD_F5; break;
1415         case 64: cmdval = CMD_F6; break;
1416         case 65: cmdval = CMD_F7; break;
1417         case 66: cmdval = CMD_F8; break;
1418         case 67: cmdval = CMD_F9; break;
1419         case 68: cmdval = CMD_F10; break;
1420 
1421         case 85: cmdval = CMD_SF2; break;                     /* shifted F2 */
1422 
1423         default:                               /* Unrecognized function key */
1424             cmdval = 0;
1425             break;
1426         }
1427     }
1428 
1429     if (cmdval)
1430         return 0;
1431     else if (c & 0xff)
1432         return c & 0xff;
1433     else
1434         goto tryAgain;
1435 }
1436 
1437 #else   /* DJGCC_386 */
1438 
1439 int
os_getc(void)1440 os_getc(void)
1441 {
1442     char        c;
1443     static char cbuf;
1444     static int  buffered = 0;
1445     os_event_info_t evt;
1446 
1447     /* if there's a special key pending, return it */
1448     if (buffered) {
1449         buffered = 0;
1450         return cbuf;
1451     }
1452 
1453     /* get the next character from standard input (non-blocking) */
1454     read(0, &c, 1);
1455 
1456     /* refresh the screen on ^L */
1457     while (c == 12 || c == k_reprint) {
1458         t_refresh();
1459         read(0, &c, 1);
1460     }
1461 
1462     /* translate the raw keystroke to a CMD_xxx sequence */
1463     evt.key[0] = c;
1464     oss_raw_key_to_cmd(&evt);
1465 
1466     /* read the new keystroke from the translated event */
1467     c = evt.key[0];
1468     cbuf = evt.key[1];
1469 
1470     /* if it's special, note the buffered command code for the next call */
1471     if (c == 0)
1472         buffered = 1;
1473 
1474     /* return the primary key code */
1475     return c;
1476 }
1477 
1478 void
oss_raw_key_to_cmd(os_event_info_t * evt)1479 oss_raw_key_to_cmd(os_event_info_t *evt)
1480 {
1481     char c;
1482     char cbuf;
1483 
1484 #define ctrl(c) (c - 'A' + 1)
1485 
1486     /* get the primary character of the keystroke event */
1487     c = evt->key[0];
1488 
1489     /* map NL to CR */
1490     if (c == 10)
1491         c = 13;
1492 
1493     /* handle special keys first, since we can't use them as switch cases */
1494     if (c == k_erase || c == 0x7F)         /* erase key or DEL -> backspace */
1495         c = ctrl('H');
1496     if (c == k_kill)
1497         c = ctrl('U');
1498 
1499     /* map special keys to command codes */
1500     switch (c) {
1501     case 0:
1502         c = 1;
1503         break;
1504 
1505     case 10:
1506         c = 13;
1507         break;
1508 
1509     case ctrl('W'):
1510         c = 0;
1511         cbuf = CMD_WORDKILL;
1512         break;
1513 
1514     case ctrl('D'):
1515         c = 0;
1516         cbuf = CMD_DEL;
1517         break;
1518 
1519     case ctrl('U'):
1520         c = 0;
1521         cbuf = CMD_KILL;
1522         break;
1523 
1524     case ctrl('K'):
1525         c = 0;
1526         cbuf = CMD_DEOL;
1527         break;
1528 
1529     case ctrl('P'):
1530         c = 0;
1531         cbuf = CMD_UP;
1532         break;
1533 
1534     case ctrl('N'):
1535         c = 0;
1536         cbuf = CMD_DOWN;
1537         break;
1538 
1539     case ctrl('F'):
1540         c = 0;
1541         cbuf = CMD_RIGHT;
1542         break;
1543 
1544     case ctrl('B'):
1545         c = 0;
1546         cbuf = CMD_LEFT;
1547         break;
1548 
1549     case ctrl('E'):
1550         c = 0;
1551         cbuf = CMD_END;
1552         break;
1553 
1554     case ctrl('A'):
1555         c = 0;
1556         cbuf = CMD_HOME;
1557         break;
1558 
1559     case 27:
1560         c = 0;
1561         cbuf = CMD_SCR;
1562         break;
1563 
1564     case '<':
1565         c = 0;
1566         cbuf = CMD_PGUP;
1567         break;
1568 
1569     case '>':
1570         c = 0;
1571         cbuf = CMD_PGDN;
1572         break;
1573     }
1574 
1575     /* store the two-character sequence back in the event */
1576     evt->key[0] = c;
1577     evt->key[1] = cbuf;
1578 }
1579 
1580 
1581 int
os_getc_raw(void)1582 os_getc_raw(void)
1583 {
1584     char c;
1585 
1586     /*
1587      *   Get the next character from standard input.  (Non-blocking)
1588      */
1589     read(0, &c, 1);
1590 
1591     /*
1592      *   Handle special keys.  os_gets expects certain keys to be certain
1593      *   codes.
1594      */
1595     if (c == k_erase || c == 0x7F)         /* erase key or DEL -> backspace */
1596         c = ctrl('H');
1597     if (c == k_kill)
1598         c = ctrl('U');
1599 
1600     /* handle some more special key translations */
1601     switch (c) {
1602     case 0:
1603         c = 1;
1604         break;
1605 
1606     case 10:
1607         /* map newline to return */
1608         c = 13;
1609         break;
1610     }
1611 
1612     /* return the key */
1613     return c;
1614 #undef ctrl
1615 }
1616 
1617 void
os_waitc(void)1618 os_waitc(void)
1619 {
1620     char    c;
1621 
1622     /*
1623      * Get the next character from standard input.
1624      * Wait until we get one.
1625      */
1626     while (read(0, &c, 1) < 1);
1627 }
1628 #endif  /* DJGCC_386 */
1629 
1630 /*
1631  * Update the real screen so it matches our idea of what's on the screen.
1632  */
1633 static void
t_refresh(void)1634 t_refresh(void)
1635 {
1636     if (unix_tdb) {
1637         t_redraw(0, LINES * 2 - 1);
1638     }
1639     else
1640         t_redraw(0, LINES - 1);
1641 }
1642 
1643 /*
1644  * Update internal understanding of where the physical cursor is.
1645  */
1646 static void
t_update(int y,int x)1647 t_update(int y, int x)
1648 {
1649     cursorX = x;
1650     cursorY = y;
1651 }
1652 
1653 /*
1654  * Move the cursor.  Note that this has nothing to do with the virtual cursor
1655  * that TADS tells us to move around -- that's updated by ossloc.
1656  *
1657  * The force parameter tells us to not use the current value of cursorX
1658  * and cursorY and to reposition the cursor absolutely.
1659  *
1660  */
1661 static void
t_loc(int y,int x,int force)1662 t_loc(int y, int x, int force)
1663 {
1664 #ifdef  DJGCC_386
1665 
1666     bios_video_set_cursor_position(x, y);
1667 
1668 #else   /* DJGCC_386 */
1669 
1670     register int    i;
1671 
1672 #if 0
1673 #define X printf("[line %d]\n", __LINE__);
1674 #else
1675 #define X
1676 #endif
1677 
1678     if (!force)
1679         if (cursorX == x && cursorY == y)
1680             return;
1681 
1682 #if 0
1683     printf("[*%d, %d*]", x, y);
1684 #endif
1685 
1686     /*
1687      * User direct cursor addressing if we have it; otherwise
1688      * home the cursor and move right and down from there.
1689      *
1690      * If we can get to the destiation by moving just one
1691      * space, we'll use the shorter single movement commands.
1692      */
1693     if (!force && x == cursorX && y == cursorY - 1 && (Tup || TUP)) {
1694 X
1695         /*
1696          * Up one space
1697          */
1698         if (Tup)
1699             t_puts(Tup);
1700         else
1701             t_puts(Tparm(TUP, 1));
1702     }
1703     else if (!force && x == cursorX && y == cursorY + 1 && (Tdo || TDO)) {
1704 X
1705         /*
1706          * Down one space
1707          */
1708         if (Tdo)
1709             t_puts(Tdo);
1710         else
1711             t_puts(Tparm(TDO, 1));
1712     }
1713     else if (!force && y == cursorY && x == cursorX + 1 && (Tnd || TRI)) {
1714 X
1715         /*
1716          * Right one space
1717          */
1718         if (Tnd)
1719             t_puts(Tnd);
1720         else
1721             t_puts(Tparm(TRI, 1));
1722     }
1723     else if (!force && y == cursorY && x == cursorX - 1 && (Tle || TLE)) {
1724 X
1725         /*
1726          * Left one space
1727          */
1728         if (Tle)
1729             t_puts(Tle);
1730         else
1731             t_puts(Tparm(TLE, 1));
1732     }
1733     else if (Tcm) {
1734 X
1735         t_puts(Tparm(Tcm, y, x));
1736     }
1737     else {
1738 X
1739         t_puts(Tho);
1740 
1741         if (TRI)
1742             t_puts(Tparm(TRI, x - 1));
1743         else
1744             for (i = 0; i < x; i++)
1745                 t_puts(Tnd);
1746 
1747         if (TDO)
1748             t_puts(Tparm(TDO, y - 1));
1749         else
1750             for (i = 0; i < y; i++)
1751                 t_puts(Tdo);
1752     }
1753 
1754 #undef X
1755     t_update(y, x);
1756 
1757 #endif  /* DJGCC_386 */
1758 }
1759 
1760 /*
1761  * Change subsequent text to new color.
1762  */
1763 static void
t_color(char c)1764 t_color(char c)
1765 {
1766 #ifdef  DJGCC_386
1767 
1768     if (c & cREVERSE)
1769         bios_video_set_color(revcolor);
1770     else if (c & cBOLD)
1771         bios_video_set_color(boldcolor);
1772     else
1773         bios_video_set_color(plaincolor);
1774 
1775 #else   /* DJGCC_386 */
1776 
1777     if (c == COLOR)
1778         return;
1779 
1780     /*
1781      * Disable all attributes (if possible), including standout.
1782      */
1783     t_puts(Tme);
1784     if (standout_ok && !(c & cSTANDOUT))
1785         t_puts(Tse);
1786     if (underscore_ok && !(c & cUNDERSCORE))
1787         t_puts(Tue);
1788 
1789     if (c & cSTANDOUT)
1790         if (standout_ok)
1791             t_puts(Tso);
1792     if (c & cREVERSE)
1793         if (rev_ok)
1794             t_puts(Tmr);
1795     if (c & cBOLD)
1796         if (bold_ok)
1797             t_puts(Tmd);
1798     if (c & cBLINK)
1799         if (blink_ok)
1800             t_puts(Tmb);
1801     if (c & cUNDERSCORE)
1802         if (underscore_ok)
1803             t_puts(Tus);
1804 
1805     /*
1806      * Save color.
1807      */
1808     COLOR = c;
1809 
1810 #endif  /* DJGCC_386 */
1811 }
1812 
1813 /*
1814  * Redraw a portion of the screen.
1815  */
1816 static void
t_redraw(int top,int bottom)1817 t_redraw(int top, int bottom)
1818 {
1819     static char ds[MAXCOLS + 1] = {0};
1820 
1821     register int    row, col, pos;
1822     int     color, newcolor;
1823 
1824     /*
1825      * Hide cursor
1826      */
1827     t_hide();
1828 
1829     /*
1830      * Position to top line, column zero.
1831      */
1832     t_loc(top, 0, 1);
1833 
1834     /*
1835      * Redraw each line between top line and bottom line.
1836      * We have to build a string from our text and color
1837      * information so we can write the output a line at a time
1838      * rather than a character at a time.
1839      */
1840     color = colors[top][0];
1841     t_color(color);
1842     for (row = top; row <= bottom; row++) {
1843         /*
1844          * Move cursor to beginning of this line
1845          */
1846         t_loc(row, 0, 1);
1847 
1848         /*
1849          * Combine color and text information into a single
1850          * string for this line.
1851          */
1852         for (col = 0, pos = 0; col < COLS; col++) {
1853             /*
1854              * Do we have to change text color?
1855              * If so, print the text we've buffered for
1856              * this line and then print the change string.
1857              */
1858             if ((newcolor = colors[row][col]) != color) {
1859                 ds[pos] = 0;
1860                 t_outs(ds);
1861                 t_color(newcolor);
1862                 t_loc(row, col, 1);
1863                 pos = 0;
1864                 color = newcolor;
1865             }
1866             ds[pos++] = screen[row][col];
1867         }
1868 
1869         ds[pos] = 0;
1870         t_outs(ds);     /* blast the string */
1871     }
1872 
1873     /*
1874      * Reposition cursor to its former location
1875      */
1876     t_loc(inputY, inputX, 1);
1877 
1878     /*
1879      * Show cursor
1880      */
1881     t_show();
1882 }
1883 
1884 /*
1885  * Scroll region
1886  * lines < 0 -> scroll up (delete line)
1887  * lines > 0 -> scroll down (insert line)
1888  */
1889 static void
t_scroll(int top,int bot,int lines)1890 t_scroll(int top, int bot, int lines)
1891 {
1892 #ifdef  DJGCC_386
1893 
1894     bios_video_scroll_region(top, bot, 0, COLS - 1, lines);
1895 
1896 #else   /* DJGCC_386 */
1897 
1898     char    *single = lines > 0 ? Tsr : Tsf;
1899     char    *multi  = lines > 0 ? TSR : TSF;
1900     int labs = lines < 0 ? -lines : lines;
1901     int i;
1902 
1903     /*
1904      * Make sure new lines have the right background color
1905      */
1906     t_color(text_normal_color);
1907 
1908     /*
1909      * If we have scroll region capability, use it.
1910      * Otherwise fake it with insert/delete line.
1911      */
1912     if ((single || multi) && (Tcs || TcS || Twi)) {
1913         t_set_scroll_region(top, bot);
1914 
1915         if (lines < 0)
1916             t_loc(bot, 0, 1);
1917         else
1918             t_loc(top, 0, 1);
1919 
1920         if (labs > 1 && multi || !single)
1921             t_puts(Tparm(multi, labs));
1922         else
1923             for (i = 0; i < labs; i++)
1924                 t_puts(single);
1925 
1926         t_set_scroll_region(0, LINES - 1);
1927     }
1928     else {
1929         /*
1930          * Make sure we never eat lines below the window
1931          */
1932         if (labs> bot - top + 1)
1933             labs = bot - top + 1;
1934 
1935         if (lines < 0) {
1936             /*
1937              * Delete lines
1938              */
1939             t_loc(top, 0, 1);
1940             if (TDL && labs != 1) {
1941                 t_puts(Tparm(TDL, labs));
1942             }
1943             else if (Tdl) {
1944                 for (i = 0; i < labs; i++)
1945                     t_puts(Tdl);
1946             }
1947             else {
1948                 /* shouldn't happen */
1949             }
1950 
1951             /*
1952              * Insert lines to keep windows below this
1953              * one intact.
1954              */
1955             if (bot < LINES - 1) {
1956                 t_loc(bot + 1 - labs, 0, 1);
1957                 if (TAL && labs != 1) {
1958                     t_puts(Tparm(TAL, labs));
1959                 }
1960                 else if (Tal) {
1961                     for (i = 0; i < labs; i++)
1962                         t_puts(Tal);
1963                 }
1964                 else {
1965                     /* shouldn't happen */
1966                 }
1967             }
1968         }
1969         else {
1970             /*
1971              * Insert lines
1972              */
1973 
1974             /*
1975              * Delete lines to keep windows below this
1976              * one intact.
1977              */
1978             if (bot < LINES - 1) {
1979                 t_loc(bot + 1 - labs, 0, 1);
1980                 if (TDL && labs != 1) {
1981                     t_puts(Tparm(TDL, labs));
1982                 }
1983                 else if (Tdl) {
1984                     for (i = 0; i < labs; i++)
1985                         t_puts(Tdl);
1986                 }
1987                 else {
1988                     /* shouldn't happen */
1989                 }
1990             }
1991 
1992 
1993             /*
1994              * Insert lines at top of window.
1995              */
1996             t_loc(top, 0, 1);
1997             if (TAL && labs != 1) {
1998                 t_puts(Tparm(TAL, labs));
1999             }
2000             else if (Tal) {
2001                 for (i = 0; i < labs; i++)
2002                     t_puts(Tal);
2003             }
2004             else {
2005                 /* shouldn't happen */
2006             }
2007         }
2008     }
2009 
2010     /*
2011      * After we scroll, we don't know where the cursor is.
2012      */
2013     t_update(-1, -1);
2014 
2015 #endif  /* DJGCC_386 */
2016 }
2017 
2018 static void
t_set_scroll_region(int top,int bot)2019 t_set_scroll_region(int top, int bot)
2020 {
2021 #ifndef DJGCC_386
2022     static int  topsave = -1, botsave = -1;
2023 
2024     if (top == topsave && bot == botsave)
2025         return;
2026 
2027     if (Tcs) {
2028         t_puts(Tparm(Tcs, top, bot));
2029     }
2030     else if (TcS) {
2031         t_puts(Tparm(TcS, LINES, top, LINES - (bot + 1), LINES));
2032     }
2033     else {
2034         t_puts(Tparm(Twi, top, 0, bot, COLS - 1));
2035     }
2036 
2037     topsave = top;
2038     botsave = bot;
2039 #endif
2040 }
2041 
2042 /*
2043  * Scroll region down a line.
2044  * This is equivalent to inserting a line at the top of the region.
2045  */
2046 void
ossscu(int top,int left,int bottom,int right,int blank_color)2047 ossscu(int top, int left, int bottom, int right, int blank_color)
2048 {
2049     register int    r1, r2, col;
2050 
2051     if (unix_tdb) {
2052         top += LINES * cw;
2053         bottom += LINES * cw;
2054     }
2055 
2056 #ifdef DEBUG_OUTPUT
2057     printf("[ossscu(%d, %d, %d, %d, %d)]",
2058         top, left, bottom, right, blank_color);
2059 #endif
2060 
2061     if (unix_tc)
2062         return;
2063 
2064     /*
2065      * Update our internal version
2066      */
2067     for (r1 = bottom - 1, r2 = bottom; r1 >= top; r1--, r2--)
2068         for (col = left; col <= right; col++) {
2069             screen[r2][col] = screen[r1][col];
2070             colors[r2][col] = colors[r1][col];
2071         }
2072     for (col = left; col <= right; col++) {
2073         screen[r2][col] = ' ';
2074         colors[r2][col] = blank_color;
2075     }
2076 
2077     /*
2078      * If we can duplicate the effect of this scroll on the screen
2079      * with scrolling commands, do so; otherwise, refresh by redrawing
2080      * every affected line.
2081      */
2082 #ifdef  T_OPTIMIZE
2083     if (left == 0 && right >= unix_max_column) {
2084         t_scroll(top, bottom, 1);
2085     }
2086     else {
2087 #else
2088         t_redraw(top, bottom);
2089 #endif
2090 
2091 #ifdef  T_OPTIMIZE
2092     }
2093 #endif
2094 }
2095 
2096 /*
2097  * Scroll region up a line
2098  * This is equivalent to deleting a line at the top of the region and pulling
2099  * everything below it up.
2100  */
2101 void
ossscr(int top,int left,int bottom,int right,int blank_color)2102 ossscr(int top, int left, int bottom, int right, int blank_color)
2103 {
2104     register int    r1, r2, col;
2105 
2106     if (unix_tdb) {
2107         top += LINES * cw;
2108         bottom += LINES * cw;
2109     }
2110 
2111 #ifdef DEBUG_OUTPUT
2112     printf("[ossscr(%d, %d, %d, %d, %d)]",
2113         top, left, bottom, right, blank_color);
2114 #endif
2115 
2116     if (unix_tc) {
2117         putchar('\n');
2118         return;
2119     }
2120 
2121     /*
2122      * Update our internal version
2123      */
2124     for (r1 = top, r2 = top + 1; r2 <= bottom; r1++, r2++)
2125         for (col = left; col <= right; col++) {
2126             screen[r1][col] = screen[r2][col];
2127             colors[r1][col] = colors[r2][col];
2128         }
2129     for (col = left; col <= right; col++) {
2130         screen[r1][col] = ' ';
2131         colors[r1][col] = blank_color;
2132     }
2133 
2134     /*
2135      * If we can duplicate the effect of this scroll on the screen
2136      * with a scrolling command, do so; otherwise, refresh by redrawing
2137      * every affected line.
2138      */
2139 #ifdef  T_OPTIMIZE
2140     if (left == 0 && right >= unix_max_column) {
2141         t_scroll(top, bottom, -1);
2142     }
2143     else {
2144 #else
2145         t_redraw(top, bottom);
2146 #endif
2147 
2148 #ifdef  T_OPTIMIZE
2149     }
2150 #endif
2151 }
2152 
2153 /*
2154  * Clear region (fill with spaces)
2155  */
2156 void
ossclr(int top,int left,int bottom,int right,int blank_color)2157 ossclr(int top, int left, int bottom, int right, int blank_color)
2158 {
2159     register int    row, col;
2160 
2161     if (unix_tdb) {
2162         top += LINES * cw;
2163         bottom += LINES * cw;
2164     }
2165 
2166 #ifdef DEBUG_OUTPUT
2167     printf("[ossclr(%d, %d, %d, %d, %d)]",
2168         top, left, bottom, right, blank_color);
2169 #endif
2170 
2171     if (unix_tc)
2172         return;
2173 
2174     /*
2175      * Update our internal version
2176      */
2177     for (row = top; row <= bottom; row++)
2178         for (col = left; col <= right; col++) {
2179             screen[row][col] = ' ';
2180             colors[row][col] = blank_color;
2181         }
2182 
2183     /*
2184      * If we can duplicate the effect of this clear on the screen
2185      * with a clear command, do so; otherwise, refresh by redrawing every
2186      * affected line.
2187      */
2188 #ifdef  DJGCC_386
2189     bios_video_set_bkgnd(plaincolor);
2190     bios_video_clear_region(top, bottom, left, right);
2191 #else   /* DJGCC_386 */
2192 
2193 #ifdef  T_OPTIMIZE
2194     if (Tce && left == 0 && right >= unix_max_column) {
2195         t_color(blank_color);
2196 
2197         if (Tcl && top == 0 && bottom >= unix_max_line) {
2198             t_loc(0, 0, 1);
2199             t_puts(Tcl);
2200         }
2201         else if (Tcd && bottom >= unix_max_line) {
2202             t_loc(top, 0, 1);
2203             t_puts(Tcd);
2204         }
2205         else for (row = top; row <= bottom; row++) {
2206             t_loc(row, 0, 1);
2207             t_puts(Tce);
2208         }
2209 
2210         /*
2211          * Don't know where the cursor is after clear.
2212          */
2213         t_update(-1, -1);
2214     }
2215     else {
2216 #endif
2217         t_redraw(top, bottom);
2218 #ifdef  T_OPTIMIZE
2219     }
2220 #endif
2221 
2222 #endif  /* DJGCC_386 */
2223 }
2224 
2225 /*
2226  * Locate (input) cursor at given row and column.
2227  * Nore that this is never used to determine where things are drawn.
2228  * It's only used for aesthetics; i.e., showing the user where input
2229  * will be taken from next.
2230  */
2231 void
ossloc(int row,int col)2232 ossloc(int row, int col)
2233 {
2234     if (unix_tc)
2235         return;
2236 
2237     if (unix_tdb)
2238         row += LINES * cw;
2239 
2240     t_loc(row, col, 0);
2241 
2242     /*
2243      * Update internal cursor position so t_redraw will
2244      * be correct.
2245      */
2246     inputX = col;
2247     inputY = row;
2248 }
2249 
2250 /*
2251  * Display msg with color at coordinates (y, x).
2252  * The color must be in the range 0 <= color <= 16, and specifies both
2253  * foreground and background colors.
2254  */
ossdsp(int y,int x,int color,const char * msg)2255 void ossdsp(int y, int x, int color, const char *msg)
2256 {
2257     register int    col;
2258     register char   *s, *m = msg, *c;
2259 
2260 #ifdef DEBUG_OUTPUT
2261     printf("[ossdsp(%d, %d, %d, \"%s\")]", y, x, color, msg);
2262 #endif
2263 
2264     if (unix_tc) {
2265         printf("%s", msg);
2266         fflush(stdout);
2267     }
2268 
2269     if (y >= LINES || x >= COLS)
2270         return;
2271 
2272     if (unix_tdb)
2273         y += LINES * cw;
2274 
2275     s = &screen[y][x];
2276     c = &colors[y][x];
2277 
2278     /*
2279      * Update our own version of the screen.
2280      */
2281     for (col = x; *m && col < COLS; col++) {
2282         *s++ = *m++;
2283         *c++ = color;
2284     }
2285 
2286 #ifdef  FAST_OSSDSP
2287     /*
2288      * XXX
2289      *
2290      * We redraw the whole line if it's the status line.
2291      * This is pretty bogus.
2292      */
2293      t_loc(y, x, 0);
2294      t_color(color);
2295      t_outs(msg);
2296      t_update(cursorY, cursorX + strlen(msg));
2297 #else
2298     t_redraw(y, y);
2299 #endif
2300 }
2301 
2302 
2303 /*
2304  * Stuff for the debugger
2305  */
2306 void
ossgmx(int * maxline,int * maxcol)2307 ossgmx(int *maxline, int *maxcol)
2308 {
2309     *maxline = LINES - 1;
2310     *maxcol = COLS - 1;
2311 }
2312 
2313 /* clear a window */
osdbgclr(oswdef * win)2314 void osdbgclr(oswdef *win)
2315 {
2316     ossclr(win->oswy1, win->oswx1, win->oswy2, win->oswx2, win->oswcolor);
2317 }
2318 
osdbgini(int rows,int cols)2319 int osdbgini(int rows, int cols)
2320 {
2321     return(0);
2322 }
2323 
ossvpg(char pg)2324 int ossvpg(char pg)
2325 {
2326     static int  s_inputX = 0, s_inputY = 0;
2327     int     ret;
2328 
2329     if (cw == pg)
2330         return cw;
2331 
2332     inputX = s_inputX;
2333     inputY = s_inputY;
2334 
2335     ret = cw;
2336     cw = pg;
2337     t_update(-1, -1);
2338     COLOR = -1;
2339 /*    t_refresh(); */
2340 
2341     return ret;
2342 }
2343 
ossmon(void)2344 int ossmon(void)
2345 {
2346     return 0;
2347 }
2348 
2349 /* scroll a window up a line */
osdbgsc(oswdef * win)2350 static void osdbgsc(oswdef *win)
2351 {
2352     ossscr(win->oswy1, win->oswx1, win->oswy2, win->oswx2, win->oswcolor);
2353 }
2354 
2355 # ifdef USE_STDARG
osdbgpt(oswdef * win,const char * fmt,...)2356 void osdbgpt(oswdef *win, const char *fmt, ...)
2357 # else /* USE_STDARG */
2358 void osdbgpt( win, fmt, a1, a2, a3, a4, a5, a6, a7, a8 )
2359 oswdef *win;
2360 char *fmt;
2361 long  a1, a2, a3, a4, a5, a6, a7, a8;
2362 # endif /* USE_STDARG */
2363 {
2364     char buf[256];
2365     char *p;
2366 
2367 # ifdef USE_STDARG
2368     va_list argptr;
2369 
2370     va_start( argptr, fmt );
2371     vsprintf( buf, fmt, argptr );
2372     va_end( argptr );
2373 # else /* USE_STDARG */
2374     sprintf( buf, fmt, a1, a2, a3, a4, a5, a6, a7, a8 );
2375 # endif /* USE_STDARG */
2376 
2377     for (p=buf ; *p ; )
2378     {
2379     char *p1;
2380 
2381     if ((win->oswflg & OSWFMORE) && win->oswx == win->oswx1 &&
2382         win->oswmore+1 >= win->oswy2 - win->oswy1)
2383     {
2384         char c;
2385         int eof;
2386 
2387         ossdsp(win->oswy, win->oswx, win->oswcolor, "[More]");
2388         ossdbgloc(win->oswy, win->oswx+6);
2389         eof = FALSE;
2390         do
2391         {
2392         switch(c = os_getc())
2393         {
2394         case '\n':
2395         case '\r':
2396             win->oswmore--;
2397             break;
2398 
2399         case ' ':
2400             win->oswmore = 0;
2401             break;
2402 
2403         case 0:
2404             if (os_getc() == CMD_EOF)
2405             {
2406             eof = TRUE;
2407             win->oswmore = 0;
2408             }
2409             break;
2410         }
2411         } while (c != ' ' && c != '\n' && c != '\r' && !eof);
2412 
2413         ossdsp(win->oswy, win->oswx, win->oswcolor, "      ");
2414     }
2415 
2416     for (p1 = p ; *p1 && *p1 != '\n' && *p1 != '\r' && *p1 != '\t'; p1++);
2417     if (*p1 == '\n' || *p1 == '\r' || *p1 == '\t')
2418     {
2419         int c = *p1;
2420 
2421         *p1 = '\0';
2422 
2423         if (win->oswx + strlen(p) > win->oswx2 &&
2424         (win->oswflg & OSWFCLIP))
2425         p[win->oswx2 - win->oswx + 1] = '\0';
2426         ossdsp(win->oswy, win->oswx, win->oswcolor, p);
2427 
2428         if (c == '\n')
2429         {
2430         ++(win->oswy);
2431         win->oswx = win->oswx1;
2432         if (win->oswy > win->oswy2)
2433         {
2434             win->oswy = win->oswy2;
2435             osdbgsc(win);
2436         }
2437         win->oswmore++;
2438         }
2439         else if (c == '\t')
2440         {
2441         win->oswx += strlen(p);
2442         do
2443         {
2444             ossdsp(win->oswy, win->oswx, win->oswcolor, " ");
2445             ++(win->oswx);
2446             if (win->oswx > win->oswx2 && (win->oswflg & OSWFCLIP))
2447             break;
2448         } while ((win->oswx - 2) & 7);
2449         }
2450         p = p1 + 1;
2451         if (win->oswx > win->oswx2) return;
2452     }
2453     else
2454     {
2455         if (win->oswx + strlen(p) > win->oswx2
2456         && (win->oswflg & OSWFCLIP))
2457         p[win->oswx2 - win->oswx + 1] = '\0';
2458         ossdsp(win->oswy, win->oswx, win->oswcolor, p);
2459         win->oswx += strlen(p);
2460         p = p1;
2461     }
2462     }
2463 }
2464 
2465 /* open a window - set up location */
osdbgwop(oswdef * win,int x1,int y1,int x2,int y2,int color)2466 void osdbgwop(oswdef *win, int x1, int y1, int x2, int y2, int color)
2467 {
2468     win->oswx1 = win->oswx = x1;
2469     win->oswx2 = x2;
2470     win->oswy1 = win->oswy = y1;
2471     win->oswy2 = y2;
2472     win->oswcolor = color;
2473     win->oswflg = 0;
2474 }
2475 
ossdbgloc(int y,int x)2476 void ossdbgloc(int y, int x)
2477 {
2478     ossloc(y, x);
2479 }
2480 
2481 /* get some text */
osdbggts(oswdef * win,char * buf,int (* cmdfn)(),void * cmdctx)2482 int osdbggts( oswdef *win,
2483           char *buf,
2484           int (*cmdfn)(/*_ void *ctx, char cmd _*/),
2485           void *cmdctx)
2486 {
2487     char *p = buf;
2488     char *eol = buf;
2489     char *eob = buf + 127;
2490     int   x = win->oswx;
2491     int   y = win->oswy;
2492     int   origx = x;
2493     int   cmd = 0;
2494 
2495     win->oswmore = 0;
2496     for (buf[0] = '\0' ; ; )
2497     {
2498     char c;
2499 
2500     ossdbgloc(y, x);
2501     switch(c = os_getc())
2502     {
2503         case 8:
2504         if (p > buf)
2505         {
2506             char *q;
2507             char  tmpbuf[2];
2508             int   thisx, thisy;
2509 
2510             for ( q=(--p) ; q<eol ; q++ ) *q = *( q+1 );
2511             eol--;
2512             if ( --x < 0 )
2513             {
2514             x = win->oswx2;
2515             y--;
2516             }
2517             *eol = ' ';
2518             thisx = x;
2519             thisy = y;
2520             for ( q=p, tmpbuf[1]='\0' ; q<=eol ; q++ )
2521             {
2522             tmpbuf[0] = *q;
2523             ossdsp( thisy, thisx, win->oswcolor, tmpbuf );
2524             if ( ++thisx > win->oswx2 )
2525             {
2526                 thisx = 0;
2527                 thisy++;
2528             }
2529             }
2530             *eol = '\0';
2531         }
2532         break;
2533         case 13:
2534         /*
2535          *   Scroll the screen to account for the carriage return,
2536          *   position the cursor at the end of the new line, and
2537          *   null-terminate the line.
2538          */
2539         *eol = '\0';
2540         while( p != eol )
2541         {
2542             p++;
2543             if ( ++x > win->oswx2 )
2544             {
2545             y++;
2546             x = 0;
2547             }
2548         }
2549 
2550         if ( y == win->oswy2 ) osdbgsc(win);
2551         else ++y;
2552         x = 0;
2553         ossdbgloc( y, x );
2554 
2555         /*
2556          *   Finally, copy the buffer to the screen save buffer
2557          *   (if applicable), and return the contents of the buffer.
2558          */
2559         win->oswx = x;
2560         win->oswy = y;
2561         return(0);
2562 
2563         case 0:
2564         switch(c = os_getc())
2565         {
2566             case CMD_EOF:
2567             return 0;
2568 
2569             case CMD_LEFT:
2570             if ( p>buf )
2571             {
2572                 p--;
2573                 x--;
2574                 if ( x < 0 )
2575                 {
2576                 x = win->oswx2;
2577                 y--;
2578                 }
2579             }
2580             break;
2581             case CMD_RIGHT:
2582             if ( p<eol )
2583             {
2584                 p++;
2585                 x++;
2586                 if ( x > win->oswx2 )
2587                 {
2588                 x = 0;
2589                 y++;
2590                 }
2591             }
2592             break;
2593             case CMD_DEL:
2594             if ( p<eol )
2595             {
2596                 char *q;
2597                 char  tmpbuf[2];
2598                 int   thisx=x, thisy=y;
2599 
2600                 for ( q=p ; q<eol ; q++ ) *q = *(q+1);
2601                 eol--;
2602                 *eol = ' ';
2603                 for ( q=p, tmpbuf[1]='\0' ; q<=eol ; q++ )
2604                 {
2605                 tmpbuf[0] = *q;
2606                 ossdsp( thisy, thisx, win->oswcolor, tmpbuf );
2607                 if ( ++thisx > win->oswx2 )
2608                 {
2609                     thisx = 0;
2610                     thisy++;
2611                 }
2612                 }
2613                 *eol = '\0';
2614             }
2615             break;
2616             case CMD_KILL:
2617             case CMD_HOME:
2618             do_kill:
2619             while( p>buf )
2620             {
2621                 p--;
2622                 if ( --x < 0 )
2623                 {
2624                 x = win->oswx2;
2625                 y--;
2626                 }
2627             }
2628             if ( c == CMD_HOME ) break;
2629             /*
2630              *   We're at the start of the line now; fall
2631              *   through for KILL, UP, and DOWN to the code
2632              *   which deletes to the end of the line.
2633              */
2634             case CMD_DEOL:
2635             if ( p<eol )
2636             {
2637                 char *q;
2638                 int   thisx=x, thisy=y;
2639 
2640                 for ( q=p ; q<eol ; q++ )
2641                 {
2642                 ossdsp( thisy, thisx, win->oswcolor, " " );
2643                 if ( ++thisx > win->oswx2 )
2644                 {
2645                     thisx = 0;
2646                     thisy++;
2647                 }
2648                 }
2649                 eol = p;
2650                 *p = '\0';
2651             }
2652             if (cmd) return(cmd);
2653             break;
2654             case CMD_END:
2655             while ( p<eol )
2656             {
2657                 p++;
2658                 if ( ++x > win->oswx2 )
2659                 {
2660                 x = 0;
2661                 y++;
2662                 }
2663             }
2664             break;
2665 
2666             default:
2667             if (cmd = (*cmdfn)(cmdctx, c))
2668             {
2669                 c = CMD_KILL;
2670                 goto do_kill;
2671             }
2672             break;
2673         }
2674         break;
2675         default:
2676         if ( c >= ' ' && c < 127 && eol<eob )
2677         {
2678             if ( p != eol )
2679             {
2680             char *q;
2681             int   thisy=y, thisx=x;
2682             char  tmpbuf[2];
2683 
2684             for ( q=(++eol) ; q>p ; q-- ) *q=*(q-1);
2685             *p = c;
2686             for ( q=p++, tmpbuf[1] = '\0' ; q<eol ; q++ )
2687             {
2688                 tmpbuf[0] = *q;
2689                 ossdsp( thisy, thisx, win->oswcolor, tmpbuf );
2690                 thisx++;
2691                 if ( thisx > win->oswx2 )
2692                 {
2693                 thisx = 0;
2694                 if ( thisy == win->oswy2 )
2695                 {
2696                     y--;
2697                     osdbgsc(win);
2698                 }
2699                 else thisy++;
2700                 }
2701             }
2702             if ( ++x > win->oswx2 )
2703             {
2704                 y++;
2705                 x = 0;
2706             }
2707             }
2708             else
2709             {
2710             *p++ = c;
2711             *p = '\0';
2712             eol++;
2713             ossdsp( y, x, win->oswcolor, p-1 );
2714             if ( ++x > win->oswx2 )
2715             {
2716                 x = 0;
2717                 if ( y == win->oswy2 )
2718                 osdbgsc(win);
2719                 else y++;
2720             }
2721             }
2722         }
2723         break;
2724     }
2725     }
2726 }
2727 
2728 
2729 /*
2730  * End of stuff for debugger
2731  */
2732 
2733 #else   /* USE_STDIO */
2734 
2735 int
os_init(int * argc,char * argv[],const char * prompt,char * buf,int bufsiz)2736 os_init( int *argc, char *argv[], const char *prompt, char *buf, int bufsiz )
2737 {
2738     return 0;
2739 }
2740 
2741 void
os_uninit()2742 os_uninit()
2743 {
2744 }
2745 
2746 void
os_term(int rc)2747 os_term(int rc)
2748 {
2749     exit(rc);
2750 }
2751 
2752 int
os_break(void)2753 os_break(void)
2754 {
2755     int ret;
2756 
2757     ret = break_set;
2758     break_set = 0;
2759     return ret;
2760 }
2761 
2762 void
os_waitc(void)2763 os_waitc(void)
2764 {
2765     getchar();     /* Changed from getkey() by SRG */
2766 }
2767 
2768 int
os_getc(void)2769 os_getc(void)
2770 {
2771     return getchar();  /* Changed from getkey() by SRG */
2772 }
2773 
2774 int
os_getc_raw(void)2775 os_getc_raw(void)
2776 {
2777     return os_getc();
2778 }
2779 
2780 #endif  /* USE_STDIO */
2781 
2782 int
os_paramfile(char * buf)2783 os_paramfile(char *buf)
2784 {
2785     return 0;
2786 }
2787 
2788 /*
2789  *   os_exeseek  - opens the given .EXE file and seeks to the end of the
2790  *   executable part of it, on the presumption that a datafile is to be
2791  *   found there.
2792  */
os_exeseek(const char * exefile,const char * typ)2793 osfildef *os_exeseek(const char *exefile, const char *typ )
2794 {
2795     return((osfildef *)0);
2796 }
2797 
2798 /*
2799  *   os_exfld  - load in an external function from an open file, given
2800  *   the size of the function (in bytes).  Returns a pointer to the newly
2801  *   allocated memory block containing the function in memory.
2802  */
os_exfld(osfildef * fp,unsigned len)2803 int (*os_exfld( osfildef *fp, unsigned len ))(void *)
2804 {
2805     return  (int (*)(void *)) 0;
2806 }
2807 
2808 /*
2809  *   Load an external function from a file.  This routine assumes that
2810  *   the file has the same name as the resource.
2811  */
os_exfil(const char * name)2812 int (*os_exfil(const char *name ))(void *)
2813 {
2814     return (int (*)(void *)) 0;
2815 }
2816 
2817 /*
2818  *   call an external function, passing it an argument (a string pointer),
2819  *   and passing back the string pointer returned by the external function
2820  */
os_excall(int (* extfn)(void *),void * arg)2821 int os_excall(int (*extfn)(void *), void *arg)
2822 {
2823     return 0;
2824 }
2825 
2826 /*
2827  *   Get the temporary file path.  This should fill in the buffer with a
2828  *   path prefix (suitable for strcat'ing a filename onto) for a good
2829  *   directory for a temporary file, such as the swap file.
2830  */
2831 void
os_get_tmp_path(char * s)2832 os_get_tmp_path(char *s)
2833 {
2834     strcpy(s, "/tmp");
2835 }
2836 
2837 /* os_defext(fn, ext) should append the default extension ext to the filename
2838  *  in fn.  It is assumed that the buffer at fn is big enough to hold the added
2839  *  characters in the extension.  The result should be null-terminated.  When
2840  *  an extension is already present in the filename at fn, no action should be
2841  *  taken.  On systems without an analogue of extensions, this routine should
2842  *  do nothing.
2843  *
2844  * For Unix, we extend this to also prepend the default saved game or game
2845  * file path name.
2846  *
2847  *    - The TADSSAVE environment variable holds the name of the save file
2848  *  directory
2849  *    - The TADSGAME envirnoment variable holds the name of the game file
2850  *  directory
2851  *
2852  * We only prepend  if there are no slashes in the filename already.
2853  * We don't prepand paths when running as the compiler, because we don't want
2854  * the output files to go in weird places.
2855  */
os_defext(char * fn,const char * ext)2856 void os_defext( char *fn, const char *ext )
2857 {
2858     char *p, *n, tmp[1024];
2859     char *defpath;
2860 
2861     /*
2862      * Prepend default path
2863      */
2864    if (!memicmp(ext, "sav", strlen(ext)))
2865     defpath = getenv("TADSSAVE");
2866    else if (!memicmp(ext, "gam", strlen(ext)))
2867     defpath = getenv("TADSGAME");
2868    else
2869     defpath = NULL;
2870 
2871    if (!unix_tc && defpath) {
2872     /*
2873      * Look for slashes.  If there are any, don't mess with name.
2874      */
2875     n = fn;
2876     while (*n) {
2877         if (*n == '/')
2878             break;
2879         n++;
2880     }
2881     if (!*n) {
2882         strcpy(tmp, defpath);
2883         if (defpath[strlen(defpath)] != '/')
2884             strcat(tmp, "/");
2885         strcat(tmp, fn);
2886         strcpy(fn, tmp);
2887     }
2888    }
2889 
2890     p = fn+strlen(fn);
2891     while ( p>fn )
2892       {
2893     p--;
2894     if ( *p=='.' ) return;      /* already has an extension */
2895     if ( *p=='/' || *p=='\\' || *p==':'
2896        ) break;    /* found a path */
2897       }
2898     strcat( fn, "." );          /* add a dot */
2899     strcat( fn, ext );          /* add the extension */
2900   }
2901 
2902 /* os_remext(fn) removes the extension from fn, if present.  The buffer at
2903  *  fn should be modified in place.  If no extension is present, no action
2904  *  should be taken.  For systems without an analogue of extensions, this
2905  *  routine should do nothing.
2906  */
os_remext(char * fn)2907 void os_remext( char *fn )
2908 {
2909     char *p = fn+strlen(fn);
2910     while ( p>fn )
2911       {
2912     p--;
2913     if ( *p=='.' )
2914       {
2915         *p = '\0';
2916         return;
2917       }
2918     if ( *p=='/' || *p=='\\' || *p==':'
2919         ) return;
2920       }
2921 }
2922 
2923 /*
2924  * Add an extension, even if the filename currently has one
2925  */
os_addext(char * fn,const char * ext)2926 void os_addext(char *fn, const char *ext)
2927 {
2928     strcat(fn, ".");
2929     strcat(fn, ext);
2930 }
2931 
2932 /*
2933  * Returns a pointer to the root portion of the filename. Added by SRG.
2934  */
os_get_root_name(char * buf)2935 char *os_get_root_name(char *buf)
2936 {
2937     char *p = buf;
2938 
2939     p += strlen(buf) - 1;
2940     while (*p != '/' && p > buf)
2941     p--;
2942     if (p != buf) p++;
2943 
2944     return p;
2945 }
2946 
2947 
2948 /*
2949  * This is an extremely unsophisticated version of os_xlat_html4, which translates
2950  * everything it can into its near-ASCII equivalent. Added by SRG, after code from
2951  * OSDOS.C.
2952  */
os_xlat_html4(unsigned int html4_char,char * result,size_t result_len)2953 void os_xlat_html4(unsigned int html4_char, char *result, size_t result_len)
2954 {
2955     /* default character to use for unknown charaters */
2956 #define INV_CHAR " "
2957 
2958     /*
2959      *   Translation table - provides mappings for characters the ISO
2960      *   Latin-1 subset of the HTML 4 character map (values 128-255).
2961      *
2962      *   Characters marked "(approx)" are approximations where the actual
2963      *   desired character is not available, but a reasonable approximation
2964      *   is used.  Characters marked "(approx unaccented)" are accented
2965      *   characters that are not available; these use the unaccented equivalent
2966      *   as an approximation, since this will presumably convey more meaning
2967      *   than a blank.
2968      *
2969      *   Characters marked "(n/a)" have no equivalent (even approximating),
2970      *   and are mapped to spaces.
2971      *
2972      *   Characters marked "(not used)" are not used by HTML '&' markups.
2973      */
2974     static const char *xlat_tbl[] =
2975     {
2976     INV_CHAR,                     /* 128 (not used) */
2977     INV_CHAR,                     /* 129 (not used) */
2978     "'",                        /* 130 - sbquo (approx) */
2979     INV_CHAR,                     /* 131 (not used) */
2980     "\"",                       /* 132 - bdquo (approx) */
2981     INV_CHAR,                     /* 133 (not used) */
2982     INV_CHAR,                     /* 134 - dagger (n/a) */
2983     INV_CHAR,                     /* 135 - Dagger (n/a) */
2984     INV_CHAR,                     /* 136 (not used) */
2985     INV_CHAR,                     /* 137 - permil (n/a) */
2986     INV_CHAR,                     /* 138 (not used) */
2987     "<",                       /* 139 - lsaquo (approx) */
2988     INV_CHAR,                      /* 140 - OElig (n/a) */
2989     INV_CHAR,                     /* 141 (not used) */
2990     INV_CHAR,                     /* 142 (not used) */
2991     INV_CHAR,                     /* 143 (not used) */
2992     INV_CHAR,                     /* 144 (not used) */
2993     "'",                        /* 145 - lsquo (approx) */
2994     "'",                        /* 146 - rsquo (approx) */
2995     "\"",                       /* 147 - ldquo (approx) */
2996     "\"",                       /* 148 - rdquo (approx) */
2997     INV_CHAR,                     /* 149 (not used) */
2998     "-",                            /* 150 - endash */
2999     "--",                           /* 151 - emdash */
3000     INV_CHAR,                     /* 152 (not used) */
3001     "(tm)",                     /* 153 - trade (approx) */
3002     INV_CHAR,                     /* 154 (not used) */
3003     ">",                       /* 155 - rsaquo (approx) */
3004     INV_CHAR,                      /* 156 - oelig (n/a) */
3005     INV_CHAR,                     /* 157 (not used) */
3006     INV_CHAR,                     /* 158 (not used) */
3007     "Y",                  /* 159 - Yuml (approx unaccented) */
3008     INV_CHAR,                     /* 160 (not used) */
3009     "!",                             /* 161 - iexcl */
3010     INV_CHAR,                   /* 162 - cent (n/a) */
3011     INV_CHAR,                      /* 163 - pound (n/a) */
3012     INV_CHAR,                     /* 164 - curren (n/a) */
3013     INV_CHAR,                    /* 165 - yen (n/a) */
3014     "|",                          /* 166 - brvbar (n/a) */
3015     INV_CHAR,                   /* 167 - sect (n/a) */
3016     INV_CHAR,                    /* 168 - uml (n/a) */
3017     "(c)",                       /* 169 - copy (approx) */
3018     INV_CHAR,                   /* 170 - ordf (n/a) */
3019     INV_CHAR,                      /* 171 - laquo (n/a) */
3020     INV_CHAR,                    /* 172 - not (n/a) */
3021     " ",                         /* 173 - shy (n/a) */
3022     "(R)",                        /* 174 - reg (approx) */
3023     INV_CHAR,                   /* 175 - macr (n/a) */
3024     INV_CHAR,                    /* 176 - deg (n/a) */
3025     INV_CHAR,                     /* 177 - plusmn (n/a) */
3026     INV_CHAR,                   /* 178 - sup2 (n/a) */
3027     INV_CHAR,                   /* 179 - sup3 (n/a) */
3028     "'",                        /* 180 - acute (approx) */
3029     INV_CHAR,                      /* 181 - micro (n/a) */
3030     INV_CHAR,                   /* 182 - para (n/a) */
3031     INV_CHAR,                     /* 183 - middot (n/a) */
3032     ",",                        /* 184 - cedil (approx) */
3033     INV_CHAR,                   /* 185 - sup1 (n/a) */
3034     INV_CHAR,                   /* 186 - ordm (n/a) */
3035     INV_CHAR,                      /* 187 - raquo (n/a) */
3036     "1/4",                     /* 188 - frac14 (approx) */
3037     "1/2",                     /* 189 - frac12 (approx) */
3038     "3/4",                     /* 190 - frac34 (approx) */
3039     "?",                       /* 191 - iquest (approx) */
3040     "A",                /* 192 - Agrave (approx unaccented) */
3041     "A",                /* 193 - Aacute (approx unaccented) */
3042     "A",                 /* 194 - Acirc (approx unaccented) */
3043     "A",                /* 195 - Atilde (approx unaccented) */
3044     "A",                  /* 196 - Auml (approx unaccented) */
3045     "A",                 /* 197 - Aring (approx unaccented) */
3046     "AE",                /* 198 - AElig (approx unaccented) */
3047     "C",                /* 199 - Ccedil (approx unaccented) */
3048     "E",                /* 200 - Egrave (approx unaccented) */
3049     "E",                /* 201 - Eacute (approx unaccented) */
3050     "E",                 /* 202 - Ecirc (approx unaccented) */
3051     "E",                  /* 203 - Euml (approx unaccented) */
3052     "I",                /* 204 - Igrave (approx unaccented) */
3053     "I",                /* 205 - Iacute (approx unaccented) */
3054     "I",                 /* 206 - Icirc (approx unaccented) */
3055     "I",                  /* 207 - Iuml (approx unaccented) */
3056     INV_CHAR,                    /* 208 - ETH (n/a) */
3057     "N",                /* 209 - Ntilde (approx unaccented) */
3058     "O",                /* 210 - Ograve (approx unaccented) */
3059     "O",                /* 211 - Oacute (approx unaccented) */
3060     "O",                 /* 212 - Ocirc (approx unaccented) */
3061     "O",                /* 213 - Otilde (approx unaccented) */
3062     "O",                  /* 214 - Ouml (approx unaccented) */
3063     "x",                        /* 215 - times (approx) */
3064     "O",                /* 216 - Oslash (approx unaccented) */
3065     "U",                /* 217 - Ugrave (approx unaccented) */
3066     "U",                /* 218 - Uacute (approx unaccented) */
3067     "U",                 /* 219 - Ucirc (approx unaccented) */
3068     "U",                  /* 220 - Uuml (approx unaccented) */
3069     "Y",                /* 221 - Yacute (approx unaccented) */
3070     INV_CHAR,                      /* 222 - THORN (n/a) */
3071     INV_CHAR,                      /* 223 - szlig (n/a) */
3072     "a",                /* 224 - agrave (approx unaccented) */
3073     "a",                /* 225 - aacute (approx unaccented) */
3074     "a",                 /* 226 - acirc (approx unaccented) */
3075     "a",                /* 227 - atilde (approx unaccented) */
3076     "a",                  /* 228 - auml (approx unaccented) */
3077     "a",                 /* 229 - aring (approx unaccented) */
3078     "ae",                       /* 230 - aelig (approx) */
3079     "c",                       /* 231 - ccedil (approx) */
3080     "e",                /* 232 - egrave (approx unaccented) */
3081     "e",                /* 233 - eacute (approx unaccented) */
3082     "e",                 /* 234 - ecirc (approx unaccented) */
3083     "e",                  /* 235 - euml (approx unaccented) */
3084     "i",                /* 236 - igrave (approx unaccented) */
3085     "i",                /* 237 - iacute (approx unaccented) */
3086     "i",                 /* 238 - icirc (approx unaccented) */
3087     "i",                  /* 239 - iuml (approx unaccented) */
3088     INV_CHAR,                    /* 240 - eth (n/a) */
3089     "n",                /* 241 - ntilde (approx unaccented) */
3090     "o",                /* 242 - ograve (approx unaccented) */
3091     "o",                /* 243 - oacute (approx unaccented) */
3092     "o",                 /* 244 - ocirc (approx unaccented) */
3093     "o",                /* 245 - otilde (approx unaccented) */
3094     "o",                  /* 246 - ouml (approx unaccented) */
3095     "/"                    /* 247 - divide (approx) */
3096     "o",                /* 248 - oslash (approx unaccented) */
3097     "u",                /* 249 - ugrave (approx unaccented) */
3098     "u",                /* 250 - uacute (approx unaccented) */
3099     "u",                 /* 251 - ucirc (approx unaccented) */
3100     "u",                  /* 252 - uuml (approx unaccented) */
3101     "y",                /* 253 - yacute (approx unaccented) */
3102     INV_CHAR,                      /* 254 - thorn (n/a) */
3103     "y"               /* 255 - yuml (approx unaccented) */
3104     };
3105 
3106     /*
3107      *   Map certain extended characters into our 128-255 range.  If we
3108      *   don't recognize the character, return the default invalid
3109      *   charater value.
3110      */
3111     if (html4_char > 255)
3112     {
3113     switch(html4_char)
3114     {
3115     case 338: html4_char = 140; break;
3116     case 339: html4_char = 156; break;
3117     case 376: html4_char = 159; break;
3118     case 352: html4_char = 154; break;
3119     case 353: html4_char = 138; break;
3120     case 8211: html4_char = 150; break;
3121     case 8212: html4_char = 151; break;
3122     case 8216: html4_char = 145; break;
3123     case 8217: html4_char = 146; break;
3124     case 8218: html4_char = 130; break;
3125     case 8220: html4_char = 147; break;
3126     case 8221: html4_char = 148; break;
3127     case 8222: html4_char = 132; break;
3128     case 8224: html4_char = 134; break;
3129     case 8225: html4_char = 135; break;
3130     case 8240: html4_char = 137; break;
3131     case 8249: html4_char = 139; break;
3132     case 8250: html4_char = 155; break;
3133     case 8482: html4_char = 153; break;
3134 
3135     default:
3136         /* unmappable character - return space */
3137         result[0] = (unsigned char)' ';
3138         result[1] = 0;
3139         return;
3140     }
3141     }
3142 
3143     /*
3144      *   if the character is in the regular ASCII zone, return it
3145      *   untranslated
3146      */
3147     if (html4_char < 128)
3148     {
3149     result[0] = (unsigned char)html4_char;
3150     result[1] = '\0';
3151     return;
3152     }
3153 
3154     /* look up the character in our table and return the translation */
3155     strcpy(result, xlat_tbl[html4_char - 128]);
3156 }
3157 
3158 /*
3159  * Simple versions of os_advise_load_charmap and os_gen_charmap_filename. At
3160  * some point I'll get around to making these do something real. Added by SRG.
3161  */
os_advise_load_charmap(char * id,char * ldesc,char * sysinfo)3162 void os_advise_load_charmap(char *id, char *ldesc, char *sysinfo)
3163 {
3164 }
3165 
os_gen_charmap_filename(char * filename,char * internal_id,char * argv0)3166 void os_gen_charmap_filename(char *filename, char *internal_id, char *argv0)
3167 {
3168     filename[0] = '\0';
3169 }
3170 
3171 /*
3172  *   A very simple "millisecond timer." Because of the relatively-low
3173  *   precision of a long and because we really are only precise to a
3174  *   second, we store a zero-offset to begin with. Added by SRG.
3175  */
os_get_sys_clock_ms(void)3176 long os_get_sys_clock_ms(void)
3177 {
3178     if (timeZero == 0)
3179     timeZero = time(0);
3180     return ((time(0) - timeZero) * 1000);
3181 }
3182 
3183 /*
3184  *   Sleep for a given # of ms. Added by SRG.
3185  */
os_sleep_ms(long delay_in_milliseconds)3186 void os_sleep_ms(long delay_in_milliseconds)
3187 {
3188     usleep(delay_in_milliseconds);
3189 }
3190 
3191 /*
3192  * An empty implementation of os_set_title. Added by SRG.
3193  */
os_set_title(const char * title)3194 void os_set_title(const char *title)
3195 {
3196 }
3197 
3198 /*
3199  * Provide memicmp since it's not a standard libc routine.
3200  */
memicmp(char * s1,char * s2,int len)3201 memicmp(char *s1, char *s2, int len)
3202 {
3203     char    *x1, *x2;
3204     int result;
3205     int i;
3206 
3207     x1 = malloc(len);
3208     x2 = malloc(len);
3209 
3210     if (!x1 || !x2) {
3211         printf("Out of memory!\n");
3212         exit(-1);
3213     }
3214 
3215     for (i = 0; i < len; i++) {
3216         if (isupper(s1[i]))
3217             x1[i] = tolower(s1[i]);
3218         else
3219             x1[i] = s1[i];
3220 
3221         if (isupper(s2[i]))
3222             x2[i] = tolower(s2[i]);
3223         else
3224             x2[i] = s2[i];
3225     }
3226 
3227     result = memcmp(x1, x2, len);
3228     free(x1);
3229     free(x2);
3230     return result;
3231 }
3232 
3233 /*
3234  * memcpy - copy bytes (handles overlap, so we can equivalence memmove() to it.
3235  */
3236 void *
our_memcpy(void * dst,const void * src,size_t size)3237 our_memcpy(void *dst, const void *src, size_t size)
3238 {
3239     register char *d;
3240     register const char *s;
3241     register size_t n;
3242 
3243     if (size == 0)
3244         return(dst);
3245 
3246     s = src;
3247     d = dst;
3248     if (s <= d && s + (size-1) >= d) {
3249         /* Overlap, must copy right-to-left. */
3250         s += size-1;
3251         d += size-1;
3252         for (n = size; n > 0; n--)
3253             *d-- = *s--;
3254     } else
3255         for (n = size; n > 0; n--)
3256             *d++ = *s++;
3257 
3258     return(dst);
3259 }
3260 
3261 #if !defined(DJGCC_386)
3262 /*
3263  * dbgu.c requires os_strlwr
3264  */
3265 char *
os_strlwr(char * s)3266 os_strlwr(char *s)
3267 {
3268     char *start;
3269     start = s;
3270     while (*s) {
3271         if (isupper(*s))
3272             *s = tolower(*s);
3273 
3274         s++;
3275     }
3276     return start;
3277 }
3278 #endif
3279 
3280 #ifndef DJGCC_386
3281 
3282 /*
3283  * Open file using fopen, stripping of b's from flag string.
3284  */
3285 #ifdef fopen
3286 #undef fopen
3287 #endif
3288 
3289 FILE *
our_fopen(char * filename,char * flags)3290 our_fopen(char *filename, char *flags)
3291 {
3292     static char f[80];
3293     int     i = 0;
3294 
3295     while (*flags) {
3296         if (*flags != 'b')
3297             f[i++] = *flags;
3298 
3299         flags++;
3300     }
3301     f[i] = 0;
3302 
3303     return fopen(filename, f);
3304 }
3305 #endif  /* DJGCC_386 */
3306 
3307 /* ------------------------------------------------------------------------ */
3308 /*
3309  *   Get file times
3310  */
3311 
3312 /*
3313  *   get file creation time
3314  */
os_get_file_cre_time(os_file_time_t * t,const char * fname)3315 int os_get_file_cre_time(os_file_time_t *t, const char *fname)
3316 {
3317     struct stat info;
3318 
3319     /* get the file information */
3320     if (stat(fname, &info))
3321         return 1;
3322 
3323     /* set the creation time in the return structure */
3324     t->t = info.st_ctime;
3325     return 0;
3326 }
3327 
3328 /*
3329  *   get file modification time
3330  */
os_get_file_mod_time(os_file_time_t * t,const char * fname)3331 int os_get_file_mod_time(os_file_time_t *t, const char *fname)
3332 {
3333     struct stat info;
3334 
3335     /* get the file information */
3336     if (stat(fname, &info))
3337         return 1;
3338 
3339     /* set the modification time in the return structure */
3340     t->t = info.st_mtime;
3341     return 0;
3342 }
3343 
3344 /*
3345  *   get file last access time
3346  */
os_get_file_acc_time(os_file_time_t * t,const char * fname)3347 int os_get_file_acc_time(os_file_time_t *t, const char *fname)
3348 {
3349     struct stat info;
3350 
3351     /* get the file information */
3352     if (stat(fname, &info))
3353         return 1;
3354 
3355     /* set the access time in the return structure */
3356     t->t = info.st_atime;
3357     return 0;
3358 }
3359 
3360 /*
3361  *   compare two file time structures
3362  */
os_cmp_file_times(const os_file_time_t * a,const os_file_time_t * b)3363 int os_cmp_file_times(const os_file_time_t *a, const os_file_time_t *b)
3364 {
3365     if (a->t < b->t)
3366         return -1;
3367     else if (a->t == b->t)
3368         return 0;
3369     else
3370         return 1;
3371 }
3372 
3373 /*
3374  *   open a file for reading and writing in text mode; do not truncate
3375  */
osfoprwt(const char * fname,os_filetype_t typ)3376 osfildef *osfoprwt(const char *fname, os_filetype_t typ)
3377 {
3378     osfildef *fp;
3379 
3380     /* try opening an existing file in read/write mode */
3381     fp = fopen(fname, "r+");
3382 
3383     /* if that failed, create a new file in read/write mode */
3384     if (fp == 0)
3385         fp = fopen(fname, "w+");
3386 
3387     /* return the file */
3388     return fp;
3389 }
3390 
3391 /*
3392  *   open a file for reading and writing in binary mode; do not truncate
3393  */
osfoprwb(const char * fname,os_filetype_t typ)3394 osfildef *osfoprwb(const char *fname, os_filetype_t typ)
3395 {
3396     osfildef *fp;
3397 
3398     /* try opening an existing file in read/write mode */
3399     fp = fopen(fname, "r+b");
3400 
3401     /* if that failed, create a new file in read/write mode */
3402     if (fp == 0)
3403         fp = fopen(fname, "w+b");
3404 
3405     /* return the file */
3406     return fp;
3407 }
3408 
3409 /*
3410  *   Check for a special filename
3411  */
os_is_special_file(const char * fname)3412 enum os_specfile_t os_is_special_file(const char *fname)
3413 {
3414     /* check for '.' */
3415     if (strcmp(fname, ".") == 0)
3416         return OS_SPECFILE_SELF;
3417 
3418     /* check for '..' */
3419     if (strcmp(fname, "..") == 0)
3420         return OS_SPECFILE_PARENT;
3421 
3422     /* not a special file */
3423     return OS_SPECFILE_NONE;
3424 }
3425 
3426 /*
3427  *   Build a full path name given a path and a filename
3428  *
3429  *   Copied over from osnoui.c by Suzanne Skinner (tril@igs.net), 2000/Aug/18
3430  */
os_build_full_path(char * fullpathbuf,size_t fullpathbuflen,const char * path,const char * filename)3431 void os_build_full_path(char *fullpathbuf, size_t fullpathbuflen,
3432                         const char *path, const char *filename)
3433 {
3434     size_t plen, flen;
3435     int add_sep;
3436 
3437     /*
3438      *   Note whether we need to add a separator.  If the path prefix ends
3439      *   in a separator, don't add another; otherwise, add the standard
3440      *   system separator character.
3441      *
3442      *   Do not add a separator if the path is completely empty, since this
3443      *   simply means that we want to use the current directory.
3444      */
3445     plen = strlen(path);
3446     add_sep = (plen != 0
3447                && path[plen-1] != OSPATHCHAR
3448                && strchr(OSPATHALT, path[plen-1]) == 0);
3449 
3450     /* copy the path to the full path buffer, limiting to the buffer length */
3451     if (plen > fullpathbuflen - 1)
3452         plen = fullpathbuflen - 1;
3453     memcpy(fullpathbuf, path, plen);
3454 
3455     /* add the path separator if necessary (and if there's room) */
3456     if (add_sep && plen + 2 < fullpathbuflen)
3457         fullpathbuf[plen++] = OSPATHCHAR;
3458 
3459     /* add the filename after the path, if there's room */
3460     flen = strlen(filename);
3461     if (flen > fullpathbuflen - plen - 1)
3462         flen = fullpathbuflen - plen - 1;
3463     memcpy(fullpathbuf + plen, filename, flen);
3464 
3465     /* add a null terminator */
3466     fullpathbuf[plen + flen] = '\0';
3467 }
3468 
3469 /*
3470  *   Determine if a path is absolute or relative.  If the path starts with a
3471  *   path separator character, we consider it absolute, otherwise we
3472  *   consider it relative.
3473  *
3474  *   Note that, on DOS, an absolute path can also follow a drive letter.
3475  *   So, if the path contains a letter followed by a colon, we'll consider
3476  *   the path to be absolute.
3477  *
3478  *   Copied over from osnoui.c by Suzanne Skinner (tril@igs.net),
3479  *   2000/Aug/18
3480  */
os_is_file_absolute(const char * fname)3481 int os_is_file_absolute(const char *fname)
3482 {
3483     /* if the name starts with a path separator, it's absolute */
3484     if (fname[0] == OSPATHCHAR || strchr(OSPATHALT, fname[0])  != 0)
3485         return TRUE;
3486 
3487 #ifdef MSDOS
3488     /* on DOS, a file is absolute if it starts with a drive letter */
3489     if (isalpha(fname[0]) && fname[1] == ':')
3490         return TRUE;
3491 #endif /* MSDOS */
3492 
3493     /* the path is relative */
3494     return FALSE;
3495 }
3496 
3497 /*
3498  *   Extract the path from a filename
3499  *
3500  *   Copied over from osnoui.c by tril@igs.net, 2000/Aug/18
3501  */
os_get_path_name(char * pathbuf,size_t pathbuflen,const char * fname)3502 void os_get_path_name(char *pathbuf, size_t pathbuflen, const char *fname)
3503 {
3504     const char *lastsep;
3505     const char *p;
3506     size_t len;
3507 
3508     /* find the last separator in the filename */
3509     for (p = fname, lastsep = fname ; *p != '\0' ; ++p)
3510     {
3511         /*
3512          *   if it's a path separator character, remember it as the last one
3513          *   we've found so far
3514          */
3515         if (*p == OSPATHCHAR || strchr(OSPATHALT, *p)  != 0)
3516             lastsep = p;
3517     }
3518 
3519     /* get the length of the prefix, not including the separator */
3520     len = lastsep - fname;
3521 
3522     /* make sure it fits in our buffer (with a null terminator) */
3523     if (len > pathbuflen - 1)
3524         len = pathbuflen - 1;
3525 
3526     /* copy it and null-terminate it */
3527     memcpy(pathbuf, fname, len);
3528     pathbuf[len] = '\0';
3529 }
3530 
3531 /*
3532  *   Get the name of the character mapping file.
3533  *
3534  *   NOTE - we should use some means to find the actual character set that
3535  *   the user's terminal is using and return that; maybe some environment
3536  *   variable or some curses call will tell us what we need to know.  For
3537  *   now, we'll just return "asc7dflt", which is the 7-bit ASCII character
3538  *   mapping; this should be safe for any ASCII terminal, although it won't
3539  *   allow us to display accented characters that the user's terminal might
3540  *   actually be capable of displaying if we just knew the actual character
3541  *   set it was using.
3542  */
os_get_charmap(char * mapname,int charmap_id)3543 void os_get_charmap(char *mapname, int charmap_id)
3544 {
3545     strcpy(mapname, "asc7dflt");
3546 }
3547 
3548 /*
3549  *   Reallocate storage at a different size
3550  */
osrealloc(void * buf,size_t len)3551 void *osrealloc(void *buf, size_t len)
3552 {
3553     return realloc(buf, len);
3554 }
3555 
3556 /* Full ansi color is not yet implemented in Unix, but we do handle bold text
3557  * and status-line attributes.
3558  */
ossgetcolor(int fg,int bg,int attrs,int screen_color)3559 int ossgetcolor(int fg, int bg, int attrs, int screen_color)
3560 {
3561     #ifndef USE_STDIO
3562 
3563     /* use the screen color if the background is transparent */
3564     if (bg == OSGEN_COLOR_TRANSPARENT)
3565         bg = screen_color;
3566 
3567     /* Check for the special statusline color scheme */
3568     if (fg == OSGEN_COLOR_STATUSLINE && bg == OSGEN_COLOR_STATUSBG)
3569         return sdesc_color;
3570     /* check for bold text */
3571     else if ((attrs & OS_ATTR_HILITE) != 0)
3572         return text_bold_color;
3573 
3574     #endif
3575 
3576     return cNORMAL;
3577 }
3578 
3579 extern int os_f_plain;
3580 
oss_get_sysinfo(int code,void * param,long * result)3581 int oss_get_sysinfo(int code, void *param, long *result)
3582 {
3583     switch (code) {
3584 
3585     case SYSINFO_TEXT_COLORS:
3586         *result = SYSINFO_TXC_PARAM;
3587         return TRUE;
3588 
3589     case SYSINFO_TEXT_HILITE:
3590 #ifdef USE_STDIO
3591         *result = 0;
3592 #else
3593         *result = (os_f_plain ? 0 : 1);
3594 #endif
3595         return TRUE;
3596     }
3597 
3598     return FALSE;
3599 }
3600