1 /* ********************************************
  2    *                                          *
  3    * Copyright, (C) Honeywell Bull Inc., 1987 *
  4    *                                          *
  5    ******************************************** */
  6 
  7 /* HISTORY COMMENTS:
  8   1) change(87-05-04,Wallman), approve(87-05-04,MCR7586),
  9      audit(87-07-16,Flegel), install(87-08-07,MR12.1-1072):
 10      First release.
 11   2) change(87-09-02,Wallman), approve(87-09-02,MCR7586),
 12      audit(87-07-16,Flegel), install(87-09-10,MR12.1-1103):
 13      PBF to improve robustness of string handling by avoiding all str*
 14      functions.
 15   3) change(88-02-24,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
 16      install(88-10-12,MR12.2-1160):
 17      Removed debugging code and unused variables; code re-formatting.
 18   4) change(88-04-11,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
 19      install(88-10-12,MR12.2-1160):
 20      Added mapping of ^2 key to ALT-0, DEL key to ASCII del and
 21      ^BREAK (if break flag is set) to ALT-B.
 22   5) change(88-05-18,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
 23      install(88-10-12,MR12.2-1160):
 24      Deleted parameter hide_sw in calls to replay().
 25   6) change(88-07-25,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
 26      install(88-10-12,MR12.2-1160):
 27      Documentation additions only. Added header comments to all routines.
 28   7) change(88-08-09,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
 29      install(88-10-12,MR12.2-1160):
 30      Fix references to include files; "wstdefs.h" was split into
 31      "wstdefs.h" and "wsttype.h" to avoid declaration clash; also,
 32      various constants defined to descriptive names.
 33   8) change(88-08-30,Lee), approve(88-09-12,MCR7986), audit(88-09-19,Flegel),
 34      install(88-10-12,MR12.2-1160):
 35      Removed non-edit async mode and references to non-edit async mode
 36      line editing routines.
 37   9) change(89-01-18,Lee), approve(89-01-02,MCR8043), audit(89-03-14,Flegel),
 38      install(89-04-24,MR12.3-1033):
 39      Separated input and output parameters to int86() to avoid confusion
 40      when parameters are used.
 41                                                    END HISTORY COMMENTS */
 42 
 43 /* WSTKBSCR - keyboard/screen manager for WSTERM */
 44 
 45 /* This module holds functions needed to handle keyboard input and to manage
 46    the screen display. */
 47 
 48 #include    <stdio.h>
 49 #include    <dos.h>
 50 #include    <ws.h>
 51 #include    <ws_mcb.h>
 52 #include    <wsmincap.h>
 53 #include    "wstdefs.h"
 54 #include    "wsttype.h"
 55 #include    "wstglob.h"
 56 
 57 
 58 /**** PUBLIC FUNCTIONS ******************************************************/
 59 
 60 /*^L
 61 **********************************************************************
 62 
 63   Routine:            EXTRACT_MSG
 64 
 65   Function:
 66       This routine extracts sync mode messages from kb.klin. Sync
 67   mode messages are delimited by break characters, screen line length
 68   overflow, and read count exhaust.
 69 
 70   Parameters:
 71      (input)          no_block - specifies to send zero length
 72                           messages
 73      (input)          tro_sw - timer run out switch
 74      (input)          txmt_sw - specifies to transmit the message
 75      (input)          hide_sw - switch to suppress echoing
 76 
 77   Returns:            NONE
 78 
 79 **********************************************************************/
 80 
 81 extract_msg (no_block, tro_sw, txmt_sw, hide_sw)
 82 int no_block,   /* Send 0-length messages */
 83 tro_sw,         /* Timer run out switch */
 84 txmt_sw,        /* Transmit the message */
 85 hide_sw;        /* Switch to suppress echoing */
 86 
 87 {
 88     int curc,       /* Local column value */
 89         echo;       /* Local echo control */
 90 
 91     register int    cndx;        /* Working index */
 92 
 93     /* Scan the keyboard buffer. We will hit one of:
 94        1) a break character (msgs were backed up)
 95        2) end of kb.klin (a timer runout has occurred)
 96        3) read_count exhaust
 97        4) screen.maxcol overflow */
 98 
 99     echo = kb.echo;
100     curc = screen.curcol;
101     for (cndx = kb.endx;  cndx < strlen (kb.klin) && (read_count > 0)
102         && ((echo && curc <= screen.maxcol) || ~echo);  cndx++) {
103         read_count--;           /* Decrement the read count */
104 
105         /* If WSTERM is echoing, count and echo all printing characters */
106 
107         if (echo && ~hide_sw && isprint (kb.klin [cndx])) {
108             curc++;
109             putscr (&kb.klin [cndx], 1);
110         }
111 
112         /* If the klin character is a break character or a control
113            character, we're at the linelength limit, or the read
114            count has exhausted, end the read, send that much, force
115            echoing OFF, and ltrim the data sent from kb.klin */
116 
117         if (stpchr (brk_table, kb.klin [cndx]) || iscntrl (kb.klin [cndx]) ||
118             (echo && curc == screen.maxcol) || read_count == 0) {
119             term_read = ON;     /* Terminate the read */
120 
121             send_msg (kb.klin, ++cndx);
122             strcpy (kb.klin, &kb.klin [cndx]);
123             kb.cndx = strlen (kb.klin);
124             cndx = kb.cndx;
125             kb.endx = 0;
126             echo = OFF;         /* Force echoing OFF */
127             read_active = OFF;
128         }
129     }               /* End of kb.klin scan loop */
130 
131     /* If we fall out of the kb.klin scan loop with anything left we
132        have reached the end of kb.klin */
133 
134     if (strlen (kb.klin) > 0) {
135 
136         /* Send the leftovers if not echoing, otherwise, update the last echoed
137            character index (kb.endx) */
138 
139         if (txmt_sw && ~kb.echo && read_active) {
140             send_msg (kb.klin, strlen (kb.klin));
141             kb.cndx = 0;
142             kb.endx = 0;
143             setmem (kb.klin, sizeof (kb.klin), NUL);
144         }
145         else if (kb.echo && ~hide_sw)
146             kb.endx = kb.cndx;
147     }
148 
149     screen.curcol = curc;
150 
151 }               /* End of extract_msg */
152 
153 
154 
155 /*^L
156 **********************************************************************
157 
158   Routine:            NEXT_TAB
159 
160   Function:
161       This routine computes the distance (in columns) to the next tab
162   column.
163 
164   Parameters:
165      (input)          ccol - specifies the current screen column
166 
167   Returns:            the number of columns to the next tab column
168 
169 **********************************************************************/
170 
171 next_tab (ccol)
172 int ccol;       /* Current column */
173 
174 {
175     int space;
176 
177     /* assume that the tabs are set every 10 columns; */
178     /* this calculation may have to work for negative */
179     /* values. */
180 
181     space = TAB_SIZE * ((ccol + TAB_SIZE) / TAB_SIZE) - ccol;
182     return (space);
183 }               /* End of next_tab */
184 
185 
186 
187 /*^L
188 **********************************************************************
189 
190   Routine:            PROCESS_KBRD_CHAR
191 
192   Function:
193       This routine handles keyboard input for non-edit mode and sync
194   mode.
195 
196   Parameters:
197      (input)          hide_sw - switch to suppress echoing
198 
199   Returns:            NONE
200 
201 **********************************************************************/
202 
203 process_kbrd_char (hide_sw)
204 int hide_sw;        /* Switch to suppress echoing */
205 
206 {
207     read_keyboard (BLOCK);
208 
209     /* Processing an octal escape? */
210 
211     if (octsw > 0) {
212 
213         /* Is this a meaningful octal digit? */
214 
215         if (isdigit (kb.chr [0]) && kb.chr [0] < '8' && octsw < 3) {
216             octval = 8 * octval + (int) kb.chr [0] - 060;
217             octsw++;
218 
219             return;
220         }
221         else {  /* Not part of \nnn */
222             sprintf (octstr, "%c%03o", LNC, octval);
223             sprintf (kb.dstr, "%c", kb.chr [0]);
224 
225             put_kchr (hide_sw);
226             octsw = 0;
227         }
228 
229         return;
230     }               /* End of octal processing */
231 
232     /* if control char or ALT key or function key */
233 
234     if (iscntrl (kb.chr [0]) || (kb.key_status & ALT) || kb.fkey) {
235         process_ctl_char (hide_sw);
236         return;
237     }
238 
239     if (~mowse_active) {     /* Glass TTY mode? */
240 
241         /* Send any leftover typeahead */
242 
243         if (kb.cndx > 0) {
244             puttdata (FG_TERMINAL_DATA, kb.klin, kb.cndx);
245             kb.cndx = 0;
246         }
247 
248         if (kb.echo && ~hide_sw) {
249 
250             putscr (kb.dstr, 1);
251         }
252         puttdata (FG_TERMINAL_DATA, kb.chr, 1);
253     }               /* End of glass TTY mode */
254 
255     /* Otherwise, we are in packet mode */
256 
257     else {
258         /* Transmit sync mode message for control and break characters */
259 
260         if (sync && (iscntrl (kb.chr [0]) || stpchr (brk_table, kb.chr [0]))) {
261 
262             put_kchr (hide_sw);
263             if (read_active)
264                 extract_msg (~NO_BLOCK, ~TRO,
265                     TXMT_MSG, hide_sw);
266             return;
267         }
268 
269         /* we're in sync mode */
270 
271         sprintf (kb.dstr, "%c", kb.chr [0]);
272         put_kchr (hide_sw);
273 
274     }               /* End of packet mode */
275 }               /* End of process_kbrd_char */
276 
277 
278 
279 /*^L
280 **********************************************************************
281 
282   Routine:            READ_KEYBOARD
283 
284   Function:
285       This routine reads the keyboard for key presses. If a key is
286   read, it is kept and maintained in the global structure 'kb'. The
287   caller may specify that the routine waits if no keyboard input or
288   returns immediately instead.
289 
290   Parameters:
291      (input)          block_sw - if TRUE, routine will wait if no
292                           keyboard key was pressed, otherwise routine
293                           returns immediately
294 
295   Returns:            OFF if no key in the key queue
296                       ON if a key was fetched from the key queue
297 
298 **********************************************************************/
299 
300 read_keyboard (block_sw)
301 int block_sw;       /* Block-for-input switch */
302 
303 /*  If block_sw is ON, return the key code of the first available
304     keystroke, waiting if there isn't one waiting the DOS key queue.
305     The key code is (scan code || character code).
306 
307     If block_sw is OFF, return OFF if the DOS key queue is empty;
308     otherwise, return the key code if the first queued keystroke.
309 */
310 
311 {
312     int flags,      /* CPU flags */
313     key_q_sw;
314     union regs_struct regs, outregs;
315     int ch;
316 
317     /* check if break key has been hit */
318     if (break_flag) {
319 
320         /* if block switch is off, just return key hit status */
321         if (~block_sw)
322             return(ON);
323 
324         /* handle the break key by mapping it to ALT-B for BREAK
325            processing */
326         kb.key_status = ALT;
327         kb.key_ndx = B_KEY_CODE;
328         kb.chr [0] = NUL;
329         kb.chr [1] = NUL;
330         kb.fkey = OFF;
331         break_flag = 0;
332         return(ON);
333     }
334 
335     setmem (&regs, sizeof (regs), 0);
336 
337     /* Get key queue status */
338 
339     regs.hreg.ah = BIOS_KB_STATUS;
340     flags = int86 (BIOS_KB, &regs, &outregs) & LOW_8_BITS;
341     key_q_sw = -((flags & Z_FLAG_MASK) == 0);    /* ZF = no character */
342 
343     /* Get key shift status */
344 
345     regs.hreg.ah = BIOS_KB_SHIFTS;
346     int86 (BIOS_KB, &regs, &outregs);
347     if (kb.key_status == -1)
348         kb.key_status = outregs.hreg.al;
349 
350     /* Return changes if we're not asked to block */
351 
352     if (~block_sw) {
353 
354         return (key_q_sw);
355     }
356 
357     /* Get keystroke from DOS */
358 
359     regs.hreg.ah = BIOS_KB_READ;
360     int86 (BIOS_KB, &regs, &outregs);
361     kb.key_ndx = outregs.hreg.ah;
362     kb.chr [0] = (char) outregs.hreg.al;
363     kb.chr [1] = NUL;
364     kb.fkey = OFF;
365 
366     /* encode keyboard character into unique code */
367     if (outregs.hreg.al)
368         ch = outregs.hreg.al;
369     else
370         ch = outregs.hreg.ah + ASCII_EXTEND_CODE;
371 
372     /* if ^2 (same as ^@) key hit, map it ALT-0 to send a NUL */
373     if (ch == CTRL_2) {
374         kb.key_status = ALT;
375         kb.key_ndx = zero_KEY_CODE;
376         kb.chr [0] = NUL;
377         kb.chr [1] = NUL;
378         kb.fkey = OFF;
379         return(ON);
380     }
381 
382     /* if DEL key hit, map it onto ASCII DEL */
383     else if (ch == DEL_KEY) {
384         kb.key_status = 0;
385         kb.key_ndx = 0;
386         kb.chr [0] = DEL;
387         kb.chr [1] = NUL;
388         kb.fkey = OFF;
389         return(ON);
390     }
391 
392     /* Refetch shift status in case it changed during the wait for the character */
393 
394     regs.hreg.ah = BIOS_KB_SHIFTS;
395     int86 (BIOS_KB, &regs, &outregs);
396     kb.key_status = outregs.hreg.al;
397 
398 
399     /* Is this a function key? */
400 
401     if (kb.key_ndx >= F1_KEY_CODE && kb.key_ndx < F1_KEY_CODE + 10) {
402         kb.fkey = ON;
403         strcpy (kb.chr, &F1_10 [kb.key_ndx - F1_KEY_CODE - 1][4]);
404     }
405 
406     if (kb.key_ndx >= F11_KEY_CODE && kb.key_ndx < F11_KEY_CODE + 2) {
407         kb.fkey = ON;
408         strcpy (kb.chr, &F11_12 [kb.key_ndx - F11_KEY_CODE - 1][4]);
409     }
410 
411     return (ON);
412 }               /* End of read_keyboard */
413 
414 
415 
416 /*^L
417 **********************************************************************
418 
419   Routine:            SEND_MSG
420 
421   Function:
422       This routine sends a control message to the host.
423 
424   Parameters:
425      (input)          msg_str - specifies the message buffer
426                           containing the message to send
427      (input)          msg_len - specifies the number of bytes in the
428                           message buffer to send
429 
430   Returns:            NONE
431 
432 **********************************************************************/
433 
434 send_msg (msg_str, msg_len)
435 int     msg_len;        /* Number of bytes to send */
436 char    *msg_str;       /* The byte string */
437 
438 {
439     struct snd_msg_struct snd_msg;
440 
441     setmem (&snd_msg, sizeof (snd_msg), 0);
442 
443     if (~kb.echo) {          /* RNE */
444         if (term_read) {
445             snd_msg.hdr.id [0] = 'E';
446             snd_msg.hdr.id [1] = 'N';
447             snd_msg.hdr.id [2] = 'I';
448             term_read = OFF;
449             read_active = OFF;
450             kb.echo = OFF;
451         }
452         else {
453             snd_msg.hdr.id [0] = 'U';
454             snd_msg.hdr.id [1] = 'I';
455             snd_msg.hdr.id [2] = 'C';
456         }
457     }
458     else {  /* RWE */
459         if (term_read) {
460             snd_msg.hdr.id [0] = 'E';
461             snd_msg.hdr.id [1] = 'E';
462             snd_msg.hdr.id [2] = 'I';
463             term_read = OFF;
464             read_active = OFF;
465             kb.echo = OFF;
466         }
467         else {
468             snd_msg.hdr.id [0] = 'E';
469             snd_msg.hdr.id [1] = 'I';
470             snd_msg.hdr.id [2] = 'C';
471         }
472     }
473 
474     byteshift (msg_len, &snd_msg.hdr.msb_size, &snd_msg.hdr.lsb_size);
475     snd_msg.data [0] = NUL;     /* Erase target string */
476     strncpy (snd_msg.data, msg_str, msg_len);
477 
478     puttdata (FG_CONTROL_MESSAGE, &snd_msg, msg_len + 5);
479 }               /* End of send_msg */
480 
481 
482 
483 /**** LOCAL FUNCTIONS *******************************************************/
484 
485 /*^L
486 **********************************************************************
487 
488   Routine:            PROCESS_CTL_CHAR
489 
490   Function:
491       This routine handles control, ALT and function keys in non-edit
492   mode (and sync) mode.
493 
494   Parameters:
495      (input)          hide_sw - if TRUE, echoing is suppressed
496 
497   Returns:            NONE
498 
499 **********************************************************************/
500 
501 process_ctl_char (hide_sw)
502 int hide_sw;        /* Switch to suppress echoing */
503 
504 {
505     /* Check for local escapes */
506 
507     if (kb.key_status & ALT) {
508 
509         local_esc (kb.key_ndx);
510         return;
511     }
512 
513     /* For glass TTY mode, just send the 'character' */
514 
515     if (~mowse_active) {
516         puttdata (FG_TERMINAL_DATA, kb.chr, strlen (kb.chr));
517 
518     }  /* For sync mode, they are break characters */
519 
520     else if (sync) {
521         catstr (&kb.klin [kb.cndx], kb.chr, strlen (kb.chr),
522              "kb.klin", sizeof (kb.klin));
523         kb.cndx += strlen (kb.chr);
524         kb.klin [kb.cndx] = NUL;
525 
526         if (read_active) {
527             term_read = ON;
528             extract_msg (~NO_BLOCK, ~TRO, TXMT_MSG,
529                  hide_sw);
530         }
531     }
532 }               /* End of process_ctl_char */
533 
534 
535 
536 /*^L
537 **********************************************************************
538 
539   Routine:            PUT_KCHR
540 
541   Function:
542       When a key or an octal value (specified by entering \ followed
543   by octal digits) is entered, it is stored for examination before
544   further processing takes place. This routine takes the stored value
545   and places it into the global buffer containing the input line
546   (kb.klin). The routine is called when not in edit mode.
547 
548   Parameters:
549      (input)          hide_sw - if TRUE, specifies echoing is to be
550                           suppressed
551 
552   Returns:            NONE
553 
554 **********************************************************************/
555 
556 put_kchr (hide_sw)
557 int hide_sw;        /* Switch to suppress echoing */
558 
559 {
560 
561     /* Is there a pending octal escape */
562 
563     if (octsw > 0) {
564         if (kb.cndx == strlen (kb.klin)) {   /* Tack octval on the end */
565             kb.klin [kb.cndx] = (char) octval;
566             kb.pos [kb.cndx] = ds.ccol;
567             kb.klin [++kb.cndx] = NUL;
568         }
569         else  /* Replace buffered character */
570             kb.klin [kb.cndx] = (char) octval;
571     }
572 
573     if (kb.cndx == strlen (kb.klin)) {      /* Tack kb.chr on the end */
574         kb.klin [kb.cndx] = kb.chr [0];
575         kb.pos [kb.cndx] = ds.ccol + 4 * (octsw > 0);
576         kb.klin [kb.cndx + 1] = NUL;
577     }
578     else
579         kb.klin [kb.cndx] = kb.chr [0]; /* Replace buffered character */
580 
581     if (~sync) {
582         if (put_kstr (hide_sw)) {       /* Nonzero if kb.dstr wont fit */
583             kb.klin [kb.cndx] = NUL;
584             return;
585         }
586     }
587 
588     kb.cndx++;          /* Advance buffer index */
589 }               /* End of put_kchr */
590 
591 
592 
593 /*^L
594 **********************************************************************
595 
596   Routine:            PUT_KSTR
597 
598   Function:
599       This routine displays the keyboard character string in the
600   global variable kb.dstr.
601 
602   Parameters:
603      (input)          hide_sw - if TRUE, specifies that echoing is to
604                           be suppressed
605 
606   Returns:            NONE
607 
608 **********************************************************************/
609 
610 put_kstr (hide_sw)
611 int hide_sw;        /* Switch to suppress echoing */
612 
613 {
614     int err_flg;        /* Error flag */
615 
616     err_flg = OFF;
617 
618     /* Replacing a character */
619 
620     if (ds.ccol < strlen (ds.dlin) && ~hide_sw && kb.endx == kb.cndx) {
621         if (replay (strlen (kb.dstr)))
622             err_flg = ON;
623     }
624 
625     /* Appending a character */
626 
627     else {
628 
629         /* Wrap line if last column */
630 
631         if (ds.ccol == screen.maxcol) {
632             if (wrap_line (ds.lndx, ds.ccol))
633                 err_flg = ON;       /* Error if line wont fit */
634             else
635                 ds.lndx++;
636         }
637 
638         /* If kb.dstr overflows */
639 
640         if (strlen (ds.dlin) + strlen (kb.dstr) > screen.maxcol &&
641             ~err_flg) {
642             if (replay (strlen (kb.dstr)))
643                 err_flg = ON;
644         }
645         else if (~err_flg)       /* Doesn't overflow */ {
646             if (octsw > 0) {
647                 catstr (ds.dlin, octstr, 4, "ds.dlin",
648                      sizeof (ds.dlin));
649                 ds.ccol += strlen (octstr);
650                 if (kb.echo && ~hide_sw)
651                     putscr (octstr, 4);
652             }
653 
654             if (kb.echo && ~hide_sw) {
655                 if (ds.ccol < strlen (ds.dlin)) {
656                     putscr (&ds.dlin [ds.ccol],
657                          strlen (&ds.dlin [ds.ccol]));
658 
659                     ds.ccol = strlen (ds.dlin);
660                 }
661                 else {
662                     catstr (ds.dlin, kb.dstr,
663                          strlen (kb.dstr), "ds.dlin", sizeof (ds.dlin));
664                     ds.spill [ds.lndx] = 0;
665                     ds.ccol += strlen (kb.dstr);
666                     putscr (kb.dstr, strlen (kb.dstr));
667 
668                 }
669             }
670         }
671     }
672 
673     kb.endx++;
674 
675     return (err_flg);
676 }               /* End of put_kstr */
677 
678 
679 
680 /*^L
681 **********************************************************************
682 
683   Routine:            WRAP_LINE
684 
685   Function:
686       This routine wraps a keyboard display line.
687 
688   Parameters:
689      (input)          lin - specifies the current line
690      (input)          col - specifies the current column
691 
692   Returns:            NONE
693 
694 **********************************************************************/
695 
696 wrap_line (lin, col)
697 int lin,        /* Current line */
698 col;        /* Current column */
699 
700 {
701 
702     /* Copy ds.dlin to ds.map */
703 
704     strcpy (&ds.map [lin][0], ds.dlin);
705 
706     /* Is the display map full? */
707 
708     if (lin == DS_LCT - 1) {
709         putscr (SCP, strlen (SCP));
710         cursor_move (MINI_LIN, 0);
711         printf ("%s%s", "Input line cannot be processed because it ",
712                     "exceeds screen size. (any key)");
713 
714         /* Wait for a character */
715 
716         while (read_keyboard (~BLOCK))
717             ;
718 
719         cursor_move (MINI_LIN, 0);
720         putscr (EL, strlen (EL));
721         putscr (RCP, strlen (RCP));
722 
723         cursor_move (screen.curlin + lin, col);
724         putscr (EL, strlen (EL));
725         return (ON);
726     }
727 
728     scroll ();
729     ds.ccol = 0;
730     ds.splct [lin + 1] = 0;
731     setmem (ds.dlin, sizeof (ds.dlin), NUL);
732     if (lin + 1 > ds.lct)
733         ds.lct++;
734 
735     return (OFF);
736 }               /* End of wrap_line */
737 
738