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