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