home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / VILE327.ZIP / VILE327.TAR / vile3.27 / input.c < prev    next >
C/C++ Source or Header  |  1992-12-14  |  24KB  |  1,122 lines

  1. /*    INPUT:    Various input routines for MicroEMACS
  2.  *        written by Daniel Lawrence
  3.  *        5/9/86
  4.  *
  5.  * $Log: input.c,v $
  6.  * Revision 1.49  1992/12/05  13:22:10  foxharp
  7.  * make sure we escape eolchar with '\' if passed in in kbd_strings buffer,
  8.  * since the user would have had to type the '\' to put it there themselves
  9.  *
  10.  * Revision 1.48  1992/12/04  09:25:34  foxharp
  11.  * deleted unused assigns, no longer propagate pointer to block local
  12.  * in kbd_string, and fix expansion arg to mlreply_no_bs()
  13.  *
  14.  * Revision 1.47  1992/12/03  00:32:59  foxharp
  15.  * added new mlreply_no_bs, which doesn't do backslash processing
  16.  *
  17.  * Revision 1.46  1992/11/19  09:07:30  foxharp
  18.  * added check on recursive replay in dotcmdplay() -- I think we should
  19.  * never play or record a call to dotcmdplay, so we abort if we find ourselves
  20.  * doing so.
  21.  * also added kdone() call to finish up after ksetup()
  22.  *
  23.  * Revision 1.45  1992/08/20  23:40:48  foxharp
  24.  * typo fixes -- thanks, eric
  25.  *
  26.  * Revision 1.44  1992/07/24  07:49:38  foxharp
  27.  * shorten_name changes
  28.  *
  29.  * Revision 1.43  1992/07/18  13:13:56  foxharp
  30.  * put all path-shortening in one place (shorten_path()), and took out some old code now
  31.  * unnecessary
  32.  *
  33.  * Revision 1.42  1992/07/16  22:08:34  foxharp
  34.  * make keyboard macros redoable -- when out of input on a dotcmd, be
  35.  * sure to check for pending kbdmode input
  36.  *
  37.  * Revision 1.41  1992/07/15  23:23:12  foxharp
  38.  * made '80i-ESC' work
  39.  *
  40.  * Revision 1.40  1992/07/04  14:32:08  foxharp
  41.  * rearranged/improved the insertmode arrow key code
  42.  *
  43.  * Revision 1.39  1992/06/25  23:00:50  foxharp
  44.  * changes for dos/ibmpc
  45.  *
  46.  * Revision 1.38  1992/05/19  08:55:44  foxharp
  47.  * more prototype and shadowed decl fixups
  48.  *
  49.  * Revision 1.37  1992/05/16  12:00:31  pgf
  50.  * prototypes/ansi/void-int stuff/microsoftC
  51.  *
  52.  * Revision 1.36  1992/03/19  23:21:33  pgf
  53.  * linux portability (pathn)
  54.  *
  55.  * Revision 1.35  1992/03/07  10:28:52  pgf
  56.  * don't write a null past the end of the input buffer in kbd_string
  57.  *
  58.  * Revision 1.34  1992/03/03  21:58:32  pgf
  59.  * minor optimization in screen_string
  60.  *
  61.  * Revision 1.33  1992/03/03  09:35:52  pgf
  62.  * added support for getting "words" out of the buffer via variables --
  63.  * needed _nonspace character type
  64.  *
  65.  * Revision 1.32  1992/03/03  08:42:01  pgf
  66.  * took out pre_colon_pos
  67.  *
  68.  * Revision 1.31  1992/02/17  09:05:12  pgf
  69.  * make "RECORDED_ESC" work on machines whose natural chars are unsigned, and
  70.  * add support for "pre_colon_pos", which is the value of DOT just before the
  71.  * named command was run -- this lets ':' expand correctly in all cases
  72.  *
  73.  * Revision 1.30  1992/01/06  23:10:56  pgf
  74.  * try no update() in get_recorded_char() -- don't know why it's
  75.  * necessary
  76.  *
  77.  * Revision 1.29  1992/01/05  00:06:13  pgf
  78.  * split mlwrite into mlwrite/mlprompt/mlforce to make errors visible more
  79.  * often.  also normalized message appearance somewhat.
  80.  *
  81.  * Revision 1.28  1991/12/11  06:30:58  pgf
  82.  * fixed backslashing, yet again -- should now be able to search for a
  83.  * slash in a buffer
  84.  *
  85.  * Revision 1.27  1991/11/08  13:24:05  pgf
  86.  * ifdefed unused function
  87.  *
  88.  * Revision 1.26  1991/11/01  14:38:00  pgf
  89.  * saber cleanup
  90.  *
  91.  * Revision 1.25  1991/10/29  03:00:53  pgf
  92.  * added speckey function, for '#' prefixing, and allow ESC O x in addition
  93.  * to ESC [ x as ANSI fkeys
  94.  *
  95.  * Revision 1.24  1991/10/23  14:20:53  pgf
  96.  * changes to fix interactions between dotcmdmode and kbdmode and tungetc
  97.  *
  98.  * Revision 1.23  1991/10/22  14:36:21  pgf
  99.  * bug in ANSI_SPEC -- local declaration of f_insert hid global
  100.  *
  101.  * Revision 1.22  1991/10/22  03:08:09  pgf
  102.  * made wkillc work in kbd_string
  103.  *
  104.  * Revision 1.21  1991/09/26  13:15:03  pgf
  105.  * make backslash processing optional in kbd_string, and
  106.  * fix type mismatch in ANSI_SPEC code (f_insert)
  107.  *
  108.  * Revision 1.20  1991/09/17  00:51:17  pgf
  109.  * fixed even more backslashing bugs
  110.  *
  111.  * Revision 1.19  1991/09/13  03:27:06  pgf
  112.  * bugfix for backslash changes
  113.  *
  114.  * Revision 1.18  1991/09/13  03:06:39  pgf
  115.  * backslashing now works -- expansion chars and backslashes can be
  116.  * escaped properly
  117.  *
  118.  * Revision 1.17  1991/09/12  13:03:16  pgf
  119.  * kbd_string now recognizes leading eolchar corectly, but there are still
  120.  * problems with trying to quote it, as in :s/xxx/\//g to change xxx to / .
  121.  *
  122.  * Revision 1.16  1991/09/12  12:27:41    pgf
  123.  * un-record characters pushed back with tungetc
  124.  *
  125.  * Revision 1.15  1991/09/10  00:46:57    pgf
  126.  * cleanup of the dotcmd stuff, to prevent catnap() for escape sequences
  127.  * during playback
  128.  *
  129.  * Revision 1.14  1991/08/16  11:01:39    pgf
  130.  * added catnap() before typahead check on esc char in ANS_SPEC, and
  131.  * added REPLACECHAR special check on ANSI_SPEC, and
  132.  * allow quoting of %, #, :, with \ in kbd_string, so they don't expand
  133.  *
  134.  * Revision 1.13  1991/08/12  15:06:21    pgf
  135.  * added ANSI_SPEC capability -- can now use the arrow keys from
  136.  * command or insert mode
  137.  *
  138.  * Revision 1.12  1991/08/12  10:24:16    pgf
  139.  * interrupts can now interrupt keyboard recording
  140.  *
  141.  * Revision 1.11  1991/08/07  12:35:07    pgf
  142.  * added RCS log messages
  143.  *
  144.  * revision 1.10
  145.  * date: 1991/06/26 09:37:37;
  146.  * removed ifdef BEFORE
  147.  * 
  148.  * revision 1.9
  149.  * date: 1991/06/25 19:52:47;
  150.  * massive data structure restructure
  151.  * 
  152.  * revision 1.8
  153.  * date: 1991/06/04 09:20:31;
  154.  * kcod2key is now a macro
  155.  * 
  156.  * revision 1.7
  157.  * date: 1991/06/03 17:34:53;
  158.  * switch from "meta" etc. to "ctla" etc.
  159.  * 
  160.  * revision 1.6
  161.  * date: 1991/06/03 10:22:14;
  162.  * took out some old ifdefs, and
  163.  * fixed "can't escape a slash w/ a backslash" bug in searching
  164.  * 
  165.  * revision 1.5
  166.  * date: 1991/02/19 18:05:36;
  167.  * took out extraneous check
  168.  * 
  169.  * revision 1.4
  170.  * date: 1990/12/03 12:02:16;
  171.  * change 'word-under-cursor' expansion char to ':'
  172.  * 
  173.  * revision 1.3
  174.  * date: 1990/11/07 14:28:41;
  175.  * added '+' expansion character, to expand to the path-style string under the
  176.  * cursor
  177.  * 
  178.  * revision 1.2
  179.  * date: 1990/10/03 16:00:52;
  180.  * make backspace work for everyone
  181.  * 
  182.  * revision 1.1
  183.  * date: 1990/09/21 10:25:28;
  184.  * initial vile RCS revision
  185. */
  186.  
  187. #include    <stdio.h>
  188. #include    "estruct.h"
  189. #include    "edef.h"
  190.  
  191. /*
  192.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  193.  * ABORT. The ABORT status is returned if the user bumps out of the question
  194.  * with a ^G. Used any time a confirmation is required.
  195.  */
  196.  
  197. int
  198. mlyesno(prompt)
  199. char *prompt;
  200. {
  201.     char c;         /* input character */
  202.  
  203.     for (;;) {
  204. #if    NeWS
  205.         newsimmediateon() ;
  206.         mlprompt(,"%s [y/n]? ",prompt);
  207.         c = tgetc();        /* get the response */
  208.         newsimmediateoff() ;
  209. #else
  210.         mlprompt("%s [y/n]? ",prompt);
  211.         c = tgetc();        /* get the response */
  212. #endif
  213.  
  214.         if (c == kcod2key(abortc))        /* Bail out! */
  215.             return(ABORT);
  216.  
  217.         if (c=='y' || c=='Y')
  218.             return(TRUE);
  219.  
  220.         if (c=='n' || c=='N')
  221.             return(FALSE);
  222.     }
  223. }
  224.  
  225. /*
  226.  * Write a prompt into the message line, then read back a response. Keep
  227.  * track of the physical position of the cursor. If we are in a keyboard
  228.  * macro throw the prompt away, and return the remembered response. This
  229.  * lets macros run at full speed. The reply is always terminated by a carriage
  230.  * return. Handle erase, kill, and abort keys.
  231.  */
  232.  
  233. int
  234. mlreply(prompt, buf, bufn)
  235. char *prompt;
  236. char *buf;
  237. int bufn;
  238. {
  239.     return kbd_string(prompt, buf, bufn, '\n', EXPAND,
  240. #if MSDOS
  241.         /* we certainly quote with a '\' */
  242.         FALSE
  243. #else
  244.         TRUE
  245. #endif
  246.     );
  247. }
  248.  
  249. #ifdef NEEDED
  250. /* as above, but don't expand special punctuation, like #, %, ~, etc. */
  251. int
  252. mlreply_no_exp(prompt, buf, bufn)
  253. char *prompt;
  254. char *buf;
  255. int bufn;
  256. {
  257.     return kbd_string(prompt, buf, bufn, '\n',NO_EXPAND,TRUE);
  258. }
  259. #endif
  260.  
  261. /* as above, but don't do anything to backslashes */
  262. int
  263. mlreply_no_bs(prompt, buf, bufn)
  264. char *prompt;
  265. char *buf;
  266. int bufn;
  267. {
  268.     return kbd_string(prompt, buf, bufn, '\n',EXPAND,FALSE);
  269. }
  270.  
  271. /*    kcod2key:    translate 10-bit keycode to single key value */
  272. /* probably defined as a macro in estruct.h */
  273. #ifndef kcod2key
  274. kcod2key(c)
  275. int c;
  276. {
  277.     return c & 0x7f;
  278. }
  279. #endif
  280.  
  281.  
  282. /* the numbered buffer names increment each time they are referenced */
  283. void
  284. incr_dot_kregnum()
  285. {
  286.     if (dotcmdmode == PLAY) {
  287.         if (isdigit(*dotcmdptr) && *dotcmdptr < '9')
  288.             (*dotcmdptr)++;
  289.     }
  290. }
  291.  
  292. int tungotc = -1;
  293.  
  294. void
  295. tungetc(c)
  296. int c;
  297. {
  298.     tungotc = c;
  299.     if (dotcmdmode == RECORD) {
  300.         if (tmpcmdend > tmpcmdm) {
  301.             tmpcmdend--;
  302.             tmpcmdptr--;
  303.         }
  304.     }
  305.     if (kbdmode == RECORD) {
  306.         if (kbdend > kbdm) {
  307.             kbdend--;
  308.             kbdptr--;
  309.         }
  310.     }
  311. }
  312.  
  313. int
  314. tpeekc()
  315. {
  316.     return tungotc;
  317. }
  318.  
  319. /* get the next character of a replayed '.' or macro */
  320. int
  321. get_recorded_char(eatit)
  322. int eatit;  /* consume the character? */
  323. {
  324.     if (dotcmdmode == PLAY) {
  325.             
  326.         if (interrupted) {
  327.             dotcmdmode = STOP;
  328.             return (kcod2key(abortc));
  329.         }
  330.  
  331.         /* if there is some left... */
  332.         if (dotcmdptr < dotcmdend) {
  333.             if (eatit)
  334.                 return *dotcmdptr++;
  335.             else
  336.                 return *dotcmdptr;
  337.         }
  338.  
  339.         if (!eatit) {
  340.             if (dotcmdrep > 1)
  341.                 return dotcmdm[0];
  342.         } else {
  343.             /* at the end of last repetition? */
  344.             if (--dotcmdrep < 1) {
  345.                 dotcmdmode = STOP;
  346.                 dotcmdbegin(); /* immediately start recording
  347.                            again, just in case */
  348.             } else {
  349.  
  350.                 /* reset the macro to the beginning
  351.                     for the next rep */
  352.                 dotcmdptr = &dotcmdm[0];
  353.                 if (eatit)
  354.                     return (int)*dotcmdptr++;
  355.                 else
  356.                     return (int)*dotcmdptr;
  357.             }
  358.         }
  359.     } 
  360.  
  361.     if (kbdmode == PLAY) {
  362.     /* if we are playing a keyboard macro back, */
  363.  
  364.         if (interrupted) {
  365.             kbdmode = STOP;
  366.             return (kcod2key(abortc));
  367.         }
  368.  
  369.         /* if there is some left... */
  370.         if (kbdptr < kbdend) {
  371.             if (eatit)
  372.                 return (int)*kbdptr++;
  373.             else
  374.                 return (int)*kbdptr;
  375.         }
  376.  
  377.         /* at the end of last repetition? */
  378.         if (--kbdrep < 1) {
  379.             kbdmode = STOP;
  380.         } else {
  381.  
  382.             /* reset the macro to the beginning for the next rep */
  383.             kbdptr = &kbdm[0];
  384.             if (eatit)
  385.                 return (int)*kbdptr++;
  386.             else
  387.                 return (int)*kbdptr;
  388.         }
  389.     }
  390.  
  391.  
  392.     return -1;  /* no stored chars */
  393. }
  394.  
  395. /* if we should preserve this input, do so */
  396. void
  397. record_char(c)
  398. int c;
  399. {
  400.     if (c == ESC)
  401.         c = RECORDED_ESC;
  402.  
  403.     /* save it if we need to */
  404.     if (dotcmdmode == RECORD) {
  405.         /* don't overrun the buffer  */
  406.         if (tmpcmdptr > &tmpcmdm[NKBDM - 1])
  407.             return;
  408.  
  409.         /* force the last char to be abortc */
  410.         if (tmpcmdptr == &tmpcmdm[NKBDM - 1])
  411.             c = abortc; /* safest terminator */
  412.  
  413.         *tmpcmdptr++ = c;
  414.         tmpcmdend = tmpcmdptr;
  415.     }
  416.  
  417.     /* save it if we need to */
  418.     if (kbdmode == RECORD) {
  419.         /* don't overrun the buffer */
  420.         if (kbdptr > &kbdm[NKBDM - 1])
  421.             return;
  422.  
  423.         /* force the last char to be abortc */
  424.         if (kbdptr == &kbdm[NKBDM - 1]) {
  425.             c = abortc; /* safest terminator */
  426.             kbdmode = STOP;
  427.         }
  428.         *kbdptr++ = c;
  429.         kbdend = kbdptr;
  430.     }
  431. }
  432.  
  433. /*    tgetc:    Get a key from the terminal driver, resolve any keyboard
  434.         macro action                    */
  435. int 
  436. tgetc()
  437. {
  438.     int c;    /* fetched character */
  439.  
  440.     if (tungotc >= 0) {
  441.         c = tungotc;
  442.         tungotc = -1;
  443.     } else {
  444.         c = get_recorded_char(TRUE);
  445.         if (c != -1)
  446.             return lastkey = c;
  447.         /* fetch a character from the terminal driver */
  448.         interrupted = 0;
  449.         c = TTgetc();
  450.         if (c == -1 || c == kcod2key(intrc)) {
  451.             c = kcod2key(abortc);
  452.             return lastkey = c;
  453.         }
  454.     }
  455.  
  456.     record_char(c);
  457.  
  458.     /* and finally give the char back */
  459.     return(lastkey = c);
  460. }
  461.  
  462. /*    KBD_KEY:    Get one keystroke. The only prefix legal here
  463.             is the SPEC prefix.  */
  464. int
  465. kbd_key()
  466. {
  467.     int    c;
  468.  
  469.     /* get a keystroke */
  470.     c = tgetc();
  471.  
  472. #if ANSI_SPEC
  473.     if (insert_mode_was && last1key == -abortc) {
  474.         /* then we just read the command we pushed before */
  475.         extern CMDFUNC f_insert;
  476.         static back_to_ins_char = -1;
  477.         if (back_to_ins_char == -1) /* try to initialize it.. */
  478.             back_to_ins_char = fnc2key(&f_insert);
  479.         if (back_to_ins_char == -1) /* ... but couldn't */
  480.             mlforce("[Can't re-enter insert mode]");
  481.         else
  482.             tungetc(back_to_ins_char);
  483.         insertmode = insert_mode_was;
  484.         insert_mode_was = FALSE;
  485.     }
  486.  
  487.     if ((unsigned char)c == (unsigned char)RECORDED_ESC) {
  488.         /* if this is being replayed... */
  489.         /* ...then only look for esc sequences if there's input left */
  490.         if (get_recorded_char(FALSE) != -1)
  491.             c = ESC;
  492.         else
  493.             return (last1key = ESC);
  494.     }
  495.  
  496.     if (c == ESC) {
  497.         int nextc;
  498.  
  499.         /* if there's no recorded input, and no user typeahead */
  500.         if ((nextc = get_recorded_char(FALSE)) == -1 && !typahead())
  501.             catnap(50); /* give it a little extra time... */
  502.  
  503.         /* and then, if there _was_ recorded input or new typahead... */
  504.         if (nextc != -1 || typahead()) {
  505.             c = tgetc();
  506.             if (c == '[' || c == 'O') {
  507.                 /* eat ansi sequences */
  508.                 c = tgetc();
  509.                 if (abortc != ESC || !insertmode)
  510.                     return (last1key = SPEC | c);
  511.                 if (insertmode == REPLACECHAR) {
  512.                     /* eat the sequence, but return abort */
  513.                     return abortc;
  514.                 }
  515.                 /* remember we were in insert mode */
  516.                 insert_mode_was = insertmode;
  517.                 /* save the code, but return flag to
  518.                     ins() so it can clean up */
  519.                 tungetc(SPEC | c);
  520.                 return(last1key = -abortc);
  521.             } else {
  522.                 if (abortc != ESC)
  523.                     return (last1key = c);
  524.                 tungetc(c);
  525.                 return (last1key = ESC);
  526.             }
  527.         }
  528.     }
  529. #endif
  530.  
  531. #if    MSDOS | ST520
  532.     if (c == 0) {            /* Apply SPEC prefix    */
  533.         c = tgetc();
  534.         return(last1key = SPEC | c);
  535.     }
  536. #endif
  537.  
  538. #if    AMIGA
  539.     /* apply SPEC prefix */
  540.     if ((unsigned)c == 155) {
  541.         int    d;
  542.         c = tgetc();
  543.  
  544.         /* first try to see if it is a cursor key */
  545.         if ((c >= 'A' && c <= 'D') || c == 'S' || c == 'T') {
  546.             if (!insertmode)
  547.                 return(last1key = SPEC | c);
  548.         }
  549.  
  550.         /* next, a 2 char sequence */
  551.         d = tgetc();
  552.         if (d == '~') {
  553.             if (!insertmode)
  554.                 return(last1key = SPEC | c);
  555.         }
  556.  
  557.         /* decode a 3 char sequence */
  558.         c = d + ' ';
  559.         /* if a shifted function key, eat the tilde */
  560.         if (d >= '0' && d <= '9')
  561.             d = tgetc();
  562.         if (!insertmode)
  563.             return(last1key = SPEC | c);
  564.     }
  565. #endif
  566.  
  567. #if  WANGPC
  568.     if (c == 0x1F) {    /* Apply SPEC prefix    */
  569.         c = tgetc();
  570.         if (!insertmode)
  571.             return(last1key = SPEC | c);
  572.     }
  573. #endif
  574.  
  575.     return (last1key = c);
  576. }
  577.  
  578. /*    KBD_SEQ:    Get a command sequence (multiple keystrokes) from 
  579.         the keyboard.
  580.         Process all applicable prefix keys.
  581.         Set lastcmd for commands which want stuttering.
  582. */
  583. int
  584. kbd_seq()
  585. {
  586.     int c;        /* fetched keystroke */
  587.  
  588.     /* get initial character */
  589.     c = kbd_key();
  590.  
  591.     /* process CTLA prefix */
  592.     if (c == cntl_a) {
  593.         c = kbd_key();
  594.         return (lastcmd = CTLA | c);
  595.     }
  596.  
  597.     /* process CTLX prefix */
  598.     if (c == cntl_x) {
  599.         c = kbd_key();
  600.         return (lastcmd = CTLX | c);
  601.     }
  602.  
  603.     /* otherwise, just return it */
  604.     return (lastcmd = c);
  605. }
  606.  
  607.  
  608. /* get a string consisting of inclchartype characters from the current
  609.     position.  if inclchartype is 0, return everything to eol */
  610. int
  611. screen_string(buf,bufn,inclchartype)
  612. char *buf;
  613. int bufn, inclchartype;
  614. {
  615.     register int i = 0;
  616.     /* register int s = TRUE; */
  617.     MARK mk;
  618.  
  619.     mk = DOT;
  620.     while ( i < bufn && !is_at_end_of_line(DOT)) {
  621.         buf[i] = char_at(DOT);
  622.         if (inclchartype && !istype(inclchartype, buf[i]))
  623.             break;
  624.         DOT.o++;
  625.         i++;
  626.     }
  627.     buf[bufn-1] = '\0';
  628.     if (i < bufn)
  629.         buf[i] = '\0';
  630.     DOT = mk;
  631.  
  632.     return buf[0] != '\0';
  633. }
  634.  
  635. /*    A more generalized prompt/reply function allowing the caller
  636.     to specify a terminator other than '\n'.  Both are accepted.
  637.     Assumes the buffer already contains a valid (possibly
  638.     null) string to use as the default response.
  639. */
  640. int
  641. kbd_string(prompt, extbuf, bufn, eolchar, expand, dobackslashes)
  642. char *prompt;        /* put this out first */
  643. char *extbuf;        /* the caller's (possibly full) buffer */
  644. int bufn;        /* the length of  " */
  645. int eolchar;        /* char we can terminate on, in addition to '\n' */
  646. int expand;        /* do we want to expand %, #, : */
  647. int dobackslashes;    /* do we add and delete '\' chars for the caller */
  648. {
  649.     register int cpos, extcpos;/* current character position in string */
  650.     register int c;
  651.     register int quotef;    /* are we quoting the next char? */
  652.     register int backslashes; /* are we quoting the next expandable char? */
  653.     int firstch = TRUE;
  654.     char buf[256];
  655. #if ! MSDOS
  656.     char str[80];
  657. #endif
  658.  
  659.     if (clexec)
  660.         return nextarg(extbuf);
  661.  
  662.     lineinput = TRUE;
  663.  
  664.     quotef = FALSE;
  665.  
  666.     /* prompt the user for the input string */
  667.     mlprompt(prompt);
  668.  
  669.     if (bufn > 256) bufn = 256;
  670.  
  671.     cpos = extcpos = 0;
  672.     /* add backslash escapes in front of volatile characters */
  673.     while((c = extbuf[extcpos++]) != '\0' && extcpos < bufn) {
  674.         if (c == eolchar && eolchar != '\n')
  675.             goto is_eolchar;
  676.         switch(c) {
  677.         case '%':
  678.         case '#':
  679. #if ! MSDOS
  680.         case ':':
  681. #endif
  682.             if (dobackslashes && expand == EXPAND)
  683.                 buf[cpos++] = '\\'; /* add extra */
  684.         default:
  685.             break;
  686.         case '\\':
  687.         is_eolchar:
  688.             if (dobackslashes)
  689.                 buf[cpos++] = '\\'; /* add extra */
  690.             break;
  691.         }
  692.         buf[cpos++] = c;
  693.     }
  694.     buf[cpos] = '\0';
  695.  
  696.     /* put out the default response, which comes in already in the buffer */
  697.     cpos = 0;
  698.     while((c = buf[cpos]) != '\0' && cpos < bufn-1) {
  699.         if (!isprint(c)) {
  700.             if (disinp)
  701.                 TTputc('^');
  702.             ++ttcol;
  703.             c = toalpha(c);
  704.         }
  705.  
  706.         if (disinp)
  707.             TTputc(c);
  708.  
  709.  
  710.         ++ttcol;
  711.         ++cpos;
  712.     }
  713.     TTflush();
  714.  
  715.     backslashes = 0; /* by definition, there is an even 
  716.                     number of backslashes */
  717.     for (;;) {
  718.         /* get a character from the user */
  719.         c = kbd_key();
  720.  
  721.         /* If it is a <ret>, change it to a <NL> */
  722.         if (c == '\r' && quotef == FALSE)
  723.             c = '\n';
  724.  
  725.         /* if they hit the line terminate, wrap it up */
  726.         /* don't allow newlines in the string -- they cause real
  727.             problems, especially when searching for patterns
  728.             containing them -pgf */
  729.         /* terminate with newline, or unescaped eolchar */
  730.         if ((c == '\n') || (c == eolchar && (quotef == FALSE && 
  731.                 (backslashes & 1) == 0))) {
  732.             /* if (buf[cpos] != '\0')
  733.                 buf[cpos++] = '\0'; */
  734.  
  735.             lineinput = FALSE;
  736.             if (dobackslashes)
  737.                 remove_backslashes(buf); /* take out quoters */
  738.  
  739.             strcpy(extbuf,buf);
  740.  
  741.             /* if buffer is empty, return FALSE */
  742.             return (extbuf[0] == 0) ? FALSE:TRUE;
  743.         }
  744.  
  745. #if    NeWS    /* make sure cursor is where we think it is before output */
  746.         TTmove(ttrow,ttcol) ;
  747. #endif
  748.         /* change from command form back to character form */
  749.         c = kcod2key(c);
  750.             
  751.         if (c == kcod2key(abortc) && quotef == FALSE) {
  752.             buf[cpos] = 0;
  753.             esc(FALSE, 0);
  754.             TTflush();
  755.             lineinput = FALSE;
  756.             return ABORT;
  757.         } else if ((isbackspace(c) ||
  758.             c == kcod2key(wkillc) ||
  759.             c == kcod2key(killc)) && quotef==FALSE) {
  760.  
  761.             /* have we backed thru a "word" yet? */
  762.             int saw_word;
  763.  
  764.             /* rubout/erase */
  765.             if (cpos == 0) {
  766.                 buf[0] = '\0';
  767.                 mlerase();
  768.                 lineinput = FALSE;
  769.                 return FALSE;
  770.             }
  771.  
  772.         killit:
  773.             saw_word = FALSE;
  774.             while (cpos > 0) {
  775.                 if (c == kcod2key(wkillc)) {
  776.                     if (isspace(buf[cpos-1])) {
  777.                         if (saw_word)
  778.                             break;
  779.                     } else {
  780.                         saw_word = TRUE;
  781.                     }
  782.                 }
  783.                 outstring("\b \b");
  784.                 --ttcol;
  785.  
  786.                 if (!isprint(buf[--cpos])) {
  787.                     outstring("\b \b");
  788.                     --ttcol;
  789.                 }
  790.                 if (cpos && buf[cpos-1] == '\\') {
  791.                     backslashes = 1;
  792.                     while (backslashes+1 <= cpos &&
  793.                         buf[cpos-1-backslashes] == '\\')
  794.                         backslashes++;
  795.                 } else {
  796.                     backslashes = 0;
  797.                 }
  798.  
  799.                 buf[cpos] = '\0';
  800.                 if (isbackspace(c))
  801.                     break;
  802.             }
  803.             TTflush();
  804.  
  805.         } else if (expand == EXPAND && ((backslashes & 1 ) == 0)) {
  806.             /* we prefer to expand to filenames, but a buffer name
  807.                 will do */
  808.             char *cp;
  809.             char *hist_lookup();
  810.             if (firstch == TRUE) {
  811.                 tungetc(c);
  812.                 c = killc;
  813.                 goto killit;
  814.             }
  815.             switch(c) {
  816.             case '%':
  817.                 cp = curbp->b_fname;
  818.                 if (!*(curbp->b_fname) ||
  819.                     isspace(*(curbp->b_fname)))
  820.                     cp = curbp->b_bname;
  821.                 else {
  822.                     cp = shorten_path(curbp->b_fname);
  823.                     if (!cp) cp = curbp->b_bname;
  824.                 }
  825.                 break;
  826.             case '#':
  827.                 cp = hist_lookup(1);  /* returns buffer name */
  828.                 if (cp) {
  829.                     /* but we want a file */
  830.                     BUFFER *bp;
  831.                     bp = bfind(cp,NO_CREAT,0);
  832.                     cp = shorten_path(bp->b_fname);
  833.                     if (!cp || !*cp || isspace(*cp)) {
  834.                         /* oh well, use the buffer */
  835.                         cp = bp->b_bname;
  836.                     }
  837.                 }
  838.                 break;
  839. #if ! MSDOS
  840. /* drive letters get in the way */
  841.             case ':':
  842.                 if (screen_string(str, 80, _pathn))
  843.                     cp = str;
  844.                 else
  845.                     cp = NULL;
  846.                 break;
  847. #endif
  848.             default:
  849.                 goto trymore;
  850.             }
  851.                 
  852.             if (!cp) {
  853.                 TTbeep();
  854.                 continue;
  855.             }
  856.             while (cpos < bufn-1 && (c = *cp++)) {
  857.                 buf[cpos++] = c;
  858.                 if (!isprint(c)) {
  859.                     outstring("^");
  860.                     ++ttcol;
  861.                     c = toalpha(c);
  862.                 }
  863.                 if (disinp)
  864.                     TTputc(c);
  865.                 ++ttcol;
  866.             }
  867.             buf[cpos] = '\0';
  868.             TTflush();
  869.         } else {
  870.         trymore:
  871.             if (c == kcod2key(quotec) && quotef == FALSE) {
  872.                 quotef = TRUE;
  873.             } else    {
  874.                 if (firstch == TRUE) {
  875.                     /* we always clean the buf on the
  876.                         first char typed */
  877.                     tungetc(c);
  878.                     c = killc;
  879.                     goto killit;
  880.                 }
  881.                 if (c == '\\')
  882.                     backslashes++;
  883.                 else
  884.                     backslashes = 0;
  885.                 quotef = FALSE;
  886.                 if (cpos < bufn-1) {
  887.                     buf[cpos++] = c;
  888.                     buf[cpos] = '\0';
  889.  
  890.                     if (!isprint(c)) {
  891.                         outstring("^");
  892.                         ++ttcol;
  893.                         c = toalpha(c);
  894.                     }
  895.  
  896.                     if (disinp)
  897.                         TTputc(c);
  898.  
  899.                     ++ttcol;
  900.                     TTflush();
  901.                 }
  902.             }
  903.         }
  904.         firstch = FALSE;
  905.     }
  906. }
  907.  
  908. /* turn \X into X */
  909. void
  910. remove_backslashes(s)
  911. char *s;
  912. {
  913.     char *cp;
  914.     while (*s) {
  915.         if (*s == '\\') {  /* shift left */
  916.             for (cp = s; *cp; cp++)
  917.                 *cp = *(cp+1);
  918.         }
  919.         s++;
  920.     }
  921. }
  922.  
  923. void
  924. outstring(s)    /* output a string of input characters */
  925. char *s;    /* string to output */
  926. {
  927.     if (disinp)
  928.         while (*s)
  929.             TTputc(*s++);
  930. }
  931.  
  932. void
  933. ostring(s)    /* output a string of output characters */
  934. char *s;    /* string to output */
  935. {
  936.     if (discmd)
  937.         while (*s)
  938.             TTputc(*s++);
  939. }
  940.  
  941. /* ARGSUSED */
  942. int
  943. speckey(f,n)
  944. int f,n;
  945. {
  946.  
  947.     tungetc( SPEC | kbd_key() );
  948.  
  949.     return TRUE;
  950. }
  951.  
  952. /*
  953.  * Begin recording the dot command macro.
  954.  * Set up variables and return.
  955.  * we use a temporary accumulator, in case this gets stopped prematurely
  956.  */
  957. int
  958. dotcmdbegin()
  959. {
  960.     /* never record a playback */
  961.     if (kbdmode == PLAY)
  962.         return FALSE;
  963.  
  964.     switch (dotcmdmode) {
  965.     case TMPSTOP:
  966.     case PLAY:
  967.         return FALSE;
  968.     }
  969.     tmpcmdptr = &tmpcmdm[0];
  970.     tmpcmdend = tmpcmdptr;
  971.     dotcmdmode = RECORD;
  972.     return TRUE;
  973. }
  974.  
  975. /*
  976.  * Finish dot command, and copy it to the real holding area
  977.  */
  978. int
  979. dotcmdfinish()
  980. {
  981.  
  982.     switch (dotcmdmode) {
  983.     case STOP:
  984.     case PLAY:
  985.     case TMPSTOP:
  986.         return FALSE;
  987.  
  988.     case RECORD:
  989.         ;
  990.     }
  991.     tmpcmdptr = &tmpcmdm[0];
  992.     dotcmdptr = &dotcmdm[0];
  993.     while (tmpcmdptr < tmpcmdend)
  994.         *dotcmdptr++ = *tmpcmdptr++;
  995.     dotcmdend = dotcmdptr;
  996.     dotcmdptr = &dotcmdm[0];
  997.     dotcmdmode = STOP;
  998.     return TRUE;
  999. }
  1000.  
  1001.  
  1002. /* stop recording a dot command, 
  1003.     probably because the command is not re-doable */ 
  1004. void
  1005. dotcmdstop()
  1006. {
  1007.     if (dotcmdmode == RECORD)
  1008.         dotcmdmode = STOP;
  1009. }
  1010.  
  1011. /*
  1012.  * Execute a the '.' command, by putting us in PLAY mode.
  1013.  * The command argument is the number of times to loop. Quit as soon as a
  1014.  * command gets an error. Return TRUE if all ok, else FALSE.
  1015.  */
  1016. int
  1017. dotcmdplay(f, n)
  1018. int f,n;
  1019. {
  1020.     if (!f)
  1021.         n = 1;
  1022.     else if (n <= 0)
  1023.         return TRUE;
  1024.  
  1025.     if (dotcmdmode != STOP) {
  1026.         dotcmdmode = STOP;
  1027.         return FALSE;
  1028.     }
  1029.  
  1030.     dotcmdrep = n;        /* remember how many times to execute */
  1031.     dotcmdmode = PLAY;        /* put us in play mode */
  1032.     dotcmdptr = &dotcmdm[0];    /*    at the beginning */
  1033.  
  1034.     return TRUE;
  1035. }
  1036.  
  1037. /*
  1038.  * Begin a keyboard macro.
  1039.  * Error if not at the top level in keyboard processing. Set up variables and
  1040.  * return.
  1041.  */
  1042. /* ARGSUSED */
  1043. int
  1044. kbd_mac_begin(f, n)
  1045. int f,n;
  1046. {
  1047.     if (kbdmode != STOP) {
  1048.         mlforce("[Macro already active]");
  1049.         return FALSE;
  1050.     }
  1051.     mlwrite("[Start macro]");
  1052.     kbdptr = &kbdm[0];
  1053.     kbdend = kbdptr;
  1054.     kbdmode = RECORD;
  1055.     kbdplayreg = -1;  /* default buffer */
  1056.     return TRUE;
  1057. }
  1058.  
  1059. /*
  1060.  * End keyboard macro. Check for the same limit conditions as the above
  1061.  * routine. Set up the variables and return to the caller.
  1062.  */
  1063. /* ARGSUSED */
  1064. int
  1065. kbd_mac_end(f, n)
  1066. int f,n;
  1067. {
  1068.     if (kbdmode == STOP) {
  1069.         mlforce("[Macro not active]");
  1070.         return FALSE;
  1071.     }
  1072.     if (kbdmode == RECORD) {
  1073.         mlwrite("[End macro]");
  1074.         kbdmode = STOP;
  1075.         kbdplayreg = -1;  /* default buffer */
  1076.         kbdlim = kbdend;
  1077.     }
  1078.     /* note that if kbd_mode == PLAY, we do nothing -- that makes
  1079.         the '^X-)' at the of the recorded buffer a no-op during
  1080.         playback */
  1081.     return TRUE;
  1082. }
  1083.  
  1084. /*
  1085.  * Execute a macro.
  1086.  * The command argument is the number of times to loop. Quit as soon as a
  1087.  * command gets an error. Return TRUE if all ok, else FALSE.
  1088.  */
  1089. /* ARGSUSED */
  1090. int
  1091. kbd_mac_exec(f, n)
  1092. int f,n;
  1093. {
  1094.     if (kbdmode != STOP) {
  1095.         mlforce("[Macro already active]");
  1096.         return FALSE;
  1097.     }
  1098.     if (n <= 0)
  1099.         return TRUE;
  1100.     kbdrep = n;        /* remember how many times to execute */
  1101.     kbdmode = PLAY;     /* start us in play mode */
  1102.     kbdplayreg = -1;    /* default playback register */
  1103.     kbdptr = &kbdm[0];    /*    at the beginning */
  1104.     kbdend = kbdlim;
  1105.     return TRUE;
  1106. }
  1107.  
  1108. /* ARGSUSED */
  1109. int
  1110. kbd_mac_save(f,n)
  1111. int f,n;
  1112. {
  1113.     register unsigned char *kp;
  1114.     ksetup();
  1115.     for (kp = kbdm; kp < kbdlim; kp++)
  1116.         kinsert(*kp);
  1117.     kdone();
  1118.     mlwrite("[Keyboard macro saved.]");
  1119.     return TRUE;
  1120. }
  1121.  
  1122.