1 /* ********************************************
   2    *                                          *
   3    * Copyright, (C) Honeywell Bull Inc., 1987 *
   4    *                                          *
   5    ******************************************** */
   6 
   7 
   8 /* HISTORY COMMENTS:
   9   1) change(87-05-04,Wallman), approve(87-05-04,MCR7586),
  10      audit(87-08-10,Flegel), install(87-08-07,MR12.1-1072):
  11      First release.
  12   2) change(87-08-14,Wallman), approve(87-08-14,PBF7586),
  13      audit(87-08-17,Flegel), install(87-09-10,MR12.1-1103):
  14      PBF increasing length of msg_line to 256 to fix stringsize condition.
  15   3) change(87-09-02,Wallman), approve(87-09-02,PBF7586),
  16      audit(87-08-17,Flegel), install(87-09-10,MR12.1-1103):
  17      PBF to improve robustness of string handling by avoiding all str*
  18      functions.
  19   4) change(87-09-17,Wallman), approve(87-09-17,PBF7586),
  20      audit(87-09-17,LJAdams), install(87-09-18,MR12.1-1109):
  21      Added support for Cursor Horizontal Absolute ANSI control sequence.
  22   5) change(88-02-24,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
  23      install(88-10-12,MR12.2-1160):
  24      Removed debugging code and unused variables,
  25      re-formatting.
  26   6) change(88-04-11,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
  27      install(88-10-12,MR12.2-1160):
  28      Added routine to flush keyboard input after processing BREAK
  29      function. Replaces a "while read_keyboard()" statement which
  30      did not work properly.
  31   7) change(88-04-20,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
  32      install(88-10-12,MR12.2-1160):
  33      Added support for help screen in local_esc().
  34   8) change(88-04-22,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
  35      install(88-10-12,MR12.2-1160):
  36      Added support for status line; output to screen is filtered to
  37      trap some invalid escape sequences and to trap sequences which
  38      corrupts the status line.
  39   9) change(88-05-18,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
  40      install(88-10-12,MR12.2-1160):
  41      Deleted hide_sw reference to function replay().
  42  10) change(88-05-26,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
  43      install(88-10-12,MR12.2-1160):
  44      Handling of ALT-S, ALT-O, ALT-F and ALT-P.
  45  11) change(88-07-20,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
  46      install(88-10-12,MR12.2-1160):
  47      Modified to make edit mode default after atm.
  48  12) change(88-07-25,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
  49      install(88-10-12,MR12.2-1160):
  50      Documentation additions only. Added header comments to all routines.
  51  13) change(88-08-09,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
  52      install(88-10-12,MR12.2-1160):
  53      Fix references to include files; "wstdefs.h" was split into
  54      "wstdefs.h" and "wsttype.h" to avoid declaration clash; also,
  55      various constants defined to descriptive names.
  56  14) change(88-08-30,Lee), approve(88-09-12,MCR7986), audit(88-09-19,Flegel),
  57      install(88-10-12,MR12.2-1160):
  58      Removed non-edit async mode and references to non-edit async mode
  59      line editing routines.
  60  15) change(89-01-30,Lee), approve(89-01-02,MCR8043), audit(89-03-14,Flegel),
  61      install(89-04-24,MR12.3-1033):
  62      phx21233 - Updated kb.echo when toggling between edit and non-edit mode
  63      while MOWSE is not attached. Updated end of page handling to allow
  64      variable page size.
  65                                                    END HISTORY COMMENTS */
  66 
  67 /* WSTUTIL - WSTERM utility routines */
  68 
  69 /* This module holds functions used by more than one WSTERM module */
  70 
  71 #include  <stdio.h>
  72 #include  <dos.h>
  73 #include  <ws.h>
  74 #include  <cat.h>
  75 #include  <wsmincap.h>
  76 #include  "wstdefs.h"
  77 #include  "wsttype.h"
  78 #include  "wstglob.h"
  79 
  80 /*^L
  81 **********************************************************************
  82 
  83   Routine:            BYTESHIFT
  84 
  85   Function:
  86       When loading integer values, different machines will load
  87   either the high or the low byte first. Multics always wants the
  88   data loaded in the low byte. This routine will shift the bits if
  89   needed regardless of machine type.
  90 
  91   Parameters:
  92      (input)          number - the integer value to load
  93      (output)         high - specifies the contents of the high byte
  94      (output)         low - specifies the contents of the low byte
  95 
  96   Returns:            NONE
  97 
  98 **********************************************************************/
  99 
 100 byteshift (number, high, low)
 101 int   number;
 102 byte  *high,
 103       *low;
 104 
 105 {
 106     if (number < MAX_8_BIT_VALUE) {
 107         *high = 0;
 108         *low  = number;
 109     }
 110     else {
 111         *high = number / MAX_8_BIT_VALUE;
 112         *low  = number % MAX_8_BIT_VALUE;
 113     }
 114 }                                       /* End of byteshift */
 115 
 116 
 117 
 118 /*^L
 119 **********************************************************************
 120 
 121   Routine:            CATSTR
 122 
 123   Function:
 124       This routine catenates strings with stringsize checking.
 125 
 126   Parameters:
 127      (input/output)   target - target string
 128      (input)          add_on - add-on string
 129      (input)          target_name - target string name
 130      (input)          add_on_size - character count of 'add_on'
 131      (input)          target_size - target string size
 132 
 133   Returns:            NONE
 134 
 135   Note 1 - This routine will abort WSTERM with an error message if a
 136       string size overflow is encountered!!!
 137 **********************************************************************/
 138 
 139 catstr (target, add_on, add_on_size, target_name, target_size)
 140 char    *target,            /* Target string */
 141         *add_on,            /* Add-on string */
 142         target_name [];     /* Target string name */
 143 int     add_on_size,        /* Character count of 'add_on' */
 144         target_size;        /* Target string size */
 145 {
 146     register int old_length;
 147 
 148     old_length = strlen(target);
 149 
 150     if (old_length + add_on_size > target_size - 1) {
 151 
 152         put_screen_str ("Program error: A string size overflow has been detected in ");
 153         put_screen_str (target_name);
 154 
 155         exit (1);
 156     }
 157 
 158     movmem (add_on, &target [strlen (target)], add_on_size);
 159     target [ old_length + add_on_size] = NUL_TERMINATOR;
 160     return;
 161 }                                       /* End of catstr */
 162 
 163 
 164 
 165 /*^L
 166 **********************************************************************
 167 
 168   Routine:            CLEAR_SCREEN
 169 
 170   Function:
 171       This routine clears the data area of the screen (the first 24
 172   lines) and initializes the global screen information variables
 173   accordingly. The status line area (25th line) is left untouched.
 174 
 175   Parameters:         NONE
 176 
 177   Returns:            NONE
 178 
 179 **********************************************************************/
 180 
 181 clear_screen ()
 182 
 183 {
 184     clear_window();
 185 
 186     screen.curcol = CURSOR_HOME_COL;
 187     screen.curlin = CURSOR_HOME_ROW;
 188     screen.EOP_ct = 0;
 189     ds.ccol = 0;
 190     ds.lct = 0;
 191     ds.splct [0] = NUL;
 192 }                                       /* End of clear_screen */
 193 
 194 
 195 
 196 /*^L
 197 **********************************************************************
 198 
 199   Routine:            EMIT_MSG
 200 
 201   Function:
 202       This routine displays a host or local message on the screen.
 203   The message may contain ANSI escape sequences. It calls the routine
 204   EMIT_MSG_2 to do the real work since the message is parsed and
 205   accumulated while checking for ANSI escape sequences so it must not
 206   let the message become too large for EMIT_MSG_2 to handle.
 207 
 208   Parameters:
 209      (input)          msg - specifies the buffer containing the
 210                           message to display
 211      (input)          msgl - specifies the number of characters from
 212                           the message buffer to display
 213      (input)          pstr_sw - switch to specify if the message is a
 214                           prompt string
 215 
 216   Returns:            NONE
 217 
 218 **********************************************************************/
 219 
 220 emit_msg(msg,msgl,pstr_sw)
 221 char msg[];
 222 int msgl, pstr_sw;
 223 {
 224     char *msg_ptr;
 225 
 226     msg_ptr = msg;
 227 
 228     /* display message in chunks up to 20 bytes in size */
 229     while (msgl > 20) {
 230         emit_msg_2(msg_ptr, 20, pstr_sw);
 231         msg_ptr += 20;
 232         msgl -= 20;
 233     }
 234     emit_msg_2(msg_ptr,msgl,pstr_sw);
 235 
 236     /* clear prompt string after displaying entire prompt string */
 237     if (screen.curcol < 1 || pstr_sw) {
 238         ds.pstrl = 0;
 239         setmem (ds.pstr, sizeof (ds.pstr), NUL);
 240         kb.pos [0] = NUL;
 241     }
 242 }
 243 
 244 
 245 
 246 /*^L
 247 **********************************************************************
 248 
 249   Routine:            EMIT_MSG_2
 250 
 251   Function:
 252       This routine displays a host or local message on the screen.
 253   The message may contain ANSI escape sequences.
 254 
 255   Parameters:
 256      (input)          msg - specifies the buffer containing the
 257                           message to display
 258      (input)          msgl - specifies the number of characters from
 259                           the message buffer to display
 260      (input)          pstr_sw - switch to specify if the message is a
 261                           prompt string
 262 
 263   Returns:            NONE
 264 
 265 **********************************************************************/
 266 
 267 emit_msg_2 (msg, msgl, pstr_sw)
 268 char    msg [];             /* The message */
 269 int     msgl,               /* Message length */
 270         pstr_sw;            /* Prompt string */
 271 
 272 {
 273     int curc, curl,         /* For tracking cursor position */
 274         ipstr,              /* klin index for start of pstr */
 275         newc, newl,         /* For tracking cursor position */
 276         tab_width;          /* SPs needed for tab expansion */
 277     register int    i;               /* Working index */
 278     char    msg_line [256],     /* For message display */
 279             tab_space [12];
 280 
 281     /* We must parse display message for ANSI control sequences in order that
 282        their side effects are handled properly.
 283 
 284        Presumption:
 285        Display messages contain ONLY alphamerics & ANSI control sequences */
 286 
 287     ipstr = 0;
 288     setmem (msg_line, sizeof (msg_line), NUL);
 289 
 290     curc = screen.curcol;
 291     curl = screen.curlin;
 292 
 293     /* Scan the message */
 294 
 295     for (i = 0; i < msgl; i++) {           /* Begin message parse loop */
 296 
 297         if (checkkey() == ALT_B)
 298             return;
 299 
 300         switch (msg [i]) {
 301             case 'A':                         /* CUU - cursor up */
 302                 catstr (msg_line, &msg [i], 1, "msg_line-3",
 303                               sizeof (msg_line));
 304 
 305                 if (ds.do_ctl) {
 306                     curl -= max (newl, 1);
 307                     ds.do_ctl = OFF;
 308                 }
 309                 else
 310                     curc++;
 311                 break;
 312 
 313             case 'B':                         /* CUD - cursor down */
 314                 catstr (msg_line, &msg [i], 1, "msg_line-3",
 315                               sizeof (msg_line));
 316 
 317                 if (ds.do_ctl) {
 318                     curl += max (newl, 1);
 319                     ds.do_ctl = OFF;
 320                 }
 321                 else
 322                     curc++;
 323                 break;
 324 
 325             case BEL:                         /* ASCII BEL */
 326                 catstr (msg_line, &msg [i], 1, "msg_line",
 327                               sizeof (msg_line));
 328                 break;
 329 
 330             case BSP:                         /* ASCII BS */
 331                 if (curc > 0) {
 332                     catstr (msg_line, &msg [i], 1,
 333                          "msg_line", sizeof (msg_line));
 334                     curc = max (curc - 1, 0);
 335                 }
 336                 break;
 337 
 338             case 'C':                         /* CUF - cursor forward */
 339                 catstr (msg_line, &msg [i], 1, "msg_line-3",
 340                               sizeof (msg_line));
 341 
 342                 if (ds.do_ctl) {
 343                     curl += max (newl, 1);
 344                     ds.do_ctl = OFF;
 345                 }
 346                 else
 347                     curc++;
 348                 break;
 349 
 350             case CR:                          /* ASCII CR */
 351                 if (strlen (msg_line) > 0) {
 352                     catstr (msg_line, &msg [i], 1,
 353                          "msg_line", sizeof (msg_line));
 354 
 355                     putscr (msg_line, strlen (msg_line));
 356                     setmem (msg_line, sizeof (msg_line), NUL);
 357                 }
 358 
 359                 cursor_move (curl, 0);  /* Go to left margin */
 360                 curc = 0;
 361 
 362                 ds.pstrl = 0;
 363                 setmem (ds.pstr, sizeof (ds.pstr), NUL);
 364                 ipstr = i + 1;                /* Next char starts a prompt string */
 365                 break;
 366 
 367             case 'D':                         /* CUB - cursor backward */
 368                 catstr (msg_line, &msg [i], 1, "msg_line-3",
 369                               sizeof (msg_line));
 370 
 371                 if (ds.do_ctl) {
 372                     curc -= max (newl, 1);
 373                     ds.do_ctl = OFF;
 374                 }
 375                 else
 376                     curc++;
 377                 break;
 378 
 379             case ESC:                         /* Starting display control */
 380                 if (strlen (msg_line) > 0) {
 381 
 382                     putscr (msg_line, strlen (msg_line));
 383                     setmem (msg_line, sizeof (msg_line), NUL);
 384                 }
 385 
 386                 catstr (msg_line, &msg [i], 1, "msg_line-2",
 387                               sizeof (msg_line));
 388                 newc = 0;                       /* Reset accumulators */
 389                 newl = 0;
 390                 ds.do_ctl = ON;                 /* Now parsing a display control */
 391                 break;
 392 
 393             case 'G':
 394                 catstr (msg_line, &msg [i], 1, "msg_line-3",
 395                               sizeof (msg_line));
 396 
 397                 if (ds.do_ctl) {        /* ANSI CHA - record value & restart */
 398                     curc = max (newc - 1, 0);
 399                     kb.pos [0] = curc;
 400                     ds.do_col = OFF;
 401                     ds.do_ctl = OFF;
 402                     ds.pstrl = 0;
 403                     setmem (ds.pstr, sizeof (ds.pstr), NUL);
 404                     ipstr = i + 1;        /* Next char starts a prompt string */
 405 
 406                 }
 407                 else
 408                     curc++;
 409                 break;
 410 
 411             case 'H':
 412                 catstr (msg_line, &msg [i], 1, "msg_line-3",
 413                               sizeof (msg_line));
 414 
 415                 if (ds.do_ctl) {         /* ANSI CUP - record values & restart */
 416                     curl = max (newl - 1, 0);
 417                     curc = max (newc - 1, 0);
 418                     kb.pos [0] = curc;
 419                     ds.do_col = OFF;
 420                     ds.do_ctl = OFF;
 421                     ds.pstrl = 0;
 422                     setmem (ds.pstr, sizeof (ds.pstr), NUL);
 423                     ipstr = i + 1;         /* Next char starts a prompt string */
 424 
 425                 }
 426                 else
 427                     curc++;
 428                 break;
 429 
 430             case HT:                          /* ASCII HT */
 431                 tab_width = next_tab (curc);
 432                 curc += tab_width;
 433                 catstr (msg_line, strncpy (tab_space,
 434                     spaces, tab_width), tab_width, "msg_line-4", sizeof (msg_line));
 435                 break;
 436 
 437             case 'J':
 438                 catstr (msg_line, &msg [i], 1, "msg_line-5",
 439                               sizeof (msg_line));
 440 
 441                 if (ds.do_ctl) {                 /* ANSI ED - start over */
 442                     curl = 0;
 443                     curc = 0;
 444                     kb.pos [0] = 0;
 445                     ds.do_col = OFF;
 446                     ds.do_ctl = OFF;
 447                     bk_msg_showing = OFF;
 448                     fg_msg_showing = OFF;
 449                 }
 450                 else
 451                     curc++;
 452                 break;
 453 
 454             case NL:                          /* ASCII LF */
 455                 if (strlen (msg_line) > 0) {
 456 
 457                     putscr (msg_line, strlen (msg_line));
 458                     setmem (msg_line, sizeof (msg_line), NUL);
 459                 }
 460 
 461                 scroll ();
 462                 curl = screen.curlin;
 463 
 464                 if (local_action == 'b')
 465                     return;
 466 
 467                 curc = 0;
 468                 ds.pstrl = 0;
 469                 setmem (ds.pstr, sizeof (ds.pstr), NUL);
 470                 ipstr = i + 1;
 471                 break;
 472 
 473             case ';':
 474                 catstr (msg_line, &msg [i], 1, "msg_line-6",
 475                       sizeof (msg_line));
 476 
 477                 if (ds.do_ctl)                  /* ANSI field separator */
 478                     ds.do_col = ON;
 479                 else
 480                     curc++;
 481                 break;
 482 
 483             case '[':
 484                 catstr (msg_line, &msg [i], 1, "msg_line-7",
 485                               sizeof (msg_line));
 486 
 487                 if (ds.do_ctl)
 488                     ;                 /* ANSI field opener */
 489                 else
 490                     curc++;
 491                 break;
 492 
 493             default:                          /* Anything else */
 494 
 495                 /**** NOTE: Cases for char/line insert/delete may be needed */
 496 
 497                 if (ds.do_ctl) {
 498                     catstr (msg_line, &msg [i], 1,
 499                          "msg_line-8", sizeof (msg_line));
 500 
 501                     if (isdigit (msg [i])) {
 502                         if (ds.do_col)
 503                             newc = 10 * newc + (int) msg [i] -
 504                                 060;
 505                         else
 506                             newl = 10 * newl + (int) msg [i] -
 507                                 060;
 508                     }
 509                     else { /* Nothing else matters */
 510                         ds.do_col = OFF;
 511                         ds.do_ctl = OFF;
 512                     }
 513                 }
 514                 else if (isprint (msg [i])) {
 515                     catstr (msg_line, &msg [i], 1,
 516                          "msg_line-9",  sizeof (msg_line));
 517                     curc++;
 518                 }
 519                 break;
 520         }                                   /* End of switch cases */
 521 
 522         /* Do the EOL processing */
 523 
 524         if (screen.maxcol > 0 && curc == screen.maxcol) {   /* phx21233 RL - skip if line length 0 */
 525             /* In packet mode; just wrap the line */
 526 
 527             if (mowse_active) {
 528                 if (~sync) {
 529 
 530                 putscr (msg_line,
 531                          strlen (msg_line));
 532                     setmem (msg_line, sizeof (msg_line),
 533                          NUL);
 534 
 535                     scroll ();
 536                     curl = screen.curlin;
 537 
 538                     if (local_action == 'b')
 539                         return;
 540 
 541                     setmem (msg_line, sizeof (msg_line),
 542                          NUL);
 543                     catstr (msg_line,
 544                          "\\c", 2, "wrap flag",
 545                         sizeof (msg_line));
 546                     curc = 2;
 547                     ipstr = i;
 548                 }                             /* End of ~sync code */
 549             }                               /* End of mowse_active code */
 550 
 551 
 552             /* This is used so that the cursor never goes
 553                beyond screen.maxcol */
 554 
 555             else {
 556                 catstr (msg_line, bs_str,
 557                      1, "msg_line",  sizeof (msg_line));
 558                 curc--;
 559             }
 560         }                                 /* End of maxcol code */
 561     }                                     /* End of msg parse loop */
 562 
 563     /* Display any remaining message text */
 564 
 565     if (strlen (msg_line) > 0) {
 566         putscr (msg_line, strlen (msg_line));
 567         setmem (msg_line, sizeof (msg_line), NUL);
 568     }
 569 
 570     /* If the message doesn't have a trailing NL and isn't the prompt string,
 571        it may be (at least part of) a prompt string. */
 572 
 573     if (curc > 0 && ~pstr_sw) {
 574         i -= ipstr;
 575 
 576         if (ds.pstrl + i < MAX_SCREEN_COL) {         /* Only if the shoe fits ... */
 577             catstr (ds.pstr, &msg [ipstr], i, "ds.pstr",
 578                  sizeof (ds.pstr));
 579             ds.pstrl += i;
 580             kb.pos [0] = curc;
 581         }
 582 
 583     }
 584 
 585     screen.curlin = curl;
 586     screen.curcol = curc;
 587 
 588 }                                       /* End of emit_msg */
 589 
 590 
 591 
 592 /*^L
 593 **********************************************************************
 594 
 595   Routine:            LOCAL_ESC
 596 
 597   Function:
 598       This routine handles local escape functions requested when the
 599   user hits certain ALT keys.
 600 
 601   Parameters:
 602      (input)          scan_code - specifies the scan code for the
 603                           particular ALT key entered; it determines
 604                           what local escape function is being
 605                           requested
 606 
 607   Returns:            NONE
 608 
 609 **********************************************************************/
 610 
 611 local_esc (scan_code)
 612 int scan_code;
 613 {
 614     register int    i;                  /* Working index */
 615 
 616     /* Move the action character to static so everybody knows what it is */
 617 
 618     local_action = tolower (kb.chr [0]);
 619 
 620     switch (scan_code) {
 621 
 622     case Q_KEY_CODE:                    /* Exit wsterm */
 623 
 624         if (read_active) {              /* Terminate any active read */
 625             term_read = ON;
 626             send_msg (nul_str, 0);
 627         }
 628 
 629         local_action = QUIT;            /* Force the defined QUIT */
 630         break;
 631 
 632     case S_KEY_CODE:
 633         wst_freeze = !wst_freeze;   /* toggle flag which control scrolling */
 634         update_status();            /* update change on status line */
 635         if (wst_freeze) {
 636             wait_for_key();
 637             wst_freeze = FALSE;
 638             update_status();
 639         }
 640         break;
 641 
 642     /* toggle edit mode */
 643     case E_KEY_CODE:
 644         if (mowse_active)
 645             status_err_msg("Cannot toggle edit in packet mode. <any key>-resume");
 646 
 647         else if (wst_edit_mode) {
 648             if (edlin.length > 0)
 649                 status_err_msg("Line not sent, enter or kill line first. <any key>-resume");
 650 
 651             else {
 652                 wst_edit_mode = FALSE;
 653                 /* set the default to be NOT in edit mode */
 654                 glass_edit_mode = FALSE;
 655                 kb.echo = FALSE;    /* phx21233 R.L. update echo flag for glass tty edit mode */
 656             }
 657         }
 658         else if (mowse_active && kb.cndx > 0)
 659             status_err_msg("Line not sent, enter or kill line first. <any key>-resume");
 660 
 661         else {
 662             wst_edit_mode = ON;
 663             /* set the default to be in edit mode */
 664             glass_edit_mode = TRUE;
 665             kb.echo = TRUE;    /* phx21233 R.L. update echo flag for glass tty edit mode */
 666         }
 667         update_status();
 668         break;
 669 
 670 
 671     case O_KEY_CODE:
 672         wst_paging = !wst_paging;   /* toggle flag for displaying EOPs */
 673         update_status();            /* update change on status line */
 674         break;
 675 
 676     case F_KEY_CODE:
 677         wst_f_audit = !wst_f_audit;  /* toggle flag for file audit */
 678         if (wst_f_audit) {
 679             if (begin_file_audit() < 0)
 680                 wst_f_audit = OFF;
 681         }
 682         else
 683             end_file_audit();
 684         update_status();             /* update change on status line */
 685         break;
 686 
 687     case P_KEY_CODE:
 688         wst_p_audit = !wst_p_audit;  /* toggle flag for printer audit */
 689         if (wst_p_audit) {
 690             if (begin_printer_audit() < 0)
 691                 wst_p_audit = OFF;
 692         }
 693         else
 694             end_printer_audit();
 695         update_status();             /* update change on status line */
 696         break;
 697 
 698     case M_KEY_CODE:                    /* Display minibuffer */
 699 
 700         display_bkmsg ();
 701         break;
 702 
 703     case B_KEY_CODE:                    /* Line break */
 704 
 705         /* Wipe out all input typeahead */
 706 
 707         kb.cndx = 0;
 708         kb.endx = 0;
 709         kb.klin [0] = NUL;
 710 
 711         if (wst_edit_mode && !sync) {
 712             cursor_eol(&edlin);
 713             screen.curlin = edlin.cur_row;
 714             screen.curcol = edlin.cur_col;
 715         }
 716 
 717         edlin.index = 0;
 718         edlin.length = 0;
 719 
 720         flush_dos_keys();
 721 
 722         if (mowse_active) {
 723             put_to_screen(CR);
 724             put_to_screen(LF);
 725             screen.curlin = wst_fg_screen.cursor_row;
 726             screen.curcol = wst_fg_screen.cursor_col;
 727             ds.ccol = 0;
 728             ds.lct = 0;
 729             ds.lndx = 0;
 730             setmem(ds.dlin,sizeof(ds.dlin),NUL);
 731             ds.pstrl = 0;
 732             ds.pstr[0] = 0;
 733             ds.splct[0] = 0;
 734             screen.EOP_ct = 0;
 735         }
 736 
 737         puttdata (FG_BREAK, NUL, 0);
 738         break_sent = mowse_active;
 739         break;
 740 
 741     case C_KEY_CODE:                    /* Clear screen */
 742 
 743         clear_screen ();
 744         cursor_move (CURSOR_HOME_ROW, 60);
 745         putscr("WSTERM Vers ",12);
 746         putscr(version,strlen(version));
 747         cursor_move (CURSOR_HOME_ROW,CURSOR_HOME_COL);
 748 
 749         if (ds.pstrl > 0)
 750             emit_msg (ds.pstr, ds.pstrl, PSTR);
 751         else if (wst_edit_mode) {
 752             edlin.orig_row = edlin.cur_row = edlin.max_row = CURSOR_HOME_ROW;
 753             edlin.orig_col = edlin.cur_col = edlin.cur_row = CURSOR_HOME_COL;
 754             redraw_edit_line(&edlin);
 755         }
 756 
 757         break;
 758 
 759     case zero_KEY_CODE:                 /* Send NUL (set mark) */
 760     case 129:
 761 
 762         if (mowse_active)
 763             send_msg (nul_str, 1);
 764         else
 765             puttdata (FG_TERMINAL_DATA, nul_str, 1);
 766         break;
 767 
 768     case D_KEY_CODE:                    /* Display message */
 769 
 770         if (~fg_msg_showing) {
 771             status_err_msg("No foreground messages. <any key>-resume");
 772             update_status();
 773         }
 774         else {
 775             if (wst_edit_mode || (mowse_active && ~sync)) {
 776                 /* redraw for edit mode */
 777                 erase_edit_line(&edlin);
 778                 emit_msg(fg_msg.text,fg_msg_len,~PSTR);
 779                 fg_msg_len = 0;
 780                 signal_fg(OFF);
 781                 fg_msg_showing = OFF;
 782                 cursor_pos(&edlin.cur_row,&edlin.cur_col);
 783                 edlin.orig_row = edlin.cur_row;
 784                 edlin.orig_col = edlin.cur_col;
 785                 redraw_edit_line(&edlin);
 786             }
 787         }
 788         break;
 789 
 790     case H_KEY_CODE:    /* invoke the help screen */
 791         help(GENERAL_HELP);
 792         update_status();  /* restore the status line before returning */
 793         break;
 794 
 795     case V_KEY_CODE:  /* invoke history screen */
 796         if (wst_edit_mode) {
 797             history_screen(&wst_fg_screen);
 798             update_status();
 799         }
 800         else {
 801             status_err_msg("History functions available only in edit mode. <any key>-resume");
 802             update_status();
 803         }
 804         break;
 805 
 806     default:                            /* Anything else is an error */
 807         status_err_msg("Unrecognized WSTERM request, enter ALT-H for help.  <any key>-resume");
 808         update_status();
 809         break;
 810     }
 811 }                                       /* End of local_esc */
 812 
 813 
 814 
 815 /*^L
 816 **********************************************************************
 817 
 818   Routine:            PUTSCR
 819 
 820   Function:
 821       This routine writes a string to the screen driver. Undesirable
 822   escape sequences are filtered out. The ANSI clear screen "ESC [ 2
 823   J" and clear to end of page "ESC [ J" sequences are intercepted to
 824   prevent the 25th line from being cleared. Instead, appropriate
 825   routines are called to clear all but the 25th line and to clear up
 826   to but not including the 25th line. Clearing the 25th line (the
 827   status line) causes flickering each time the screen is cleared.
 828 
 829   Parameters:
 830      (input)          str - specifies the buffer containing the
 831                           message to display
 832      (input)          strl - specifies the number of characters from
 833                           the message buffer to display
 834 
 835   Returns:            NONE
 836 
 837 **********************************************************************/
 838 
 839 putscr(str,strl)
 840 char *str;
 841 int strl;
 842 {
 843     int ch;
 844     int i;
 845 
 846     /* process each character */
 847     for (i = 0; i < strl; i++) {
 848 
 849         if (checkkey() == ALT_S) {
 850             wait_for_key();
 851             wst_freeze = !wst_freeze; /* toggle flag which control scrolling */
 852             update_status();            /* update change on status line */
 853         }
 854 
 855         if (wst_freeze) {
 856             wait_for_key();
 857             wst_freeze = FALSE;
 858             update_status();
 859         }
 860 
 861         ch = *str++;
 862 
 863 /* algorithm: increment index each time the character received
 864     matches the next character in the escape sequence until the
 865     entire escape sequence is received. Once the entire sequence
 866     is recognized, handle it. */
 867 
 868         /* matching first character of escape sequence? */
 869         if (clr_index == 0) {
 870             /* first character an escape char? */
 871             if (ch == ESC)
 872                 /* yes, match next character in sequence */
 873                 clr_index++;
 874             /* not escape character, just display the character */
 875             else
 876                 put_to_screen(ch);
 877         }
 878 
 879         /* check if second character of sequence */
 880         else if (clr_index == 1) {
 881 
 882             /* match second char in sequence ? */
 883             if (ch == '[')
 884                 clr_index++;
 885 
 886             /* check if still part of valid escape sequence */
 887             else if (is_esc[ch]) {
 888                 clr_index = 0;
 889                 put_to_screen(ESC);
 890                 put_to_screen(ch);
 891 
 892                 /* if reset sequence ESC c, recover for WSTERM */
 893                 if (ch == 'c')
 894                     wst_reset();
 895             }
 896             /* not the sequence we are looking for, flush */
 897             else {
 898                 clr_index = 0;
 899                 put_to_screen(ch);
 900             }
 901         }
 902 
 903         /* check if third character of sequence */
 904         else if (clr_index == 2) {
 905 
 906             /* last char in "ESC [ J" sequence? */
 907             if (ch == 'J') {
 908                 clear_eow();
 909                 clr_index = 0;
 910             }
 911 
 912             /* the '2' in "ESC [ 2 J" sequence? */
 913             else if (ch == '2')
 914                 clr_index++;
 915 
 916             /* not a recognized sequence, flush */
 917             else {
 918                 put_to_screen(ESC);
 919                 put_to_screen('[');
 920                 put_to_screen(ch);
 921                 clr_index = 0;
 922             }
 923         }
 924 
 925         /* the 'J' in "ESC [ 2 J" sequence */
 926         else if (clr_index == 3) {
 927             if (ch == 'J') {
 928                 clear_window();
 929                 clr_index = 0;
 930             }
 931 
 932             /* not recognized, flush */
 933             else {
 934                 put_to_screen(ESC);
 935                 put_to_screen('[');
 936                 put_to_screen('2');
 937                 put_to_screen(ch);
 938                 clr_index = 0;
 939             }
 940         }
 941 
 942         /* not recognized, flush */
 943         else {
 944             clr_index = 0;
 945             put_to_screen(ch);
 946         }
 947     }
 948 }
 949 
 950 
 951 
 952 /*^L
 953 **********************************************************************
 954 
 955   Routine:            CLEAR_EOW
 956 
 957   Function:
 958       This routine is used to handle the ANSI escape sequence "ESC [
 959   J" to clear the screen from the cursor position to the end of the
 960   screen. This routine simulates clearing to the end of the screen
 961   but does not damage the 25th line (the status line).
 962 
 963   Parameters:         NONE
 964 
 965   Returns:            NONE
 966 
 967 **********************************************************************/
 968 
 969 clear_eow()
 970 {
 971     union REGS reg, outreg;
 972     int row, col;
 973 
 974     cursor_pos(&row,&col);
 975     if (row < 24) {
 976 
 977         put_screen_str(EL);  /* display Erase Line escape sequence */
 978 
 979         if (row+1 < SCREEN_LINES) {
 980 
 981             reg.h.ah = VIDEO_SCROLL_UP_FUNC;
 982             reg.h.al = 0;   /* lines to scroll; 0 means clear scroll region */
 983             reg.h.ch = row+1;  /* start on next line */
 984             reg.h.cl = 0;      /* left most column */
 985             reg.h.dh = SCREEN_LINES-1;    /* to end of screen */
 986             reg.h.dl = SCREEN_COLS-1;     /* to last column */
 987             reg.h.bh = wst_fg_screen.attr;
 988             int86(BIOS_VIDEO,&reg,&outreg);
 989 
 990         }
 991     }
 992     cursor_move(row,col);
 993 }
 994 
 995 
 996 
 997 /*^L
 998 **********************************************************************
 999 
1000   Routine:            CLEAR_WINDOW
1001 
1002   Function:
1003       This routine is called to clear the screen and to initialize
1004   the global screen information variables accordingly. The contents
1005   of the 25th line (status line) remains untouched.
1006 
1007   Parameters:         NONE
1008 
1009   Returns:            NONE
1010 
1011 **********************************************************************/
1012 
1013 clear_window()
1014 {
1015     union REGS reg, outreg;
1016 
1017     reg.h.ah = VIDEO_SCROLL_UP_FUNC;
1018     reg.h.al = 0;  /* line to scroll, 0 means clear scroll region */
1019     reg.x.cx = 0;  /* home row and column coordinates */
1020     reg.h.dh = SCREEN_LINES-1;   /* end of screen row and column coordinates */
1021     reg.h.dl = SCREEN_COLS-1;
1022     reg.h.bh = wst_fg_screen.attr;
1023 
1024     int86(BIOS_VIDEO,&reg,&outreg);
1025     cursor_move(CURSOR_HOME_ROW,CURSOR_HOME_COL);
1026     screen.curcol = CURSOR_HOME_COL;
1027     screen.curlin = CURSOR_HOME_ROW;
1028 }
1029 
1030 
1031 
1032 /*^L
1033 **********************************************************************
1034 
1035   Routine:            SCROLL
1036 
1037   Function:
1038       This function manages the advancement of the display down the
1039   screen.
1040 
1041   Parameters:         NONE
1042 
1043   Returns:            NONE
1044 
1045 **********************************************************************/
1046 
1047 scroll ()
1048 
1049 {
1050     union regs_struct regs, outregs;
1051 
1052     /* Clear local action character */
1053 
1054     /*  action = NUL;*/
1055 
1056     /* If we're at the bottom of the main screen area, scroll up one line */
1057 
1058     if (screen.curlin >= MAX_SCREEN_LINE) {
1059         wst_scroll();
1060 
1061         regs.hreg.ah = SET_CURSOR_POS;      /* Position cursor at blank line */
1062         regs.hreg.bh = get_active_page();   /* in current active page */
1063         regs.xreg.dx = LINE24_COL0;
1064         int86 (BIOS_VIDEO, &regs, &outregs);
1065 
1066     }
1067 
1068     /* Otherwise, emit NL and count a screen line */
1069     else {
1070         putscr (nl, 2);
1071         screen.curlin++;
1072         screen.curcol = 0;
1073 
1074     }
1075 
1076     if (mowse_active && wst_paging)           /* Count an EOP line, too */
1077         screen.EOP_ct++;
1078     ds.pstrl = 0;
1079     setmem (ds.pstr, sizeof (ds.pstr), NUL);
1080 
1081     /* If mowse is attached, do local EOP processing */
1082 
1083     if (mowse_active) {
1084         if (screen.EOP_ct == screen.maxlin && screen.maxlin > 0) {
1085             putscr ("EOP", 3);
1086 
1087             /* Wait for a character from the keyboard */
1088             screen.EOP_ct = 0;
1089             while (checkkey() < 0);
1090             if (checkkey() == ALT_B || checkkey() == BREAK_KEY)
1091                 return;
1092             getkey(BLOCK);
1093             put_to_screen(CR);
1094             put_to_screen(LF);
1095         }
1096     }
1097 }                                       /* End of scroll */
1098 
1099 
1100 
1101 /*^L
1102 **********************************************************************
1103 
1104   Routine:            FLUSH_DOS_KEYS
1105 
1106   Function:
1107       This routine discards any keys pending in DOS's keyboard
1108   buffer.
1109 
1110   Parameters:         NONE
1111 
1112   Returns:            NONE
1113 
1114 **********************************************************************/
1115 
1116 flush_dos_keys()
1117 {
1118     union REGS r, outr;
1119 
1120     r.h.ah = BIOS_KB_STATUS;
1121 
1122     /* while key in keyboard buffer, get the key from keyboard
1123        buffer */
1124     while (!(int86(BIOS_KB,&r,&outr) & Z_FLAG_MASK)) {
1125         r.h.ah = BIOS_KB_READ;
1126         int86(BIOS_KB,&r,&outr);
1127         r.h.ah = BIOS_KB_STATUS;
1128     }
1129 }
1130 
1131 
1132 
1133 /*^L
1134 **********************************************************************
1135 
1136   Routine:            STATUS_ERR_MSG
1137 
1138   Function:
1139       This routine displays an error message on the status line and
1140   waits for any key hit to resume.
1141 
1142   Parameters:
1143      (input)          msg - the null-terminated string containing the
1144                           error message to display
1145 
1146   Returns:            NONE
1147 
1148 **********************************************************************/
1149 
1150 status_err_msg(msg)
1151 char *msg;
1152 {
1153     int row, col;
1154 
1155     cursor_pos(&row,&col);
1156     cursor_move(HIDE_CURSOR_ROW,HIDE_CURSOR_COL);
1157     status_line(msg);
1158     wait_for_key();
1159     cursor_move(row,col);
1160 }
1161 
1162 
1163 /* End of WSTUTIL */