1 /* ********************************************
   2    *                                          *
   3    * Copyright, (C) Honeywell Bull Inc., 1988 *
   4    *                                          *
   5    ******************************************** */
   6 
   7 /* HISTORY COMMENTS:
   8   1) change(88-07-11,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
   9      install(88-10-12,MR12.2-1160):
  10      Created.
  11   2) change(88-07-25,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
  12      install(88-10-12,MR12.2-1160):
  13      Documentation additions only. Added header comments to all routines.
  14   3) change(88-08-09,Lee), approve(88-05-16,MCR7897), audit(88-09-19,Flegel),
  15      install(88-10-12,MR12.2-1160):
  16      Fix references to include files; "wstdefs.h" was split into
  17      "wstdefs.h" and "wsttype.h" to avoid declaration clash; also,
  18      various constants defined to descriptive names.
  19   4) change(89-01-18,Lee), approve(89-01-02,MCR8043), audit(89-03-14,Flegel),
  20      install(89-04-24,MR12.3-1033):
  21      phx21233 - Updated flags to control visibility of chars redrawn, depending
  22      on state of keyboard echoing, for lines fetched from history buffer.
  23                                                    END HISTORY COMMENTS */
  24 
  25 #include <dos.h>
  26 #include "wstdefs.h"
  27 #include "wstglob.h"
  28 #include "wsthist.h"
  29 
  30 /*^L
  31 **********************************************************************
  32 
  33   Routine:            HISTORY_SCREEN
  34 
  35   Function:
  36       This routine enters the history screen, allowing several
  37   several options. These include displaying the next page (if enough
  38   history items exist), the previous page, shifting the screen left
  39   or right to allow viewing of a long history command, selecting a
  40   history command or quitting the history screen.
  41 
  42   Parameters:
  43      (output)         cur_scr - specifies the screen buffer to use
  44                           for saving the screen contents to before
  45                           the history screen is displayed
  46 
  47   Returns:            NONE
  48 
  49 **********************************************************************/
  50 
  51 history_screen(cur_scr)
  52 SCREEN *cur_scr;
  53 {
  54     int ch;
  55     ITEM_INFO item;
  56     int offset;
  57     int code;
  58     char line[SCREEN_COLS+2];
  59     char stat_mess[SCREEN_COLS+1];
  60     int selection;
  61 
  62     /* save the contents of the screen to the user specified buffer */
  63     save_wst_screen(cur_scr);
  64 
  65     /* hide the cursor */
  66     cursor_move(HIDE_CURSOR_ROW,HIDE_CURSOR_COL);
  67 
  68     ch = NONE;
  69     offset = HIST_DEFAULT_0_ARG;
  70 
  71     /* clear the temporary screen buffer */
  72     wst_screen_clear(&wst_tmp_screen);
  73     wst_screen_printline(&wst_tmp_screen,"");
  74 
  75     /* initialize structure for displaying history items */
  76     init_history_display(&item);
  77 
  78     /* loop until user enters 'q' to quit or no more to display */
  79     while (uppercase(ch) != 'Q' && item.start_chars_in_buff > NONE) {
  80 
  81         /* try to extract and display a history line */
  82         code = extract_hist_lines(&item,offset,line);
  83 
  84         if (code >= 0)
  85             /* write a line of help info to the temporary screen buffer */
  86             wst_screen_printline(&wst_tmp_screen,line);
  87 
  88         /* if no more history items or a pageful */
  89         if (code < 0 || item.cur_display_count >= HIST_MAX_LINES) {
  90 
  91             /* screen full, dump the contents of the temporary screen buffer */
  92             restore_wst_screen(&wst_tmp_screen);
  93 
  94             /* hide the cursor */
  95             cursor_move(HIDE_CURSOR_ROW,HIDE_CURSOR_COL);
  96 
  97             /* update the status line */
  98             if (item.cur_chars_in_buff < 1)
  99                 sprintf(stat_mess,
 100                     "HISTORY SCREEN:       offset %3d  <ALT-H>-help  [<Q>,<RETURN>,<P>,<N>,<L>,<R>]",
 101                     offset);
 102             else
 103                 sprintf(stat_mess,
 104                     "HISTORY SCREEN(more): offset %3d  <ALT-H>-help  [<Q>,<RETURN>,<P>,<N>,<L>,<R>]",
 105                     offset);
 106 
 107             status_line(stat_mess);
 108 
 109             /* wait for input from keyboard */
 110             selection = HIST_DEFAULT_0_ARG;
 111 
 112             while (TRUE) {
 113                 ch = getkey(BLOCK);
 114 
 115                 /* input is part of a numeric argument */
 116                 if (ch >= SMALLEST_DECI_DIGIT && ch <= LARGEST_DECI_DIGIT) {
 117                     if (selection >= MAX_ARG_LIMIT) {
 118                         selection = HIST_DEFAULT_0_ARG;
 119                         beep();
 120                     }
 121                     else {
 122                         selection *= 10;
 123                         selection += ch - '0';
 124                     }
 125                 }
 126 
 127                 /* shift screen right */
 128                 else if (uppercase(ch) == 'R') {
 129 
 130                     /* do only if screen is shifted left */
 131                     if (offset > NONE) {
 132 
 133                         /* examine numeric argument to determine amount to shift */
 134                         if (selection < 1) selection = 1;
 135                         if (offset >= selection)
 136                             offset -= selection;
 137                         else
 138                             offset = NONE;
 139 
 140                         /* redisplay from beginning of current page */
 141                         reset_hist_page(&item);
 142                         selection = HIST_DEFAULT_0_ARG;
 143 
 144                         /* quick check to optimize consecutive hitting
 145                            of screen shift keys; if next key buffered is
 146                            a screen shift key, just recalculate the offset
 147                            to minimize the number of screen redraws */
 148 
 149                         while (uppercase((ch = checkkey())) == 'R' ||
 150                             uppercase(ch) == 'L') {
 151                             ch = getkey(BLOCK);
 152                             if (uppercase(ch) == 'L') {
 153                                 if (offset < HIST_MAX_SCREEN_SHIFT)
 154                                     offset++;
 155                             }
 156                             else {
 157                                 if (offset > NONE)
 158                                     offset--;
 159                             }
 160                         }
 161 
 162                         break;
 163                     }
 164                     selection = HIST_DEFAULT_0_ARG;
 165                 }
 166 
 167                 /* shift screen left */
 168                 else if (uppercase(ch) == 'L') {
 169 
 170                     /* do only if screen can be shift left some more */
 171                     if (offset < HIST_MAX_SCREEN_SHIFT) {
 172 
 173                         /* examine numeric argument to determine amount to shift */
 174                         if (selection < 1) selection = 1;
 175                         if (offset + selection <= HIST_MAX_SCREEN_SHIFT)
 176                             offset += selection;
 177                         else
 178                             offset = HIST_MAX_SCREEN_SHIFT;
 179 
 180                         /* redisplay from beginning of current page */
 181                         reset_hist_page(&item);
 182                         selection = HIST_DEFAULT_0_ARG;
 183 
 184                         /* quick check to optimize consecutive hitting
 185                            of screen shift keys; if next key buffered is
 186                            a screen shift key, just recalculate the offset
 187                            to minimize the number of screen redraws */
 188 
 189                         while (uppercase((ch = checkkey())) == 'L' ||
 190                             uppercase(ch) == 'R') {
 191                             ch = getkey(BLOCK);
 192                             if (uppercase(ch) == 'L') {
 193                                 if (offset < HIST_MAX_SCREEN_SHIFT)
 194                                     offset++;
 195                             }
 196                             else {
 197                                 if (offset > NONE)
 198                                     offset--;
 199                             }
 200                         }
 201 
 202                         break;
 203                     }
 204                     selection = HIST_DEFAULT_0_ARG;
 205                 }
 206 
 207                 /* handle selection of history command */
 208                 else if (ch == RETURN_KEY && selection > NONE) {
 209 
 210                     /* if no such command */
 211                     if (fetch_nth_command(selection,
 212                         edlin.line, &edlin.length) < 0) {
 213                         selection = HIST_DEFAULT_0_ARG;
 214                     }
 215 
 216                     /* command successfully fetched in edlin.line */
 217                     else {
 218                         /* set flag to break out of outer loop */
 219                         ch = 'Q';
 220                         /* break out of first loop */
 221                         break;
 222                     }
 223                 }
 224 
 225                 /* display next history page */
 226                 else if (ch == ' ' || uppercase(ch) == 'N') {
 227                     selection = HIST_DEFAULT_0_ARG;
 228                     if (next_hist_page(&item) >= 0)
 229                         break;
 230                     else
 231                         beep();
 232                 }
 233 
 234                 /* display previous history page */
 235                 else if (uppercase(ch) == 'P') {
 236                     selection = HIST_DEFAULT_0_ARG;
 237                     if (previous_hist_page(&item) >= 0)
 238                         break;
 239                     else
 240                         beep();
 241                 }
 242 
 243                 /* quit from history screen */
 244                 else if (uppercase(ch) == 'Q') {
 245                     /* set to 0 to indicate no history line selected */
 246                     selection = HIST_DEFAULT_0_ARG;
 247                     break;
 248                 }
 249 
 250                 /* display help information */
 251                 else if (ch == ALT_H) {
 252                     help(HISTORY_HELP);
 253                     status_line(stat_mess);
 254                 }
 255 
 256                 /* unrecognized command, beep() */
 257                 else {
 258                     selection = HIST_DEFAULT_0_ARG;
 259                     beep();
 260                 }
 261             }
 262 
 263             /* clear screen buffer and leave blank line at top */
 264             wst_screen_clear(&wst_tmp_screen);
 265             wst_screen_printline(&wst_tmp_screen,"");
 266         }
 267     }
 268 
 269     /* restore contents of original screen and turn cursor back on */
 270     restore_wst_screen(cur_scr);
 271 
 272     /* history command selected, erase previously displayed command
 273        and draw the new one
 274     */
 275     if (selection > NONE) {
 276         /* phx21233 R.L. - update flags to determine visibility of chars redrawn */
 277         update_echo_flags(&edlin);
 278 
 279         erase_edit_line(&edlin);
 280 
 281         /* cursor at beginning of line */
 282         edlin.index = ZERO_INDEX_VALUE;
 283         /* reset escape flag and argument */
 284         edlin.escape_flag = NO_ESC_FUNC;
 285         edlin.escape_arg = NO_ESC_ARG;
 286         redraw_edit_line(&edlin);
 287         cursor_eol(&edlin);
 288     }
 289 }
 290 
 291 
 292 
 293 /*^L
 294 **********************************************************************
 295 
 296   Routine:            SAVE_TO_HISTBUFF
 297 
 298   Function:
 299       This routine saves a command to the history buffer.
 300 
 301   Parameters:
 302      (input)          str - specifies the buffer containing the
 303                           command
 304      (input)          item_size - specifies the number of bytes in
 305                           the command to be saved
 306 
 307   Returns:            NONE
 308 
 309 **********************************************************************/
 310 
 311 save_to_histbuff(str,item_size)
 312 char *str;
 313 int item_size;
 314 {
 315     int i, j;
 316     int lo_byte;
 317     int hi_byte;
 318     int first_chunk;
 319     int buffer_empty;
 320 
 321     /* if item cannot fit into hist buffer */
 322     if (item_size+TOTAL_SIZE_SPEC_BYTES >= HIST_BUFF_SIZE) {
 323         beep();
 324         return;
 325     }
 326 
 327     /* delete items from tail until enough room */
 328     while (hbi.chars_free < item_size+TOTAL_SIZE_SPEC_BYTES) {
 329 
 330         i = (hbi.tail + hbi.tail_item_size) % HIST_BUFF_SIZE;
 331 
 332         /* if insertion will overwrite all existing items */
 333         if (i == hbi.head && hbi.chars_free == NONE) {
 334             /* re-initialize; otherwise, corrupted values will be used */
 335             init_histbuff();
 336             break;
 337         }
 338 
 339         /* update pointer to tail of circular buffer */
 340         hbi.tail = i;
 341 
 342         /* get size of item stored in first two bytes of item */
 343         lo_byte = hbi.kb[i];
 344         i = (i+1)%HIST_BUFF_SIZE;
 345         hi_byte = hbi.kb[i];
 346 
 347         /* update amount of chars freed */
 348         hbi.chars_free += hbi.tail_item_size;
 349 
 350         /* update size of item pointed to by tail pointer */
 351         hbi.tail_item_size = (hi_byte * MAX_7_BIT_VAL) + lo_byte;
 352     }
 353 
 354     /* determine if the history buffer is empty */
 355     if (hbi.head == hbi.tail && hbi.chars_free == HIST_BUFF_SIZE)
 356         buffer_empty = TRUE;
 357     else
 358         buffer_empty = FALSE;
 359 
 360     /* update size of item pointed to by head pointer */
 361     hbi.head_item_size = item_size+TOTAL_SIZE_SPEC_BYTES;
 362     /* update buffer space left after adding this new item */
 363     hbi.chars_free -= hbi.head_item_size;
 364 
 365     /* encode the size of new item into two bytes */
 366     lo_byte = hbi.head_item_size % MAX_7_BIT_VAL;
 367     hi_byte = hbi.head_item_size / MAX_7_BIT_VAL;
 368 
 369     /* copy size of items as first two bytes of item */
 370     i = hbi.head;
 371     hbi.kb[i] = lo_byte;
 372     i = (i+1)%HIST_BUFF_SIZE;
 373     hbi.kb[i] = hi_byte;
 374     i = (i+1)%HIST_BUFF_SIZE;
 375 
 376     /* copy across end boundary of buffer to beginning of buffer? */
 377     if (i+item_size > HIST_BUFF_SIZE) {
 378 
 379         /* determine amount to copy up to end of buffer */
 380         first_chunk = HIST_BUFF_SIZE - i;
 381         /* determine amount to copy starting from beginning of buffer */
 382         item_size -= first_chunk;
 383 
 384         /* copy to history buffer */
 385         for (j = ZERO_INDEX_VALUE; j < first_chunk; j++)
 386             hbi.kb[i++] = str[j];
 387         for (i = ZERO_INDEX_VALUE; i < item_size; i++)
 388             hbi.kb[i] = str[j++];
 389 
 390     }
 391 
 392     /* does not wrap around history buffer, do straight copy */
 393     else {
 394         for (j = ZERO_INDEX_VALUE; j < item_size; j++)
 395             hbi.kb[i++] = str[j];
 396     }
 397 
 398     /* last two bytes of item must also hold the size of the item */
 399     i = i % HIST_BUFF_SIZE;
 400     hbi.kb[i] = lo_byte;
 401     i = (i+1) % HIST_BUFF_SIZE;
 402     hbi.kb[i] = hi_byte;
 403     i = (i+1) % HIST_BUFF_SIZE;
 404 
 405     /* if buffer was empty, make tail and head the same */
 406     if (buffer_empty) {
 407         hbi.tail = hbi.head;
 408         hbi.tail_item_size = hbi.head_item_size;
 409     }
 410     /* head pointer to next location to add */
 411     hbi.head = i;
 412 
 413     /* reset the current pointer to head on save to history buffer */
 414     hbi.current = hbi.head;
 415     hbi.current_size = hbi.head_item_size;
 416     hbi.head_of_histbuff = TRUE;
 417 
 418     /* reset direction flag */
 419     hbi.direction = HIST_START_DIR;
 420 
 421 }
 422 
 423 
 424 
 425 /*^L
 426 **********************************************************************
 427 
 428   Routine:            INIT_HISTBUFF
 429 
 430   Function:
 431       This routine initializes all fields in the history buffer
 432   structure to indicate an empty history buffer.
 433 
 434   Parameters:         NONE
 435 
 436   Returns:            NONE
 437 
 438 **********************************************************************/
 439 
 440 init_histbuff()
 441 {
 442 
 443     /* all indices point to beginning of history buffer */
 444     hbi.head = ZERO_INDEX_VALUE;
 445     hbi.tail = ZERO_INDEX_VALUE;
 446     hbi.current = ZERO_INDEX_VALUE;
 447 
 448     /* all items have zero size in empty history buffer */
 449     hbi.head_item_size = ZERO_BYTES;
 450     hbi.tail_item_size = ZERO_BYTES;
 451     hbi.current_size = ZERO_BYTES;
 452 
 453     /* chars free is same as size of history buffer */
 454     hbi.chars_free = HIST_BUFF_SIZE;
 455 
 456     /* currently at head of history buffer */
 457     hbi.head_of_histbuff = TRUE;
 458 
 459     /* no particular direction */
 460     hbi.direction = HIST_START_DIR;
 461 }
 462 
 463 
 464 
 465 /*^L
 466 **********************************************************************
 467 
 468   Routine:            INIT_HISTORY_DISPLAY
 469 
 470   Function:
 471       This routine initializes the structure used to control history
 472   screen displaying.
 473 
 474   Parameters:
 475      (output)         item - specifies the structure to initialize
 476 
 477   Returns:            NONE
 478 
 479 **********************************************************************/
 480 
 481 init_history_display(item)
 482 ITEM_INFO *item;
 483 {
 484 
 485     /* unique number associated with each history command */
 486     item->start_item_no = ZERO_BYTES;
 487 
 488     /* chars in the history buffer, displaying ends when no more chars */
 489     item->start_chars_in_buff = HIST_BUFF_SIZE - hbi.chars_free;
 490 
 491     /* index into history buffer of what item to display next */
 492     item->start_item_index = hbi.head;
 493 
 494     /* count of how many items displayed in current page */
 495     item->start_display_count = ZERO_BYTES;
 496 
 497     /* size of item to be displayed next */
 498     item->start_size = hbi.head_item_size;
 499 
 500     /* the running values for the above starting values */
 501     item->cur_item_no = item->start_item_no;
 502     item->cur_chars_in_buff = item->start_chars_in_buff;
 503     item->cur_item_index = item->start_item_index;
 504     item->cur_display_count = item->start_display_count;
 505     item->cur_size = item->start_size;
 506 }
 507 
 508 
 509 
 510 /*^L
 511 **********************************************************************
 512 
 513   Routine:            RESET_HIST_PAGE
 514 
 515   Function:
 516       This routine resets the history display control structure for
 517   redisplaying the current history page.
 518 
 519   Parameters:
 520      (input/output)   item - specifies the history display control
 521                           structure to use
 522 
 523   Returns:            0 always
 524 
 525 **********************************************************************/
 526 
 527 reset_hist_page(item)
 528 ITEM_INFO *item;
 529 {
 530 
 531     /* re-assign the running values with the starting values */
 532     item->cur_item_no = item->start_item_no;
 533     item->cur_chars_in_buff = item->start_chars_in_buff;
 534     item->cur_item_index = item->start_item_index;
 535     item->cur_display_count = item->start_display_count = ZERO_BYTES;
 536     item->cur_size = item->start_size;
 537 
 538     return(0);
 539 }
 540 
 541 
 542 
 543 /*^L
 544 **********************************************************************
 545 
 546   Routine:            NEXT_HIST_PAGE
 547 
 548   Function:
 549       This routine sets the history display control structure for
 550   displaying the next history page.
 551 
 552   Parameters:
 553      (input/output)   item - specifies the history display control
 554                           structure to use
 555 
 556   Returns:            -1 if no items are on the next page
 557                       0 if otherwise
 558 
 559 **********************************************************************/
 560 
 561 next_hist_page(item)
 562 ITEM_INFO *item;
 563 {
 564     /* no action of nothing more to display */
 565     if (item->cur_chars_in_buff < 1)
 566         return(-1);
 567 
 568     /* starting values are assigned results of previous running values */
 569     item->start_item_no = item->cur_item_no;
 570     item->start_chars_in_buff = item->cur_chars_in_buff;
 571     item->start_item_index = item->cur_item_index;
 572     item->start_display_count = item->cur_display_count = ZERO_BYTES;
 573     item->start_size = item->cur_size;
 574 
 575     return(0);
 576 }
 577 
 578 
 579 /*^L
 580 **********************************************************************
 581 
 582   Routine:            PREVIOUS_HIST_PAGE
 583 
 584   Function:
 585       This routine sets the history display control structure for
 586   displaying the previous history page.
 587 
 588   Parameters:
 589      (input/output)   item - specifies the history display control
 590                           structure to use
 591 
 592   Returns:            -1 if no items are in previous page
 593                       0 otherwise
 594 
 595 **********************************************************************/
 596 
 597 previous_hist_page(item)
 598 ITEM_INFO *item;
 599 {
 600     int i;
 601     int hi, lo;
 602     int cnt;
 603     int times;
 604 
 605     /* no previous page if current page does not start past last line
 606        of first page
 607     */
 608     if (item->start_item_no < HIST_MAX_LINES)
 609         return(-1);
 610 
 611     /* traverse backward through history buffer to update item fields */
 612     for (times = 0; times < HIST_MAX_LINES; times++) {
 613 
 614         /* update pointer to next item to display */
 615         i = item->start_item_index;
 616 
 617         /* extract size of item from kill buffer */
 618         lo = hbi.kb[i];
 619         i = (i+1)%HIST_BUFF_SIZE;
 620         hi = hbi.kb[i];
 621         i = (i+1)%HIST_BUFF_SIZE;
 622 
 623         cnt = (hi * MAX_7_BIT_VAL) + lo;
 624 
 625         /* decrement item index circularly by size */
 626         item->start_item_index = (item->start_item_index + cnt)%HIST_BUFF_SIZE;
 627 
 628         /* decrement id number of history command item */
 629         item->start_item_no--;
 630 
 631         /* tally characters to be displayed in history buffer */
 632         item->start_chars_in_buff += cnt;
 633 
 634         /* update item size */
 635         item->start_size = cnt;
 636 
 637     }
 638 
 639     /* current values are same as starting values */
 640     item->cur_item_index = item->start_item_index;
 641     item->cur_item_no = item->start_item_no;
 642     item->cur_chars_in_buff = item->start_chars_in_buff;
 643     item->cur_display_count = item->start_display_count = ZERO_BYTES;
 644     item->cur_size = item->start_size;
 645 
 646     return(0);
 647 }
 648 
 649 
 650 
 651 /*^L
 652 **********************************************************************
 653 
 654   Routine:            EXTRACT_HIST_LINES
 655 
 656   Function:
 657       This function extracts commands from the history buffer and
 658   copies it into a string. The length of the string will be at most,
 659   the width of the screen. If a command exceeds the width of the
 660   screen, the string will be terminated by a '$' to indicate there is
 661   more that is not shown. A value is specified to allow portions of
 662   long commands to be displayed starting from an offset from the
 663   beginning of the command.
 664 
 665   Parameters:
 666      (input)          item - structure containing information as to
 667                           which item in the history buffer to extract
 668      (input)          offset - specifies the offset from the
 669                           beginning of the line to begin extracting
 670      (output)         line - pointer to the string to pass back the
 671                           history command; if the length of the
 672                           command is less than the value of 'offset',
 673                           an empty string is passed back
 674 
 675   Returns:            -1 if unsuccessful
 676                       0 if successful
 677 
 678 **********************************************************************/
 679 
 680 extract_hist_lines(item,offset,line)
 681 ITEM_INFO *item;
 682 int offset;
 683 char *line;
 684 {
 685     int i, j;
 686     int lo, hi;
 687     int cnt;
 688     int linelen;
 689     int previous;
 690 
 691     /* entire buffer is free, nothing in buffer */
 692     if (item->cur_chars_in_buff < 1)
 693         return(-1);
 694 
 695     /* get index to next item to extract */
 696     i = item->cur_item_index;
 697 
 698     /* check if no more previous items */
 699     if (i == hbi.tail && !hbi.head_of_histbuff)
 700         return(-1);
 701 
 702     /* skip over current item and point to previous item */
 703     previous = (item->cur_item_index + HIST_BUFF_SIZE - item->cur_size)
 704                % HIST_BUFF_SIZE;
 705     i = previous;
 706 
 707     /* if history buffer is not empty */
 708     if (item->cur_chars_in_buff > NONE) {
 709 
 710         /* determine the size of this previous item */
 711         lo = hbi.kb[i];
 712         i = (i+1)%HIST_BUFF_SIZE;
 713         hi = hbi.kb[i];
 714         i = (i+1)%HIST_BUFF_SIZE;
 715 
 716         cnt = (hi * MAX_7_BIT_VAL) + lo;
 717 
 718         /* 4 bytes are used store history item size; subtract to    */
 719         /* get command length                                       */
 720         cnt -= TOTAL_SIZE_SPEC_BYTES;
 721         item->cur_chars_in_buff -= TOTAL_SIZE_SPEC_BYTES;
 722 
 723         /* format line number into resulting line */
 724         sprintf(line,"%3d  ", item->cur_item_no+1);
 725         linelen = strlen(line);
 726 
 727         /* copy the history command if offset does not exceed       */
 728         /* command's length                                         */
 729         if (cnt > offset) {
 730 
 731             /* index into specified offset of history command */
 732             i = (i+offset) % HIST_BUFF_SIZE;
 733             item->cur_chars_in_buff -= offset;
 734             cnt -= offset;
 735 
 736             /* copy the command a byte at a time */
 737             for (j = 0; j < cnt; j++) {
 738 
 739                 /* make sure copies only up to screen width in      */
 740                 /* length                                           */
 741                 if (linelen < SCREEN_COLS) {
 742 
 743                     /* display control character using ^ notation   */
 744                     /* (e.g. NUL is ^@)                             */
 745                     if (hbi.kb[i] < ' ') {
 746                         line[linelen++] = '^';
 747                         line[linelen++] = hbi.kb[i] + '@';
 748                     }
 749                     else {
 750                         line[linelen++] = hbi.kb[i];
 751                     }
 752                 }
 753 
 754                 i = (i+1)%HIST_BUFF_SIZE;
 755                 item->cur_chars_in_buff--;
 756             }
 757         }
 758 
 759         else {
 760             i = (i+cnt) % HIST_BUFF_SIZE;
 761             item->cur_chars_in_buff -= cnt;
 762         }
 763 
 764         /* overwrite last character of line with '$' if greater     */
 765         /* than screen width                                        */
 766         if (linelen > SCREEN_COLS-1) {
 767             line[SCREEN_COLS-1] = '$';
 768             line[SCREEN_COLS] = NUL_TERMINATOR;
 769         }
 770         else
 771             line[linelen] = NUL_TERMINATOR;
 772 
 773         /* tally item and update item index in history display      */
 774         /* control structure                                        */
 775         item->cur_item_no++;
 776         item->cur_item_index = previous;
 777         item->cur_display_count++;
 778 
 779         /* update item size field if not last item in history       */
 780         /* buffer                                                   */
 781         if (previous != hbi.tail) {
 782             previous = (previous + HIST_BUFF_SIZE - N_SIZE_SPEC_BYTES)
 783                 % HIST_BUFF_SIZE;
 784             lo = hbi.kb[previous];
 785             previous = (previous+1) % HIST_BUFF_SIZE;
 786             hi = hbi.kb[previous];
 787             item->cur_size = (hi * MAX_7_BIT_VAL) + lo;
 788         }
 789 
 790         return(0);
 791     }
 792     return(-1);
 793 }
 794 
 795 
 796 
 797 /*^L
 798 **********************************************************************
 799 
 800   Routine:            FETCH_NEXT_COMMAND
 801 
 802   Function:
 803       This routine allows the next command in the history buffer
 804   (entered chronologically) to be fetched from the history buffer.
 805 
 806   Parameters:
 807      (input)          n_times - specifies the number of times to
 808                           repeat the operation
 809      (output)         cmd - specifies the target string for placing
 810                           the extracted command
 811      (output)         cmd_len - specifies the integer for passing
 812                           back the length of the fetched command
 813 
 814   Returns:            -1 if unsuccessful
 815                       0 otherwise
 816 
 817   Note 1 - if an error occurs, this routine will sound a beep.
 818 **********************************************************************/
 819 
 820 fetch_next_command(n_times,cmd,cmd_len)
 821 int n_times;
 822 char *cmd;
 823 int *cmd_len;
 824 {
 825     int i, j;
 826     int cnt;
 827     int lo, hi;
 828     int times;
 829     int prev_index;
 830 
 831     /* if nothing in history buffer */
 832     if (hbi.chars_free == HIST_BUFF_SIZE)
 833         return(-1);
 834 
 835     prev_index = (hbi.head+HIST_BUFF_SIZE-hbi.head_item_size) % HIST_BUFF_SIZE;
 836 
 837     /* if changing directions and not single item in history buffer */
 838     if (hbi.direction == HIST_PREVIOUS_DIR && prev_index != hbi.tail)
 839         n_times++;
 840 
 841     hbi.direction = HIST_NEXT_DIR;
 842 
 843     for (times = 0; times < n_times; times++) {
 844 
 845         if (hbi.current == hbi.head) {
 846             hbi.head_of_histbuff = TRUE;
 847             beep();
 848             return(-1);
 849         }
 850 
 851         i = hbi.current;
 852 
 853         lo = hbi.kb[i];
 854         i = (i+1)%HIST_BUFF_SIZE;
 855         hi = hbi.kb[i];
 856         i = (i+1)%HIST_BUFF_SIZE;
 857 
 858         cnt = (hi * MAX_7_BIT_VAL) + lo;
 859         hbi.current_size = cnt;
 860         cnt -= TOTAL_SIZE_SPEC_BYTES;
 861 
 862         /* do modify parameters only last time through loop */
 863         if (times == n_times-1) {
 864             *cmd_len = cnt;
 865             for (j = 0; j < cnt; j++) {
 866                 cmd[j] = hbi.kb[i];
 867                 i = (i+1)%HIST_BUFF_SIZE;
 868             }
 869         }
 870         /* otherwise just skip this particular item */
 871         else
 872             i = (i+cnt)%HIST_BUFF_SIZE;
 873 
 874         i = (i+1)%HIST_BUFF_SIZE;
 875         i = (i+1)%HIST_BUFF_SIZE;
 876 
 877         hbi.current = i;
 878     }
 879 
 880     return(0);
 881 }
 882 
 883 
 884 
 885 /*^L
 886 **********************************************************************
 887 
 888   Routine:            FETCH_PREVIOUS_COMMAND
 889 
 890   Function:
 891       This routine allows the previous command in the history buffer
 892   (reversed chronologically) to be fetched from the history buffer.
 893 
 894   Parameters:
 895      (input)          n_times - specifies the number of times to
 896                           repeat the operation
 897      (output)         cmd - specifies the target string for placing
 898                           the fetched command
 899      (output)         cmd_len - specifies the integer for passing
 900                           back the length of the fetched command
 901 
 902   Returns:            -1 if unsuccessful
 903                       0 otherwise
 904 
 905   Note 1 - If an error occurs, this routine will sound a beep.
 906 **********************************************************************/
 907 
 908 fetch_previous_command(n_times,cmd,cmd_len)
 909 int n_times;
 910 char *cmd;
 911 int *cmd_len;
 912 {
 913     int i, j;
 914     int cnt;
 915     int lo, hi;
 916     int previous;
 917     int times;
 918     int prev_index;
 919 
 920     /* nothing in history buffer */
 921     if (hbi.chars_free == HIST_BUFF_SIZE)
 922         return(-1);
 923 
 924     prev_index = (hbi.head+HIST_BUFF_SIZE-hbi.head_item_size) % HIST_BUFF_SIZE;
 925 
 926     /* if change of directions while extracting, then must extract  */
 927     /* an extra to prevent the current item to be refetched;        */
 928     /* check for direction change and not last item in history      */
 929     /* buffer                                                       */
 930     if (hbi.direction == HIST_NEXT_DIR && prev_index != hbi.tail)
 931         n_times++;
 932 
 933     hbi.direction = HIST_PREVIOUS_DIR;
 934 
 935     /* perform extraction specified number of times */
 936     for (times = 0; times < n_times; times++) {
 937 
 938         /* check if at last item */
 939         if (hbi.current == hbi.tail && !hbi.head_of_histbuff) {
 940             beep();
 941             return(-1);
 942         }
 943 
 944         /* index to previous item */
 945         previous = (hbi.current + HIST_BUFF_SIZE - hbi.current_size) %
 946             HIST_BUFF_SIZE;
 947 
 948         i = previous;
 949 
 950         /* get size of previous item */
 951         lo = hbi.kb[i];
 952         i = (i+1)%HIST_BUFF_SIZE;
 953         hi = hbi.kb[i];
 954         i = (i+1)%HIST_BUFF_SIZE;
 955 
 956         cnt = (hi * MAX_7_BIT_VAL) + lo;
 957 
 958         /* subtract item size bytes to get bytes in command */
 959         cnt -= TOTAL_SIZE_SPEC_BYTES;
 960 
 961         /* do extraction only for last iteration of loop */
 962         if (times == n_times-1) {
 963             *cmd_len = cnt;
 964 
 965             /* copy from history buffer to target string */
 966             for (j = 0; j < cnt; j++) {
 967                 cmd[j] = hbi.kb[i];
 968                 i = (i+1)%HIST_BUFF_SIZE;
 969             }
 970         }
 971 
 972         /* skip index over this item */
 973         else
 974             i = (i+cnt)%HIST_BUFF_SIZE;
 975 
 976         /* update history buffer index to previous item for next    */
 977         /* fetch                                                    */
 978         hbi.current = previous;
 979         if (previous != hbi.tail) {
 980             previous = (previous + HIST_BUFF_SIZE - N_SIZE_SPEC_BYTES)
 981                 % HIST_BUFF_SIZE;
 982             lo = hbi.kb[previous];
 983             previous = (previous+1) % HIST_BUFF_SIZE;
 984             hi = hbi.kb[previous];
 985             hbi.current_size = (hi * MAX_7_BIT_VAL)+lo;
 986         }
 987 
 988         /* current is no longer at head of list, set flag to FALSE */
 989         hbi.head_of_histbuff = FALSE;
 990     }
 991 
 992     return(0);
 993 }
 994 
 995 
 996 
 997 /*^L
 998 **********************************************************************
 999 
1000   Routine:            FETCH_NTH_COMMAND
1001 
1002   Function:
1003       This routine fetches the Nth command from the history buffer.
1004   Commands in the history buffer are numbered with the most recently
1005   entered command being item 1, the next most recent being item 2,
1006   etc.
1007 
1008   Parameters:
1009      (input)          cmd_num - specifies the number of the history
1010                           command to extract
1011      (output)         cmd - specifies the target string to copy the
1012                           fetched history command to
1013      (output)         cmd_len - pointer to the integer for passing
1014                           back the length of the fetched command
1015 
1016   Returns:            -1 if unsuccessful
1017                       0 otherwise
1018 
1019 **********************************************************************/
1020 
1021 fetch_nth_command(cmd_num,cmd,cmd_len)
1022 int cmd_num;
1023 char *cmd;
1024 int *cmd_len;
1025 {
1026     int i, j;
1027     int cnt;
1028     int lo, hi;
1029     int previous;
1030     int times;
1031     int at_head;
1032     int cur_index;
1033     int cur_size;
1034 
1035     /* nothing in history buffer */
1036     if (hbi.chars_free == HIST_BUFF_SIZE)
1037         return(-1);
1038 
1039     /* initialize to start of history buffer */
1040     cur_index = hbi.head;
1041     cur_size = hbi.head_item_size;
1042     at_head = hbi.head_of_histbuff;
1043 
1044     /* loop specified number of times to get specified command */
1045     for (times = ZERO_INDEX_VALUE; times < cmd_num; times++) {
1046 
1047         /* if at end of history buffer */
1048         if (cur_index == hbi.tail && !at_head) {
1049             beep();
1050             return(-1);
1051         }
1052 
1053         /* index to previous item in circular buffer */
1054         previous = (cur_index + HIST_BUFF_SIZE - cur_size) %
1055             HIST_BUFF_SIZE;
1056         i = previous;
1057 
1058         /* get size of previous item */
1059         lo = hbi.kb[i];
1060         i = (i+1)%HIST_BUFF_SIZE;
1061         hi = hbi.kb[i];
1062         i = (i+1)%HIST_BUFF_SIZE;
1063 
1064         cnt = (hi * MAX_7_BIT_VAL) + lo;
1065 
1066         /* subtract size storage bytes to get command size */
1067         cnt -= TOTAL_SIZE_SPEC_BYTES;
1068 
1069         *cmd_len = cnt;
1070 
1071         /* fetch the command */
1072         for (j = ZERO_INDEX_VALUE; j < cnt; j++) {
1073             cmd[j] = hbi.kb[i];
1074             i = (i+1)%HIST_BUFF_SIZE;
1075         }
1076 
1077         /* update current index to previous item for next time thru loop */
1078         cur_index = previous;
1079 
1080         /* update item size if no end of history buffer */
1081         if (previous != hbi.tail) {
1082             previous = (previous + HIST_BUFF_SIZE - N_SIZE_SPEC_BYTES)
1083                 % HIST_BUFF_SIZE;
1084             lo = hbi.kb[previous];
1085             previous = (previous+1) % HIST_BUFF_SIZE;
1086             hi = hbi.kb[previous];
1087             cur_size = (hi * MAX_7_BIT_VAL)+lo;
1088         }
1089 
1090         /* no longer at start of history buffer */
1091         at_head = FALSE;
1092     }
1093 
1094     /* update history information */
1095     hbi.current = cur_index;
1096     hbi.current_size = cur_size;
1097     hbi.head_of_histbuff = at_head;
1098     hbi.direction = HIST_PREVIOUS_DIR;
1099 
1100     return(0);
1101 }
1102