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 (®s, sizeof (regs), 0); 336 337 /* Get key queue status */ 338 339 regs.hreg.ah = BIOS_KB_STATUS; 340 flags = int86 (BIOS_KB, ®s, &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, ®s, &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, ®s, &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, ®s, &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