home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / SH162_2S.ZIP / SH9.C < prev    next >
C/C++ Source or Header  |  1990-08-04  |  31KB  |  1,437 lines

  1. /* MS-DOS SHELL - History Processing
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited
  4.  *
  5.  * This code is subject to the following copyright restrictions:
  6.  *
  7.  * 1.  Redistribution and use in source and binary forms are permitted
  8.  *     provided that the above copyright notice is duplicated in the
  9.  *     source form and the copyright notice in file sh6.c is displayed
  10.  *     on entry to the program.
  11.  *
  12.  * 2.  The sources (or parts thereof) or objects generated from the sources
  13.  *     (or parts of sources) cannot be sold under any circumstances.
  14.  *
  15.  *    $Header: sh9.c 1.12 90/05/31 10:39:26 MS_user Exp $
  16.  *
  17.  *    $Log:    sh9.c $
  18.  * Revision 1.12  90/05/31  10:39:26  MS_user
  19.  * Initialise the input buffer in case of interrupts
  20.  *
  21.  * Revision 1.11  90/03/27  20:22:07  MS_user
  22.  * Fix problem with paging down history file - the last item was incorrect
  23.  *
  24.  * Revision 1.10  90/03/26  04:10:53  MS_user
  25.  * Scan_History uses the Match length and not the string length for matching
  26.  *
  27.  * Revision 1.9  90/03/21  14:05:26  MS_user
  28.  * History search sometimes includes the optionals in the search string.
  29.  *
  30.  * Revision 1.8  90/03/14  13:23:48  MS_user
  31.  * Change names of configuration fields to reflect function
  32.  *
  33.  * Revision 1.7  90/03/13  18:36:07  MS_user
  34.  * Add initialisation file processing
  35.  *
  36.  * Revision 1.6  90/03/09  16:07:40  MS_user
  37.  * Add SH_ALT_KEYS processing
  38.  * Fix bottom line processing so that cursor doesn't disappear
  39.  * Fix EGA detection so we get the correct screen size
  40.  *
  41.  * Revision 1.5  90/03/06  16:50:57  MS_user
  42.  * Add disable history option
  43.  *
  44.  * Revision 1.4  90/03/06  15:14:40  MS_user
  45.  * Complete changes for file name completion
  46.  * Add find Max Lines function
  47.  *
  48.  * Revision 1.3  90/03/05  13:54:28  MS_user
  49.  * Fix get previous command request
  50.  * Add filename completion
  51.  * Add Max Columns from BIOS
  52.  * Add cursor position check function
  53.  * Add !! option
  54.  * Change erase to end of line processing to remove ANSI.SYS dependency
  55.  *
  56.  * Revision 1.2  90/02/19  15:42:39  MS_user
  57.  * Remove dependency on ANSI.SYS
  58.  *
  59.  * Revision 1.1  90/01/26  17:25:19  MS_user
  60.  * Initial revision
  61.  *
  62.  *
  63.  */
  64.  
  65. #include <sys/types.h>
  66. #include <stdio.h>
  67. #include <conio.h>
  68. #include <string.h>
  69. #include <memory.h>
  70. #include <ctype.h>
  71. #include <signal.h>
  72. #include <stdlib.h>
  73. #include <stddef.h>
  74. #include <errno.h>
  75. #include <setjmp.h>
  76. #include <limits.h>
  77. #include <unistd.h>
  78. #include <dir.h>
  79. #include "sh.h"
  80.  
  81. #define INCL_NOPM
  82. #define INCL_VIO
  83. #include <os2.h>
  84.  
  85. /* Keyboard functions */
  86.  
  87. #define KF_LENGTH        (sizeof (KF_List) / sizeof (KF_List[0]))
  88. #define KF_SCANBACKWARD    0x00        /* Scan backwards in history    */
  89. #define KF_SCANFOREWARD    0x01        /* Scan forewards in history    */
  90. #define KF_PREVIOUS    0x02        /* Previous command        */
  91. #define KF_NEXT        0x03        /* Next command            */
  92. #define KF_LEFT        0x04        /* Left one character        */
  93. #define KF_RIGHT    0x05        /* Right one character        */
  94. #define KF_WORDRIGHT    0x06        /* Right one word        */
  95. #define KF_WORDLEFT    0x07        /* Left one word        */
  96. #define KF_START    0x08        /* Move to start of line    */
  97. #define KF_CLEAR    0x09        /* Clear input line        */
  98. #define KF_FLUSH    0x0a        /* Flush to end of line        */
  99. #define KF_END        0x0b        /* End of line            */
  100. #define KF_INSERT    0x0c        /* Insert mode switch        */
  101. #define KF_DELETERIGHT    0x0d        /* Delete right character    */
  102. #define KF_DELETELEFT    0x0e        /* Delete left character    */
  103. #define KF_COMPLETE    0x0f        /* Complete file name        */
  104. #define KF_DIRECTORY    0x10        /* Complete directory function    */
  105. #define KF_JOBS         0x11            /* Jobs list */
  106. #define KF_END_FKEYS    0x12        /* End of function keys        */
  107. #define KF_RINGBELL    0x12        /* Ring bell            */
  108. #define KF_HALFHEIGTH    0x13        /* Half height cursor        */
  109.  
  110.  
  111. /* Function Declarations */
  112.  
  113. #ifndef NO_HISTORY
  114. static bool    alpha_numeric (int);
  115. static bool    function (int);
  116. static bool    Process_History (int);
  117. static bool    Scan_History (void);
  118. static void    Redisplay_Line (void);
  119. static void    Process_Stdin (void);
  120. static void    Page_History (int);
  121. static bool    UpDate_CLine (char *);
  122. static bool    Re_start (char *);
  123. static void    memrcpy (char *, char *, int);
  124. static void    set_cursor_position (int);
  125. static void    gen_cursor_position (void);
  126. static void    erase_to_end_of_line (void);
  127. static void    set_cursor_shape (bool);
  128. static bool    Complete_file (char *, bool);
  129. static void    Init_Input (bool);
  130. #endif
  131. static void    read_cursor_position (void);
  132. static void    Get_Screen_Params (void);
  133.  
  134. static int    s_cursor;        /* Start cursor position    */
  135. static int    Max_Cols  = 80;        /* Max columns            */
  136. static int    Max_Lines = 25;        /* Max Lines            */
  137. #ifndef NO_HISTORY
  138. static bool    insert_mode = TRUE;
  139. static char    *c_buffer_pos;        /* Position in command line    */
  140. static char    *end_buffer;        /* End of command line        */
  141. static int    m_line = 0;        /* Max write line number    */
  142. static int    c_history = -1;        /* Current entry        */
  143. static int    l_history = 0;        /* End of history array        */
  144. static int    M_length = -1;        /* Match length            */
  145. static int    Max_Length = 0;        /* Max line length        */
  146. static char    l_buffer[LINE_MAX + 1];
  147. static char    *No_prehistory   = "history: No previous commands";
  148. static char    *No_MatchHistory = "history: No history match found";
  149. static char    *No_posthistory  = "history: No more commands";
  150. static char    *History_2long   = "history: History line too long";
  151. static char    *H_TooLongI = "History file line too long - ignored (%d)\n";
  152.  
  153. /* Function Key table */
  154.  
  155. static struct Key_Fun_List {
  156.     char    *kf_name;
  157.     char    akey;
  158.     char    fkey;
  159.     char    fcode;
  160. } KF_List[] = {
  161.     { "ScanBackward",    0,    'I',    KF_SCANBACKWARD },
  162.     { "ScanForeward",    0,    'Q',    KF_SCANFOREWARD },
  163.     { "Previous",    0,    'H',    KF_PREVIOUS },
  164.     { "Next",        0,    'P',    KF_NEXT },
  165.     { "Left",        0,    'K',    KF_LEFT },
  166.     { "Right",        0,    'M',    KF_RIGHT },
  167.     { "WordRight",    0,    't',    KF_WORDRIGHT },
  168.     { "WordLeft",    0,    's',    KF_WORDLEFT },
  169.     { "Start",        0,    'G',    KF_START },
  170.     { "Clear",        0x1b,    0,    KF_CLEAR },
  171.     { "Flush",        0,    'u',    KF_FLUSH },
  172.     { "End",        0,    'O',    KF_END },
  173.     { "Insert",        0,    'R',    KF_INSERT },
  174.     { "DeleteRight",    0,    'S',    KF_DELETERIGHT },
  175.     { "DeleteLeft",    0x08,    0,    KF_DELETELEFT },
  176.     { "Complete",    0x09,    0,    KF_COMPLETE },
  177.     { "Directory",    0,    0x0f,    KF_DIRECTORY },
  178.     { "Jobs",        0,    0x94,    KF_JOBS },
  179.  
  180. /* End of function keys - flags */
  181.  
  182.     { "Bell",        1,    0,    KF_RINGBELL },
  183.     { "HalfHeight",    1,    0,    KF_HALFHEIGTH }
  184. };
  185.  
  186. /* Arrary of history Items */
  187.  
  188. static struct    cmd_history {
  189.     int        number;
  190.     char    *command;
  191. } cmd_history[HISTORY_MAX];
  192.  
  193. /* Processing standard input */
  194.  
  195. int            Get_stdin (ap)
  196. register IO_Args    *ap;
  197. {
  198.     int        coff = (int)ap->afpos;
  199.     char    rv;
  200.  
  201. /* Is there anything in the input buffer.  If not, add the previous line to
  202.  * the history buffer and get the next line
  203.  */
  204.  
  205.     if (!coff)
  206.     Process_Stdin ();            /* No - get input    */
  207.  
  208. /* Get the next character */
  209.  
  210.     if ((rv = l_buffer[coff]) == NL)
  211.     {
  212.     l_buffer[coff] = 0;
  213.     ap->afpos = 0L;
  214.     }
  215.  
  216. /* Check for end of file */
  217.  
  218.     else if (rv == 0x1a)
  219.     {
  220.     l_buffer[coff] = 0;
  221.     ap->afpos = 0L;
  222.     rv = 0;
  223.     }
  224.  
  225.     else
  226.     ap->afpos++;
  227.  
  228.     return rv;
  229. }
  230.  
  231. /* Input processing function */
  232.  
  233. static void    Process_Stdin ()
  234. {
  235.     char    a_key, f_key;
  236.     int        i;
  237.  
  238. /* Set to last history item */
  239.  
  240.     c_history = l_history;
  241.     memset (l_buffer, 0, LINE_MAX + 1);
  242.  
  243. /* Process the input */
  244.  
  245.     while (TRUE)
  246.     {
  247.     Init_Input (TRUE);            /* Initialise        */
  248.  
  249.     while (((a_key = (char)getch ()) != 0x1a) && (a_key != NL) &&
  250.         (a_key != '\r'))
  251.     {
  252.  
  253. /* If function key, get the fkey value */
  254.  
  255.             if ( a_key == 0xE0 )
  256.               a_key = 0;
  257.  
  258.         if (!a_key)
  259.         f_key = (char)getch ();
  260.  
  261. /* Look up the keystroke to see if it is one of our functions */
  262.  
  263.         for (i = 0; (i < KF_END_FKEYS); ++i)
  264.         {
  265.         if (KF_List[i].akey != a_key)
  266.             continue;
  267.  
  268.         if ((a_key != 0) || (KF_List[i].fkey == f_key))
  269.             break;
  270.         }
  271.  
  272. /* If this is a function key and is not ours, ignore it */
  273.  
  274.         if ((i == KF_END_FKEYS) && (!a_key))
  275.         continue;
  276.  
  277.         if (((i == KF_END_FKEYS) ? alpha_numeric (a_key)
  278.                   : function (KF_List[i].fcode)))
  279.         Redisplay_Line ();
  280.  
  281. /* Reposition the cursor */
  282.  
  283.         gen_cursor_position ();
  284.     }
  285.  
  286. /* Terminate the line */
  287.  
  288.     *end_buffer = 0;
  289.     v1_putc (NL);
  290.     s_cursor = -1;
  291.  
  292. /* Line input - check for history */
  293.  
  294.     if ((*l_buffer == '!') && Process_History (0))
  295.     {
  296.         v1a_puts (l_buffer);
  297.         break;
  298.     }
  299.  
  300.     else if (*l_buffer != '!')
  301.         break;
  302.     }
  303.  
  304.     set_cursor_shape (FALSE);
  305.     *end_buffer = (char)((a_key == '\r') ? NL : a_key);
  306. }
  307.  
  308. /* Handler Alpha_numeric characters */
  309.  
  310. static bool    alpha_numeric (c)
  311. int        c;
  312. {
  313.     bool    redisplay = FALSE;
  314.  
  315. /* Normal character processing */
  316.  
  317.     if ((c_buffer_pos - l_buffer) == LINE_MAX)
  318.     {
  319.     v1_putc (0x07);            /* Ring bell            */
  320.     return FALSE;
  321.     }
  322.  
  323.     else if (!insert_mode)
  324.     {
  325.     if (c_buffer_pos == end_buffer)
  326.         ++end_buffer;
  327.  
  328.     else if (iscntrl (*c_buffer_pos) || iscntrl (c))
  329.         redisplay = TRUE;
  330.  
  331.     *(c_buffer_pos++) = (char)c;
  332.  
  333.     if (redisplay || (c == '\t'))
  334.         return TRUE;
  335.  
  336.     if (iscntrl (c))
  337.     {
  338.         v1_putc ('^');
  339.         c += '@';
  340.     }
  341.  
  342.     v1_putc ((char)c);
  343.     return FALSE;
  344.     }
  345.  
  346.     else if ((end_buffer - l_buffer) == LINE_MAX)
  347.     {
  348.     v1_putc (0x07);            /* Ring bell - line full    */
  349.     return FALSE;
  350.     }
  351.  
  352.     else
  353.     {
  354.     if (c_buffer_pos != end_buffer)
  355.         memrcpy (end_buffer + 1, end_buffer, end_buffer - c_buffer_pos + 1);
  356.  
  357.     ++end_buffer;
  358.     *(c_buffer_pos++) = (char)c;
  359.     return TRUE;
  360.     }
  361. }
  362.  
  363. /* Process function keys */
  364.  
  365. static bool    function (fn)
  366. int        fn;
  367. {
  368.     bool    fn_search = FALSE;
  369.  
  370.     switch (fn)
  371.     {
  372.     case KF_SCANBACKWARD:        /* Scan backwards in history    */
  373.     case KF_SCANFOREWARD:        /* Scan forewards in history    */
  374.         *end_buffer = 0;
  375.  
  376.         if (M_length == -1)
  377.         M_length = strlen (l_buffer);
  378.  
  379.         Page_History ((fn == KF_SCANBACKWARD) ? -1 : 1);
  380.         return TRUE;
  381.  
  382.     case KF_PREVIOUS:        /* Previous command        */
  383.         *end_buffer = 0;
  384.         Process_History (-1);
  385.         return TRUE;
  386.  
  387.     case KF_NEXT:            /* Next command line        */
  388.         Process_History (1);
  389.         return TRUE;
  390.  
  391.     case KF_LEFT:            /* Cursor left            */
  392.         if (c_buffer_pos != l_buffer)
  393.         --c_buffer_pos;
  394.  
  395.         else
  396.         v1_putc (0x07);
  397.  
  398.         return FALSE;
  399.  
  400.     case KF_RIGHT:            /* Cursor right            */
  401.         if (c_buffer_pos != end_buffer)
  402.         ++c_buffer_pos;
  403.  
  404.         else
  405.         v1_putc (0x07);
  406.  
  407.         return FALSE;
  408.  
  409.     case KF_WORDLEFT:        /* Cursor left a word        */
  410.         if (c_buffer_pos != l_buffer)
  411.         {
  412.         --c_buffer_pos;        /* Reposition on previous char    */
  413.  
  414.         while (isspace (*c_buffer_pos) && (c_buffer_pos != l_buffer))
  415.             --c_buffer_pos;
  416.  
  417.         while (!isspace (*c_buffer_pos) && (c_buffer_pos != l_buffer))
  418.             --c_buffer_pos;
  419.  
  420.         if (c_buffer_pos != l_buffer)
  421.             ++c_buffer_pos;
  422.         }
  423.  
  424.         else
  425.         v1_putc (0x07);
  426.  
  427.         return FALSE;
  428.  
  429.     case KF_WORDRIGHT:        /* Cursor right a word        */
  430.         if (c_buffer_pos != end_buffer)
  431.         {
  432.  
  433. /* Skip to the end of the current word */
  434.  
  435.         while (!isspace (*c_buffer_pos) && (c_buffer_pos != end_buffer))
  436.             ++c_buffer_pos;
  437.  
  438. /* Skip over the white space */
  439.  
  440.         while (isspace (*c_buffer_pos) && (c_buffer_pos != end_buffer))
  441.             ++c_buffer_pos;
  442.         }
  443.  
  444.         else
  445.         v1_putc (0x07);
  446.  
  447.         return FALSE;
  448.  
  449.     case KF_START:            /* Cursor home            */
  450.         c_buffer_pos = l_buffer;
  451.         return FALSE;
  452.  
  453.     case KF_CLEAR:            /* Erase buffer            */
  454.         c_buffer_pos = l_buffer;
  455.  
  456.     case KF_FLUSH:            /* Flush to end            */
  457.         memset (c_buffer_pos, ' ', end_buffer - c_buffer_pos);
  458.         end_buffer = c_buffer_pos;
  459.         return TRUE;
  460.  
  461.     case KF_END:            /* Cursor end of command    */
  462.         if (*l_buffer == '!')
  463.         {
  464.         *end_buffer = 0;
  465.         Process_History (2);
  466.         return TRUE;
  467.         }
  468.  
  469.         c_buffer_pos = end_buffer;
  470.         return FALSE;
  471.  
  472.     case KF_INSERT:            /* Switch insert mode        */
  473.         insert_mode = (insert_mode) ? FALSE : TRUE;
  474.         set_cursor_shape (insert_mode);
  475.         return FALSE;
  476.  
  477.     case KF_DELETERIGHT:        /* Delete right character    */
  478.         if (c_buffer_pos == end_buffer)
  479.         return FALSE;
  480.  
  481.         memcpy (c_buffer_pos, c_buffer_pos + 1, end_buffer - c_buffer_pos);
  482.  
  483.         if (end_buffer == l_buffer)
  484.         {
  485.         v1_putc (0x07);
  486.         return TRUE;
  487.         }
  488.  
  489.         if (--end_buffer < c_buffer_pos)
  490.         --c_buffer_pos;
  491.  
  492.            return TRUE;
  493.  
  494.         case KF_JOBS:
  495.         v1_putc (NL);
  496.             dojobs(NULL);
  497.         put_prompt (last_prompt);
  498.         read_cursor_position ();
  499.             return TRUE;
  500.  
  501.     case KF_DIRECTORY:        /* File name directory        */
  502.         fn_search = TRUE;
  503.  
  504.     case KF_COMPLETE:        /* File name completion        */
  505.     {
  506.         char    *fn_start = c_buffer_pos;
  507.  
  508.         *end_buffer = 0;
  509.  
  510.         if (isspace (*fn_start))
  511.         --fn_start;
  512.  
  513.         if (isspace (*fn_start) || (fn_start < l_buffer))
  514.         break;
  515.  
  516.         return Complete_file (fn_start, fn_search);
  517.     }
  518.  
  519.     case KF_DELETELEFT:        /* Delete left character    */
  520.         if (c_buffer_pos == l_buffer)
  521.         {
  522.         v1_putc (0x07);        /* Ring bell            */
  523.         return FALSE;
  524.         }
  525.  
  526. /* Decrement current position */
  527.  
  528.         --c_buffer_pos;
  529.         memcpy (c_buffer_pos, c_buffer_pos + 1, end_buffer - c_buffer_pos);
  530.         --end_buffer;
  531.         return TRUE;
  532.     }
  533. }
  534.  
  535. /* Set cursor shape */
  536.  
  537. static void    set_cursor_shape (mode)
  538. bool        mode;
  539. {
  540.   VIOCURSORINFO vioci;
  541.  
  542.   vioci.yStart = mode ? (KF_List[KF_HALFHEIGTH].akey ? -50 : 0) : -90;
  543.   vioci.cEnd = -100;
  544.   vioci.cx = 0;
  545.   vioci.attr = 0;
  546.  
  547.   VioSetCurType(&vioci, 0);
  548. }
  549. #endif
  550.  
  551. /* Read Cursor position */
  552.  
  553. static void    read_cursor_position ()
  554. {
  555.     USHORT usRow, usColumn;
  556.  
  557.     VioGetCurPos(&usRow, &usColumn, 0);
  558.     s_cursor = (usRow * Max_Cols) + usColumn;
  559. }
  560.  
  561. /* Re-position the cursor */
  562.  
  563. #ifndef NO_HISTORY
  564. static void    set_cursor_position (new)
  565. int        new;
  566. {
  567.     int diff;
  568.     USHORT usRow, usColumn;
  569.  
  570.     usRow    = (unsigned char)(new / Max_Cols);
  571.     usColumn = (unsigned char)(new % Max_Cols);
  572.  
  573. /* Are we at the bottom of the page? */
  574.  
  575.     if (usRow >= (unsigned char)Max_Lines)
  576.     {
  577.     diff = usRow + 1 - Max_Lines;
  578.     usRow = (unsigned char)(Max_Lines - 1);
  579.     s_cursor -= Max_Cols * diff;
  580.     }
  581.  
  582.     VioSetCurPos(usRow, usColumn, 0);
  583. }
  584.  
  585. /* Erase to end of line (avoid need for STUPID ansi.sys memory eater!) */
  586.  
  587. static void    erase_to_end_of_line ()
  588. {
  589.     USHORT usRow, usColumn;
  590.  
  591.     VioGetCurPos(&usRow, &usColumn, 0);
  592.     VioWrtNChar(" ", Max_Cols - usColumn, usRow, usColumn, 0);
  593. }
  594.  
  595. /* Generate the new cursor position */
  596.  
  597. static void    gen_cursor_position ()
  598. {
  599.     char    *cp = l_buffer - 1;
  600.     int        off = s_cursor;
  601.  
  602. /* Search to current position */
  603.  
  604.     while (++cp != c_buffer_pos)
  605.     {
  606.     if (*cp == '\t')
  607.         while ((++off) % 8);
  608.  
  609.     else if (iscntrl (*cp))
  610.         off += 2;
  611.  
  612.     else
  613.         ++off;
  614.     }
  615.  
  616. /* Position the cursor */
  617.  
  618.     set_cursor_position (off);
  619. }
  620.  
  621. /* Redisplay the current line */
  622.  
  623. static void    Redisplay_Line ()
  624. {
  625.     char    *control = "^x";
  626.     char    *cp = l_buffer;
  627.     int        off = s_cursor;
  628.  
  629. /* Reposition to start of line */
  630.  
  631.     set_cursor_position (s_cursor);
  632.  
  633. /* Output the line */
  634.  
  635.     while (cp != end_buffer)
  636.     {
  637.     if (*cp == '\t')
  638.     {
  639.         do
  640.         {
  641.         v1_putc (SP);
  642.         } while ((++off) % 8);
  643.     }
  644.  
  645.     else if (iscntrl (*cp))
  646.     {
  647.         control[1] = *cp + '@';
  648.         v1_puts (control);
  649.         off += 2;
  650.     }
  651.  
  652.     else
  653.     {
  654.         ++off;
  655.         v1_putc (*cp);
  656.     }
  657.  
  658.     ++cp;
  659.     }
  660.  
  661.     if ((m_line = ((s_cursor + Max_Length) / Max_Cols) + 1) >= Max_Lines)
  662.     m_line = Max_Lines - 1;
  663.  
  664.     erase_to_end_of_line ();        /* clear to end of line    */
  665.     Max_Length = end_buffer - l_buffer;
  666. }
  667.  
  668. /* Process history command
  669.  *
  670.  * -1: Previous command
  671.  *  1: Next command
  672.  *  0: Current command
  673.  *  2: Current command with no options processing
  674.  */
  675.  
  676. static bool    Process_History (direction)
  677. int        direction;
  678. {
  679.     char    *optionals = null;
  680.  
  681.     c_buffer_pos = l_buffer;
  682.     end_buffer = l_buffer;
  683.     c_history += (direction == 2) ? 0 : direction;
  684.  
  685.     switch (direction)
  686.     {
  687.     case -1:            /* Move up one line        */
  688.         if (c_history < 0)
  689.         {
  690.         c_history = -1;
  691.         return Re_start (No_prehistory);
  692.         }
  693.  
  694.         break;
  695.  
  696.     case 1:                /* Move to next history line    */
  697.         if (c_history >= l_history)
  698.         {
  699.         c_history = l_history;
  700.         return Re_start (No_posthistory);
  701.         }
  702.  
  703.         break;
  704.  
  705.     case 0:                /* Check out l_buffer        */
  706.         optionals = l_buffer;    /* Are there any additions to    */
  707.                     /* the history line        */
  708.  
  709. /* Find the end of the first part */
  710.  
  711.         while (!isspace (*optionals) && *optionals)
  712.         {
  713.         if (*optionals == '!')
  714.         {
  715.  
  716. /* Terminate at !! */
  717.  
  718.             if (*(optionals + 1) == '!')
  719.             {
  720.             optionals += 2;
  721.             break;
  722.             }
  723.  
  724. /* Terminate at a numeric value */
  725.  
  726.             else if (isdigit (*(optionals + 1)) ||
  727.                  (*(optionals + 1) == '-'))
  728.             {
  729.             optionals += 2;
  730.             while (isdigit (*optionals))
  731.                 ++optionals;
  732.  
  733.             break;
  734.             }
  735.         }
  736.  
  737.         ++optionals;
  738.         }
  739.  
  740. /* Copy selected item into line buffer */
  741.  
  742.     case 2:
  743.         M_length = (optionals == null) ? strlen (l_buffer) - 1
  744.                        : optionals - l_buffer - 1;
  745.  
  746.         if (!Scan_History ())
  747.         return FALSE;
  748.  
  749.         break;
  750.     }
  751.  
  752.     return UpDate_CLine (optionals);
  753. }
  754.  
  755. /* Ok c_history points to the new line.  Move optionals after history
  756.  * and the copy in history and add a space
  757.  */
  758.  
  759. static bool    UpDate_CLine (optionals)
  760. char        *optionals;
  761. {
  762.     int        opt_len;
  763.  
  764.     end_buffer = &l_buffer[strlen (cmd_history[c_history].command)];
  765.  
  766.     if ((end_buffer - l_buffer + (opt_len = strlen (optionals)) + 1) >= LINE_MAX)
  767.     return Re_start (History_2long);
  768.  
  769.     if (end_buffer > optionals)
  770.     memrcpy (end_buffer + opt_len, optionals + opt_len, opt_len + 1);
  771.  
  772.     else
  773.     strcpy (end_buffer, optionals);
  774.  
  775.     strncpy (l_buffer, cmd_history[c_history].command, (end_buffer - l_buffer));
  776.     end_buffer = &l_buffer[strlen (l_buffer)];
  777.     c_buffer_pos = end_buffer;
  778.     return TRUE;
  779. }
  780.  
  781. /* Scan the line buffer for a history match */
  782.  
  783. static bool    Scan_History ()
  784. {
  785.     char    *cp = l_buffer + 1;
  786.     char    *ep;
  787.     int        i = (int)strtol (cp, &ep, 10);
  788.  
  789. /* Get the previous command ? (single ! or double !!) */
  790.  
  791.     if ((M_length == 0) || (*cp == '!'))
  792.     {
  793.     if (c_history >= l_history)
  794.         c_history = l_history - 1;
  795.  
  796.     if (c_history < 0)
  797.         return Re_start (No_prehistory);
  798.  
  799.     return TRUE;
  800.     }
  801.  
  802. /* Request for special history number item.  Check History file empty */
  803.  
  804.     if (l_history == 0)
  805.     return Re_start (No_MatchHistory);
  806.  
  807. /* Check for number */
  808.  
  809.     if ((*l_buffer == '!') && (ep > cp) && M_length)
  810.     {
  811.     M_length = -1;
  812.  
  813.     for (c_history = l_history - 1;
  814.         (cmd_history[c_history].number != i) && (c_history >= 0);
  815.         --c_history);
  816.     }
  817.  
  818. /* No - scan for a match */
  819.  
  820.     else
  821.     {
  822.     for (c_history = l_history - 1;
  823.         (strncmp (cp, cmd_history[c_history].command, M_length) != 0)
  824.          && (c_history >= 0);
  825.         --c_history);
  826.     }
  827.  
  828. /* Anything found ? */
  829.  
  830.     if (c_history == -1)
  831.     {
  832.     c_history = l_history - 1;
  833.     return Re_start (No_MatchHistory);
  834.     }
  835.  
  836.     return TRUE;
  837. }
  838.  
  839. /* Scan back or forward from current history */
  840.  
  841. static void    Page_History (direction)
  842. int        direction;
  843. {
  844.     c_buffer_pos = l_buffer;
  845.     end_buffer = l_buffer;
  846.  
  847.     if (l_history == 0)
  848.     {
  849.     Re_start (No_MatchHistory);
  850.     return;
  851.     }
  852.  
  853. /* scan for a match */
  854.  
  855.     while (((c_history += direction) >= 0) && (c_history != l_history) &&
  856.        (strncmp (l_buffer, cmd_history[c_history].command, M_length) != 0));
  857.  
  858. /* Anything found ? */
  859.  
  860.     if ((c_history < 0) || (c_history >= l_history))
  861.     {
  862.     c_history = l_history - 1;
  863.     Re_start (No_MatchHistory);
  864.     }
  865.  
  866.     else
  867.     UpDate_CLine (null);
  868. }
  869.  
  870. /* Load history file */
  871.  
  872. void    Load_History ()
  873. {
  874.     FILE        *fp;
  875.     char        *cp;
  876.     int            i = 0;
  877.     Var_List        *lset;
  878.  
  879. /* Initialise history array */
  880.  
  881.     memset (cmd_history, 0, sizeof (struct cmd_history) * HISTORY_MAX);
  882.     c_history = -1;            /* Current entry        */
  883.     l_history = 0;            /* End of history array        */
  884.  
  885.     if ((lset = lookup (history_file, TRUE))->value == null)
  886.     {
  887.     setval (lset, (cp = Build_H_Filename ("history.sh")));
  888.     DELETE (cp);
  889.     }
  890.  
  891.     if (!History_Enabled || ((fp = fopen (lset->value, "rt")) == (FILE *)NULL))
  892.     return;
  893.  
  894. /* Read in file */
  895.  
  896.     while (fgets (l_buffer, LINE_MAX, fp) != (char *)NULL)
  897.     {
  898.     ++i;
  899.  
  900.     if ((cp = strchr (l_buffer, NL)) == (char *)NULL)
  901.         print_warn (H_TooLongI, i);
  902.  
  903.     else
  904.     {
  905.         *cp = 0;
  906.         Add_History (TRUE);
  907.     }
  908.     }
  909.  
  910.     fclose (fp);
  911. }
  912.  
  913. /* Add entry to history file */
  914.  
  915. void    Add_History (past)
  916. bool    past;                /* Past history?    */
  917. {
  918.     int            i;
  919.  
  920.     if ((!History_Enabled) || (strlen (l_buffer) == 0))
  921.     return;
  922.  
  923. /* If adding past history, decrement all numbers previous */
  924.  
  925.     if ((past) && l_history)
  926.     {
  927.     for (i = 0; i < l_history; i++)
  928.         --(cmd_history[i].number);
  929.     }
  930.  
  931. /* If the array is full, remove the last item */
  932.  
  933.     if (l_history == HISTORY_MAX)
  934.     {
  935.     if (cmd_history[0].command != null)
  936.         DELETE (cmd_history[0].command);
  937.  
  938.     --l_history;
  939.     memcpy (&cmd_history[0], &cmd_history[1],
  940.         sizeof (struct cmd_history) * (HISTORY_MAX - 1));
  941.     }
  942.  
  943. /* If there are any items in the array */
  944.  
  945.     c_history = l_history;
  946.     Current_Event = (l_history) ? cmd_history[l_history - 1].number + 1 : 0;
  947.     cmd_history[l_history].number = Current_Event;
  948.  
  949. /* Save the string */
  950.  
  951.     cmd_history[l_history++].command = strsave (l_buffer, 0);
  952. }
  953.  
  954. /* Print history */
  955.  
  956. void    Display_History ()
  957. {
  958.     int            i;
  959.     struct cmd_history    *cp = cmd_history;
  960.  
  961.     if (!l_history)
  962.     return;
  963.  
  964.     for (i = 0; i < l_history; ++cp, ++i)
  965.     {
  966.     v1printf ("%5d: ", cp->number);
  967.     v1a_puts (cp->command);
  968.     }
  969. }
  970.  
  971. /* Dump history to file */
  972.  
  973. void    Dump_History ()
  974. {
  975.     int            i;
  976.     struct cmd_history    *cp = cmd_history;
  977.     FILE        *fp;
  978.  
  979.     if (!History_Enabled ||
  980.     ((fp = fopen (lookup (history_file, FALSE)->value, "wt")) ==
  981.      (FILE *)NULL))
  982.     return;
  983.  
  984.     for (i = 0; i < l_history; ++cp, ++i)
  985.     {
  986.     fputs (cp->command, fp);
  987.     fputc (NL, fp);
  988.     }
  989.  
  990.     fclose (fp);
  991. }
  992.  
  993. /* Clear out history */
  994.  
  995. void    Clear_History ()
  996. {
  997.     int            i;
  998.     struct cmd_history    *cp = cmd_history;
  999.  
  1000.     for (i = 0; i < l_history; ++cp, ++i)
  1001.     {
  1002.     if (cp->command != null)
  1003.         DELETE (cp->command);
  1004.     }
  1005.  
  1006.     memset (cmd_history, 0, sizeof (struct cmd_history) * HISTORY_MAX);
  1007.  
  1008.     c_history = -1;            /* Current entry        */
  1009.     l_history = 0;            /* End of history array        */
  1010.     Current_Event = 0;
  1011. }
  1012.  
  1013. /* Output warning message and prompt */
  1014.  
  1015. static bool    Re_start (cp)
  1016. char        *cp;
  1017. {
  1018.     if (cp != (char *)NULL)
  1019.     {
  1020.     if (strlen (l_buffer) && (s_cursor != -1))
  1021.         S_putc (NL);
  1022.  
  1023.     print_warn (cp);
  1024.     erase_to_end_of_line ();
  1025.     v1_putc (NL);
  1026.     }
  1027.  
  1028.     put_prompt (last_prompt);
  1029.  
  1030. /* Re-initialise */
  1031.  
  1032.     Init_Input (insert_mode);
  1033.     return FALSE;
  1034. }
  1035.  
  1036. /* Copy backwards */
  1037.  
  1038. static void    memrcpy (sp1, sp, cnt)
  1039. char        *sp1;
  1040. char        *sp;
  1041. int        cnt;
  1042. {
  1043.     while (cnt--)
  1044.     *(sp1--) =  *(sp--);
  1045. }
  1046.  
  1047. /* Complete file name */
  1048.  
  1049. static bool    Complete_file (fn_start, fn_search)
  1050. char        *fn_start;
  1051. bool        fn_search;
  1052. {
  1053.     char        *fn_end, *cp, *fn_mstart, fn_es, *fn_dir;
  1054.     int            fn_len, pre_len, i;
  1055.     DIR            *dn;
  1056.     char        d_name [NAME_MAX + 1];
  1057.     struct direct    *d_ce;
  1058.     int            found_cnt = 0;
  1059.     int            max_per_line;
  1060.     static char        *ms_drive = "a:/";
  1061.  
  1062.     while (!isspace (*fn_start) && (fn_start != l_buffer))
  1063.     --fn_start;
  1064.  
  1065.     if (isspace (*fn_start))
  1066.     ++fn_start;
  1067.  
  1068.     fn_end = fn_start;
  1069.  
  1070.     while (!isspace (*fn_end) && (fn_end != end_buffer))
  1071.     ++fn_end;
  1072.  
  1073. /* Get the directory name */
  1074.  
  1075.     if (fn_end != end_buffer)
  1076.     {
  1077.     fn_es = *fn_end;
  1078.     *fn_end = 0;
  1079.     }
  1080.  
  1081. /* Find the directory name */
  1082.  
  1083.     if ((cp = strrchr (fn_start, '/')) != (char *)NULL)
  1084.     {
  1085.     fn_mstart = cp + 1;
  1086.     fn_dir = fn_start;
  1087.     }
  1088.  
  1089. /* No directory flag - Drive specifier? */
  1090.  
  1091.     else if (*(fn_start + 1) == ':')
  1092.     {
  1093.     *(fn_dir = ms_drive) = *fn_start;
  1094.     *(fn_dir + 2) = '.';
  1095.     fn_mstart = fn_start + 2;
  1096.     }
  1097.  
  1098. /* No drive specifier */
  1099.  
  1100.     else
  1101.     {
  1102.     fn_dir = ".";
  1103.     fn_mstart = fn_start;
  1104.     }
  1105.  
  1106. /* Set up some values - length and end */
  1107.  
  1108.     fn_len = fn_end - fn_mstart;
  1109.  
  1110.     if (fn_end != end_buffer)
  1111.     *fn_end = fn_es;
  1112.  
  1113. /* Get the match length which must be nonzero unless we are doing a display
  1114.  * of the directory
  1115.  */
  1116.  
  1117.     if (!fn_len && !fn_search)
  1118.     {
  1119.     v1_putc (0x07);
  1120.     return FALSE;
  1121.     }
  1122.  
  1123. /* Reset the / to a zero to terminate the directory name */
  1124.  
  1125.     if (cp != (char *)NULL)
  1126.     *cp = 0;
  1127.  
  1128. /* Check for some special cases - root */
  1129.  
  1130.     if ((i = strlen (fn_dir)) == 0)
  1131.     fn_dir = "/";
  1132.  
  1133.     else if ((i == 2) && (*(fn_dir + 1) == ':'))
  1134.     {
  1135.     *(fn_dir = ms_drive) = *fn_start;
  1136.     *(fn_dir + 2) = '/';
  1137.     }
  1138.  
  1139. /* Get the prefix length and open the directory */
  1140.  
  1141.     pre_len = fn_mstart - l_buffer;
  1142.     dn = opendir (fn_dir);
  1143.  
  1144.     if (cp != (char *)NULL)
  1145.     *cp = '/';
  1146.  
  1147.     if (dn == (DIR *)NULL)
  1148.     {
  1149.     v1_putc (0x07);
  1150.     return FALSE;
  1151.     }
  1152.  
  1153. /* Initialise the save buffer for a search or a match.  In the case of a
  1154.  * search, we alway want to output NAME_MAX characters.  In the case of a
  1155.  * match we want to know if we found it.
  1156.  */
  1157.  
  1158.     d_name[NAME_MAX] = 0;
  1159.     *d_name = 0;
  1160.     max_per_line = (Max_Cols / (((NAME_MAX / 8) + 1) * 8));
  1161.  
  1162. /* Scan the directory */
  1163.  
  1164.     while ((d_ce = readdir (dn)) != (struct direct *)NULL)
  1165.     {
  1166.     if (strnicmp (d_ce->d_name, fn_mstart, fn_len) == 0)
  1167.     {
  1168.  
  1169. /* Are we displaying the directory or just searching */
  1170.  
  1171.         if (fn_search)
  1172.         {
  1173.         v1_putc ((char)((found_cnt % max_per_line == 0) ? NL : '\t'));
  1174.         memset (d_name, ' ', NAME_MAX);
  1175.         memcpy (d_name, d_ce->d_name, strlen (d_ce->d_name));
  1176.         v1_puts (d_name);
  1177.         }
  1178.  
  1179. /* Just search - check for first entry match */
  1180.  
  1181.         else if (!*d_name)
  1182.         strcpy (d_name, d_ce->d_name);
  1183.  
  1184.         else
  1185.         {
  1186.         for (i = fn_len; d_name[i] == d_ce->d_name[i] ; i++);
  1187.         d_name[i] = 0;
  1188.         }
  1189.  
  1190. /* Increment counter */
  1191.  
  1192.         ++found_cnt;
  1193.     }
  1194.     }
  1195.  
  1196.     closedir (dn);
  1197.  
  1198. /* If we are searching and we found something - redraw */
  1199.  
  1200.     if (fn_search && found_cnt)
  1201.     {
  1202.     v1_putc (NL);
  1203.     put_prompt (last_prompt);
  1204.     read_cursor_position ();
  1205.     return TRUE;
  1206.     }
  1207.  
  1208. /* Did I find anything? - no exit */
  1209.  
  1210.     if (!*d_name)
  1211.     {
  1212.     v1_putc (0x07);
  1213.     return FALSE;
  1214.     }
  1215.  
  1216. /* Check that the line is not too long and if there is an end bit, we can
  1217.  * save a copy of it.
  1218.  */
  1219.  
  1220.     cp = null;
  1221.     fn_len = strlen (fn_end);
  1222.  
  1223.     if (((fn_len + strlen (d_name) + pre_len) >= LINE_MAX) ||
  1224.     ((fn_len != 0) && ((cp = strdup (fn_end)) == (char *)NULL)))
  1225.     {
  1226.     v1_putc (0x07);
  1227.     return FALSE;
  1228.     }
  1229.  
  1230. /* Append the new end of line bits */
  1231.  
  1232.     strcpy (fn_mstart, d_name);
  1233.     strcat (fn_mstart, cp);
  1234.  
  1235.     if (cp != null)
  1236.     free (cp);
  1237.  
  1238.     end_buffer = &l_buffer[strlen (l_buffer)];
  1239.     c_buffer_pos = end_buffer;
  1240.  
  1241. /* Beep if more than one */
  1242.  
  1243.     if (found_cnt > 1)
  1244.     v1_putc (0x07);
  1245.  
  1246.     return TRUE;
  1247. }
  1248.  
  1249. /* Initialise input */
  1250.  
  1251. static void    Init_Input (im)
  1252. bool        im;
  1253. {
  1254.     c_buffer_pos = l_buffer;    /* Initialise            */
  1255.     end_buffer = l_buffer;
  1256.     insert_mode = im;
  1257.     set_cursor_shape (insert_mode);
  1258.     M_length = -1;
  1259.  
  1260. /* Reset max line length and get the number of columns */
  1261.  
  1262.     Max_Length = 0;
  1263.     Get_Screen_Params ();
  1264.  
  1265. /* Save the cursor position */
  1266.  
  1267.     read_cursor_position ();
  1268. }
  1269.  
  1270. /* Configure Keyboard I/O */
  1271.  
  1272. void    Configure_Keys ()
  1273. {
  1274.     char        *sp;            /* Line pointers    */
  1275.     char        *cp;
  1276.     FILE        *fp;
  1277.     char        *line;            /* Input line        */
  1278.     char        c;            /* Save character    */
  1279.     int            i, fval, cval;
  1280.     int            line_len;
  1281.  
  1282. /* Get some memory for the input line and the file name */
  1283.  
  1284.     line_len = max (strlen (Program_Name) + 4, 200);
  1285.     if ((line = getcell (line_len)) == (char *)NULL)
  1286.     return;
  1287.  
  1288.     strcpy (line, Program_Name);
  1289.  
  1290. /* Find the .exe in the name */
  1291.  
  1292.     if ((cp = strrchr (line, '/')) != (char *)NULL)
  1293.     ++cp;
  1294.  
  1295.     else
  1296.     cp = line;
  1297.  
  1298.     if ((cp = strrchr (cp, '.')) == (char *)NULL)
  1299.     cp = &line[strlen (line)];
  1300.  
  1301.     strcpy (cp, ".ini");
  1302.  
  1303.     if ((fp = fopen (line, "rt")) == (FILE *)NULL)
  1304.     {
  1305.     DELETE (line);
  1306.     return;
  1307.     }
  1308.  
  1309.     while (fgets (line, line_len - 1, fp) != (char *)NULL)
  1310.     {
  1311.  
  1312. /* Ignore comment lines */
  1313.  
  1314.     if (*line == '#')
  1315.         continue;
  1316.  
  1317. /* Remove the EOL */
  1318.  
  1319.     if ((cp = strchr (line, '\n')) != (char *)NULL)
  1320.         *cp = 0;
  1321.  
  1322. /* Find the keyword */
  1323.  
  1324.     cp = line;
  1325.     while (!isspace (*cp) && *cp && (*cp != '='))
  1326.         ++cp;
  1327.  
  1328.     if (!*cp)
  1329.         continue;
  1330.  
  1331.     c = *cp;
  1332.     *cp = 0;
  1333.  
  1334. /* Look up the keyword name */
  1335.  
  1336.     for (i = 0; (i < KF_LENGTH) &&
  1337.             (stricmp (line, KF_List[i].kf_name) != 0); ++i);
  1338.  
  1339. /* Ignore no matches */
  1340.  
  1341.     if (i == KF_LENGTH)
  1342.         continue;
  1343.  
  1344. /* Find the equals */
  1345.  
  1346.     *cp = c;
  1347.     while (isspace (*cp))
  1348.         ++cp;
  1349.  
  1350.     if (*(cp++) != '=')
  1351.         continue;
  1352.  
  1353.     while (isspace (*cp))
  1354.         ++cp;
  1355.  
  1356. /* Get the value */
  1357.  
  1358.     errno = 0;
  1359.     cval = 0;
  1360.  
  1361.     fval = (int)strtol (cp, &sp, 0);
  1362.  
  1363. /* Check for correct terminator */
  1364.  
  1365.     if (errno || (fval < 0) ||
  1366.         ((fval != 0) && *sp) ||
  1367.         ((fval == 0) &&
  1368.           (((i < KF_END_FKEYS) && !isspace (*sp)) ||
  1369.            ((i >= KF_END_FKEYS) && *sp))))
  1370.         continue;
  1371.  
  1372.     if ((fval == 0) && (i < KF_END_FKEYS))
  1373.     {
  1374.         cp = sp;
  1375.         while (isspace (*cp))
  1376.         ++cp;
  1377.  
  1378.         errno = 0;
  1379.         cval = (int)strtol (cp, &sp, 0);
  1380.  
  1381.         if (errno || (cval == 0) || *sp)
  1382.         continue;
  1383.     }
  1384.  
  1385. /* OK we have a valid value, save it */
  1386.  
  1387.     KF_List[i].akey = (char)fval;
  1388.     KF_List[i].fkey = (char)cval;
  1389.     }
  1390.  
  1391.     DELETE (line);
  1392.     fclose (fp);
  1393. }
  1394. #endif
  1395.  
  1396. /* Check cursor is in column zero */
  1397.  
  1398. void    In_Col_Zero ()
  1399. {
  1400.     CHAR str[1];
  1401.     USHORT cb = sizeof(str);
  1402.     USHORT usRow, usColumn;
  1403.  
  1404.     Get_Screen_Params ();
  1405.     read_cursor_position ();
  1406.  
  1407.     VioGetCurPos(&usRow, &usColumn, 0);
  1408.     VioReadCharStr(str, &cb, usRow, usColumn, 0);
  1409.  
  1410.     if ((s_cursor % Max_Cols) || (str[0] != ' '))
  1411.     v1_putc (NL);
  1412. }
  1413.  
  1414. /* Get screen parameters */
  1415.  
  1416. static void    Get_Screen_Params ()
  1417. {
  1418.     VIOMODEINFO viomi;
  1419.  
  1420.     viomi.cb = sizeof(viomi);
  1421.     VioGetMode(&viomi, 0);
  1422.  
  1423.     Max_Cols  = viomi.col;
  1424.     Max_Lines = viomi.row;
  1425. }
  1426.  
  1427. /* Ring Bell ? */
  1428.  
  1429. bool    Ring_Bell ()
  1430. {
  1431. #ifdef NO_HISTORY
  1432.     return TRUE;
  1433. #else
  1434.     return (bool)(KF_List[KF_RINGBELL].akey ? TRUE : FALSE);
  1435. #endif
  1436. }
  1437.