1/*
2 * ed.chared.c: Character editing functions.
3 */
4/*-
5 * Copyright (c) 1980, 1991 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32/*
33  Bjorn Knutsson @ Thu Jun 24 19:02:17 1999
34
35  e_dabbrev_expand() did not do proper completion if quoted spaces were present
36  in the string being completed. Exemple:
37
38  # echo hello\ world
39  hello world
40  # echo h<press key bound to dabbrev-expande>
41  # echo hello\<cursor>
42
43  Correct behavior is:
44  # echo h<press key bound to dabbrev-expande>
45  # echo hello\ world<cursor>
46
47  The same problem occured if spaces were present in a string withing quotation
48  marks. Example:
49
50  # echo "hello world"
51  hello world
52  # echo "h<press key bound to dabbrev-expande>
53  # echo "hello<cursor>
54
55  The former problem could be solved with minor modifications of c_preword()
56  and c_endword(). The latter, however, required a significant rewrite of
57  c_preword(), since quoted strings must be parsed from start to end to
58  determine if a given character is inside or outside the quotation marks.
59
60  Compare the following two strings:
61
62  # echo \"" 'foo \' bar\"
63  " 'foo \' bar\
64  # echo '\"" 'foo \' bar\"
65  \"" foo ' bar"
66
67  The only difference between the two echo lines is in the first character
68  after the echo command. The result is either one or three arguments.
69
70 */
71
72#include "sh.h"
73#include "ed.h"
74#include "tw.h"
75#include "ed.defns.h"
76
77/* #define SDEBUG */
78
79#define TCSHOP_NOP    	  0x00
80#define TCSHOP_DELETE 	  0x01
81#define TCSHOP_INSERT 	  0x02
82#define TCSHOP_CHANGE 	  0x04
83
84#define CHAR_FWD	0
85#define CHAR_BACK	1
86
87/*
88 * vi word treatment
89 * from: Gert-Jan Vons <vons@cesar.crbca1.sinet.slb.com>
90 */
91#define C_CLASS_WHITE	1
92#define C_CLASS_WORD	2
93#define C_CLASS_OTHER	3
94
95static Char *InsertPos = InputBuf; /* Where insertion starts */
96static Char *ActionPos = 0;	   /* Where action begins  */
97static int  ActionFlag = TCSHOP_NOP;	   /* What delayed action to take */
98/*
99 * Word search state
100 */
101static int  searchdir = F_UP_SEARCH_HIST; 	/* Direction of last search */
102static struct Strbuf patbuf; /* = Strbuf_INIT; Search target */
103/*
104 * Char search state
105 */
106static int  srch_dir = CHAR_FWD;		/* Direction of last search */
107static Char srch_char = 0;			/* Search target */
108
109/* all routines that start with c_ are private to this set of routines */
110static	void	 c_alternativ_key_map	(int);
111void	 c_insert		(int);
112void	 c_delafter		(int);
113void	 c_delbefore		(int);
114static 	int	 c_to_class		(Char);
115static	Char	*c_prev_word		(Char *, Char *, int);
116static	Char	*c_next_word		(Char *, Char *, int);
117static	Char	*c_number		(Char *, int *, int);
118static	Char	*c_expand		(Char *);
119static	int	 c_excl			(Char *);
120static	int	 c_substitute		(void);
121static	void	 c_delfini		(void);
122static	int	 c_hmatch		(Char *);
123static	void	 c_hsetpat		(void);
124#ifdef COMMENT
125static	void	 c_get_word		(Char **, Char **);
126#endif
127static	Char	*c_preword		(Char *, Char *, int, Char *);
128static	Char	*c_nexword		(Char *, Char *, int);
129static	Char	*c_endword		(Char *, Char *, int, Char *);
130static	Char	*c_eword		(Char *, Char *, int);
131static	void	 c_push_kill		(Char *, Char *);
132static	void	 c_save_inputbuf	(void);
133static  CCRETVAL c_search_line		(Char *, int);
134static  CCRETVAL v_repeat_srch		(int);
135static	CCRETVAL e_inc_search		(int);
136#ifdef notyet
137static  CCRETVAL e_insert_str		(Char *);
138#endif
139static	CCRETVAL v_search		(int);
140static	CCRETVAL v_csearch_fwd		(Char, int, int);
141static	CCRETVAL v_action		(int);
142static	CCRETVAL v_csearch_back		(Char, int, int);
143
144static void
145c_alternativ_key_map(int state)
146{
147    switch (state) {
148    case 0:
149	CurrentKeyMap = CcKeyMap;
150	break;
151    case 1:
152	CurrentKeyMap = CcAltMap;
153	break;
154    default:
155	return;
156    }
157
158    AltKeyMap = (Char) state;
159}
160
161void
162c_insert(int num)
163{
164    Char *cp;
165
166    if (LastChar + num >= InputLim)
167	return;			/* can't go past end of buffer */
168
169    if (Cursor < LastChar) {	/* if I must move chars */
170	for (cp = LastChar; cp >= Cursor; cp--)
171	    cp[num] = *cp;
172	if (Mark && Mark > Cursor)
173		Mark += num;
174    }
175    LastChar += num;
176}
177
178void
179c_delafter(int num)
180{
181    Char *cp, *kp = NULL;
182
183    if (num > LastChar - Cursor)
184	num = (int) (LastChar - Cursor);	/* bounds check */
185
186    if (num > 0) {			/* if I can delete anything */
187	if (VImode) {
188	    kp = UndoBuf;		/* Set Up for VI undo command */
189	    UndoAction = TCSHOP_INSERT;
190	    UndoSize = num;
191	    UndoPtr  = Cursor;
192	    for (cp = Cursor; cp <= LastChar; cp++) {
193		*kp++ = *cp;	/* Save deleted chars into undobuf */
194		*cp = cp[num];
195	    }
196	}
197	else
198	    for (cp = Cursor; cp + num <= LastChar; cp++)
199		*cp = cp[num];
200	LastChar -= num;
201	/* Mark was within the range of the deleted word? */
202	if (Mark && Mark > Cursor && Mark <= Cursor+num)
203		Mark = Cursor;
204	/* Mark after the deleted word? */
205	else if (Mark && Mark > Cursor)
206		Mark -= num;
207    }
208#ifdef notdef
209    else {
210	/*
211	 * XXX: We don't want to do that. In emacs mode overwrite should be
212	 * sticky. I am not sure how that affects vi mode
213	 */
214	inputmode = MODE_INSERT;
215    }
216#endif /* notdef */
217}
218
219void
220c_delbefore(int num)		/* delete before dot, with bounds checking */
221{
222    Char *cp, *kp = NULL;
223
224    if (num > Cursor - InputBuf)
225	num = (int) (Cursor - InputBuf);	/* bounds check */
226
227    if (num > 0) {			/* if I can delete anything */
228	if (VImode) {
229	    kp = UndoBuf;		/* Set Up for VI undo command */
230	    UndoAction = TCSHOP_INSERT;
231	    UndoSize = num;
232	    UndoPtr  = Cursor - num;
233	    for (cp = Cursor - num; cp <= LastChar; cp++) {
234		*kp++ = *cp;
235		*cp = cp[num];
236	    }
237	}
238	else
239	    for (cp = Cursor - num; cp + num <= LastChar; cp++)
240		*cp = cp[num];
241	LastChar -= num;
242	Cursor -= num;
243	/* Mark was within the range of the deleted word? */
244	if (Mark && Mark > Cursor && Mark <= Cursor+num)
245		Mark = Cursor;
246	/* Mark after the deleted word? */
247	else if (Mark && Mark > Cursor)
248		Mark -= num;
249    }
250}
251
252static Char *
253c_preword(Char *p, Char *low, int n, Char *delim)
254{
255  while (n--) {
256    Char *prev = low;
257    Char *new;
258
259    while (prev < p) {		/* Skip initial non-word chars */
260      if (!Strchr(delim, *prev) || *(prev-1) == (Char)'\\')
261	break;
262      prev++;
263    }
264
265    new = prev;
266
267    while (new < p) {
268      prev = new;
269      new = c_endword(prev-1, p, 1, delim); /* Skip to next non-word char */
270      new++;			/* Step away from end of word */
271      while (new <= p) {	/* Skip trailing non-word chars */
272	if (!Strchr(delim, *new) || *(new-1) == (Char)'\\')
273	  break;
274	new++;
275      }
276    }
277
278    p = prev;			/* Set to previous word start */
279
280  }
281  if (p < low)
282    p = low;
283  return (p);
284}
285
286/*
287 * c_to_class() returns the class of the given character.
288 *
289 * This is used to make the c_prev_word(), c_next_word() and c_eword() functions
290 * work like vi's, which classify characters. A word is a sequence of
291 * characters belonging to the same class, classes being defined as
292 * follows:
293 *
294 *	1/ whitespace
295 *	2/ alphanumeric chars, + underscore
296 *	3/ others
297 */
298static int
299c_to_class(Char ch)
300{
301    if (Isspace(ch))
302        return C_CLASS_WHITE;
303
304    if (isword(ch))
305        return C_CLASS_WORD;
306
307    return C_CLASS_OTHER;
308}
309
310static Char *
311c_prev_word(Char *p, Char *low, int n)
312{
313    p--;
314
315    if (!VImode) {
316	while (n--) {
317	    while ((p >= low) && !isword(*p))
318		p--;
319	    while ((p >= low) && isword(*p))
320		p--;
321	}
322
323	/* cp now points to one character before the word */
324	p++;
325	if (p < low)
326	    p = low;
327	/* cp now points where we want it */
328	return(p);
329    }
330
331    while (n--) {
332        int  c_class;
333
334        if (p < low)
335            break;
336
337        /* scan until beginning of current word (may be all whitespace!) */
338        c_class = c_to_class(*p);
339        while ((p >= low) && c_class == c_to_class(*p))
340            p--;
341
342        /* if this was a non_whitespace word, we're ready */
343        if (c_class != C_CLASS_WHITE)
344            continue;
345
346        /* otherwise, move back to beginning of the word just found */
347        c_class = c_to_class(*p);
348        while ((p >= low) && c_class == c_to_class(*p))
349            p--;
350    }
351
352    p++;                        /* correct overshoot */
353
354    return (p);
355}
356
357static Char *
358c_next_word(Char *p, Char *high, int n)
359{
360    if (!VImode) {
361	while (n--) {
362	    while ((p < high) && !isword(*p))
363		p++;
364	    while ((p < high) && isword(*p))
365		p++;
366	}
367	if (p > high)
368	    p = high;
369	/* p now points where we want it */
370	return(p);
371    }
372
373    while (n--) {
374        int  c_class;
375
376        if (p >= high)
377            break;
378
379        /* scan until end of current word (may be all whitespace!) */
380        c_class = c_to_class(*p);
381        while ((p < high) && c_class == c_to_class(*p))
382            p++;
383
384        /* if this was all whitespace, we're ready */
385        if (c_class == C_CLASS_WHITE)
386            continue;
387
388	/* if we've found white-space at the end of the word, skip it */
389        while ((p < high) && c_to_class(*p) == C_CLASS_WHITE)
390            p++;
391    }
392
393    p--;                        /* correct overshoot */
394
395    return (p);
396}
397
398static Char *
399c_nexword(Char *p, Char *high, int n)
400{
401    while (n--) {
402	while ((p < high) && !Isspace(*p))
403	    p++;
404	while ((p < high) && Isspace(*p))
405	    p++;
406    }
407
408    if (p > high)
409	p = high;
410    /* p now points where we want it */
411    return(p);
412}
413
414/*
415 * Expand-History (originally "Magic-Space") code added by
416 * Ray Moody <ray@gibbs.physics.purdue.edu>
417 * this is a neat, but odd, addition.
418 */
419
420/*
421 * c_number: Ignore character p points to, return number appearing after that.
422 * A '$' by itself means a big number; "$-" is for negative; '^' means 1.
423 * Return p pointing to last char used.
424 */
425
426/*
427 * dval is the number to subtract from for things like $-3
428 */
429
430static Char *
431c_number(Char *p, int *num, int dval)
432{
433    int i;
434    int sign = 1;
435
436    if (*++p == '^') {
437	*num = 1;
438	return(p);
439    }
440    if (*p == '$') {
441	if (*++p != '-') {
442	    *num = INT_MAX;	/* Handle $ */
443	    return(--p);
444	}
445	sign = -1;		/* Handle $- */
446	++p;
447    }
448    for (i = 0; *p >= '0' && *p <= '9'; i = 10 * i + *p++ - '0')
449	continue;
450    *num = (sign < 0 ? dval - i : i);
451    return(--p);
452}
453
454/*
455 * excl_expand: There is an excl to be expanded to p -- do the right thing
456 * with it and return a version of p advanced over the expanded stuff.  Also,
457 * update tsh_cur and related things as appropriate...
458 */
459
460static Char *
461c_expand(Char *p)
462{
463    Char *q;
464    struct Hist *h = Histlist.Hnext;
465    struct wordent *l;
466    int     i, from, to, dval;
467    int    all_dig;
468    int    been_once = 0;
469    Char   *op = p;
470    Char   *buf;
471    size_t buf_len;
472    Char   *modbuf;
473
474    buf = NULL;
475    if (!h)
476	goto excl_err;
477excl_sw:
478    switch (*(q = p + 1)) {
479
480    case '^':
481	buf = expand_lex(&h->Hlex, 1, 1);
482	break;
483
484    case '$':
485	if ((l = (h->Hlex).prev) != 0)
486	    buf = expand_lex(l->prev->prev, 0, 0);
487	break;
488
489    case '*':
490	buf = expand_lex(&h->Hlex, 1, INT_MAX);
491	break;
492
493    default:
494	if (been_once) {	/* unknown argument */
495	    /* assume it's a modifier, e.g. !foo:h, and get whole cmd */
496	    buf = expand_lex(&h->Hlex, 0, INT_MAX);
497	    q -= 2;
498	    break;
499	}
500	been_once = 1;
501
502	if (*q == ':')		/* short form: !:arg */
503	    --q;
504
505	if (HIST != '\0' && *q != HIST) {
506	    /*
507	     * Search for a space, tab, or colon.  See if we have a number (as
508	     * in !1234:xyz).  Remember the number.
509	     */
510	    for (i = 0, all_dig = 1;
511		 *q != ' ' && *q != '\t' && *q != ':' && q < Cursor; q++) {
512		/*
513		 * PWP: !-4 is a valid history argument too, therefore the test
514		 * is if not a digit, or not a - as the first character.
515		 */
516		if ((*q < '0' || *q > '9') && (*q != '-' || q != p + 1))
517		    all_dig = 0;
518		else if (*q == '-')
519		    all_dig = 2;/* we are sneeky about this */
520		else
521		    i = 10 * i + *q - '0';
522	    }
523	    --q;
524
525	    /*
526	     * If we have a number, search for event i.  Otherwise, search for
527	     * a named event (as in !foo).  (In this case, I is the length of
528	     * the named event).
529	     */
530	    if (all_dig) {
531		if (all_dig == 2)
532		    i = -i;	/* make it negitive */
533		if (i < 0)	/* if !-4 (for example) */
534		    i = eventno + 1 + i;	/* remember: i is < 0 */
535		for (; h; h = h->Hnext) {
536		    if (h->Hnum == i)
537			break;
538		}
539	    }
540	    else {
541		for (i = (int) (q - p); h; h = h->Hnext) {
542		    if ((l = &h->Hlex) != 0) {
543			if (!Strncmp(p + 1, l->next->word, (size_t) i))
544			    break;
545		    }
546		}
547	    }
548	}
549	if (!h)
550	    goto excl_err;
551	if (q[1] == ':' || q[1] == '-' || q[1] == '*' ||
552	    q[1] == '$' || q[1] == '^') {	/* get some args */
553	    p = q[1] == ':' ? ++q : q;
554	    /*
555	     * Go handle !foo:*
556	     */
557	    if ((q[1] < '0' || q[1] > '9') &&
558		q[1] != '-' && q[1] != '$' && q[1] != '^')
559		goto excl_sw;
560	    /*
561	     * Go handle !foo:$
562	     */
563	    if (q[1] == '$' && (q[2] != '-' || q[3] < '0' || q[3] > '9'))
564		goto excl_sw;
565	    /*
566	     * Count up the number of words in this event.  Store it in dval.
567	     * Dval will be fed to number.
568	     */
569	    dval = 0;
570	    if ((l = h->Hlex.prev) != 0) {
571		for (l = l->prev; l != h->Hlex.next; l = l->prev, dval++)
572		    continue;
573	    }
574	    if (!dval)
575		goto excl_err;
576	    if (q[1] == '-')
577		from = 0;
578	    else
579		q = c_number(q, &from, dval);
580	    if (q[1] == '-') {
581		++q;
582		if ((q[1] < '0' || q[1] > '9') && q[1] != '$')
583		    to = dval - 1;
584		else
585		    q = c_number(q, &to, dval);
586	    }
587	    else if (q[1] == '*') {
588		++q;
589		to = INT_MAX;
590	    }
591	    else {
592		to = from;
593	    }
594	    if (from < 0 || to < from)
595		goto excl_err;
596	    buf = expand_lex(&h->Hlex, from, to);
597	}
598	else			/* get whole cmd */
599	    buf = expand_lex(&h->Hlex, 0, INT_MAX);
600	break;
601    }
602    if (buf == NULL)
603	buf = SAVE("");
604
605    /*
606     * Apply modifiers, if any.
607     */
608    if (q[1] == ':') {
609	modbuf = buf;
610	while (q[1] == ':' && modbuf != NULL) {
611	    switch (q[2]) {
612	    case 'r':
613	    case 'e':
614	    case 'h':
615	    case 't':
616	    case 'q':
617	    case 'x':
618	    case 'u':
619	    case 'l':
620		if ((modbuf = domod(buf, (int) q[2])) != NULL) {
621		    xfree(buf);
622		    buf = modbuf;
623		}
624		++q;
625		break;
626
627	    case 'a':
628	    case 'g':
629		/* Not implemented; this needs to be done before expanding
630		 * lex. We don't have the words available to us anymore.
631		 */
632		++q;
633		break;
634
635	    case 'p':
636		/* Ok */
637		++q;
638		break;
639
640	    case '\0':
641		break;
642
643	    default:
644		++q;
645		break;
646	    }
647	    if (q[1])
648		++q;
649	}
650    }
651
652    buf_len = Strlen(buf);
653    /*
654     * Now replace the text from op to q inclusive with the text from buf.
655     */
656    q++;
657
658    /*
659     * Now replace text non-inclusively like a real CS major!
660     */
661    if (LastChar + buf_len - (q - op) >= InputLim)
662	goto excl_err;
663    (void) memmove(op + buf_len, q, (LastChar - q) * sizeof(Char));
664    LastChar += buf_len - (q - op);
665    Cursor += buf_len - (q - op);
666    (void) memcpy(op, buf, buf_len * sizeof(Char));
667    *LastChar = '\0';
668    xfree(buf);
669    return op + buf_len;
670excl_err:
671    xfree(buf);
672    SoundBeep();
673    return(op + 1);
674}
675
676/*
677 * c_excl: An excl has been found at point p -- back up and find some white
678 * space (or the beginning of the buffer) and properly expand all the excl's
679 * from there up to the current cursor position. We also avoid (trying to)
680 * expanding '>!'
681 * Returns number of expansions attempted (doesn't matter whether they succeeded
682 * or not).
683 */
684
685static int
686c_excl(Char *p)
687{
688    int i;
689    Char *q;
690    int nr_exp;
691
692    /*
693     * if />[SPC TAB]*![SPC TAB]/, back up p to just after the >. otherwise,
694     * back p up to just before the current word.
695     */
696    if ((p[1] == ' ' || p[1] == '\t') &&
697	(p[-1] == ' ' || p[-1] == '\t' || p[-1] == '>')) {
698	for (q = p - 1; q > InputBuf && (*q == ' ' || *q == '\t'); --q)
699	    continue;
700	if (*q == '>')
701	    ++p;
702    }
703    else {
704	while (*p != ' ' && *p != '\t' && p > InputBuf)
705	    --p;
706    }
707
708    /*
709     * Forever: Look for history char.  (Stop looking when we find the cursor.)
710     * Count backslashes.  If odd, skip history char.  Expand if even number of
711     * backslashes.
712     */
713    nr_exp = 0;
714    for (;;) {
715	if (HIST != '\0')
716	    while (*p != HIST && p < Cursor)
717		++p;
718	for (i = 1; (p - i) >= InputBuf && p[-i] == '\\'; i++)
719	    continue;
720	if (i % 2 == 0)
721	    ++p;
722	if (p >= Cursor)   /* all done */
723	    return nr_exp;
724	if (i % 2 == 1) {
725	    p = c_expand(p);
726	    ++nr_exp;
727	}
728    }
729}
730
731
732static int
733c_substitute(void)
734{
735    Char *p;
736    int  nr_exp;
737
738    /*
739     * Start p out one character before the cursor.  Move it backwards looking
740     * for white space, the beginning of the line, or a history character.
741     */
742    for (p = Cursor - 1;
743	 p > InputBuf && *p != ' ' && *p != '\t' && *p && *p != HIST; --p)
744	continue;
745
746    /*
747     * If we found a history character, go expand it.
748     */
749    if (p >= InputBuf && HIST != '\0' && *p == HIST)
750	nr_exp = c_excl(p);
751    else
752        nr_exp = 0;
753    Refresh();
754
755    return nr_exp;
756}
757
758static void
759c_delfini(void)		/* Finish up delete action */
760{
761    int Size;
762
763    if (ActionFlag & TCSHOP_INSERT)
764	c_alternativ_key_map(0);
765
766    ActionFlag = TCSHOP_NOP;
767
768    if (ActionPos == 0)
769	return;
770
771    UndoAction = TCSHOP_INSERT;
772
773    if (Cursor > ActionPos) {
774	Size = (int) (Cursor-ActionPos);
775	c_delbefore(Size);
776	RefCursor();
777    }
778    else if (Cursor < ActionPos) {
779	Size = (int)(ActionPos-Cursor);
780	c_delafter(Size);
781    }
782    else  {
783	Size = 1;
784	c_delafter(Size);
785    }
786    UndoPtr = Cursor;
787    UndoSize = Size;
788}
789
790static Char *
791c_endword(Char *p, Char *high, int n, Char *delim)
792{
793    Char inquote = 0;
794    p++;
795
796    while (n--) {
797        while (p < high) {	/* Skip non-word chars */
798	  if (!Strchr(delim, *p) || *(p-1) == (Char)'\\')
799	    break;
800	  p++;
801        }
802	while (p < high) {	/* Skip string */
803	  if ((*p == (Char)'\'' || *p == (Char)'"')) { /* Quotation marks? */
804	    if (inquote || *(p-1) != (Char)'\\') { /* Should it be honored? */
805	      if (inquote == 0) inquote = *p;
806	      else if (inquote == *p) inquote = 0;
807	    }
808	  }
809	  /* Break if unquoted non-word char */
810	  if (!inquote && Strchr(delim, *p) && *(p-1) != (Char)'\\')
811	    break;
812	  p++;
813	}
814    }
815
816    p--;
817    return(p);
818}
819
820
821static Char *
822c_eword(Char *p, Char *high, int n)
823{
824    p++;
825
826    while (n--) {
827        int  c_class;
828
829        if (p >= high)
830            break;
831
832        /* scan until end of current word (may be all whitespace!) */
833        c_class = c_to_class(*p);
834        while ((p < high) && c_class == c_to_class(*p))
835            p++;
836
837        /* if this was a non_whitespace word, we're ready */
838        if (c_class != C_CLASS_WHITE)
839            continue;
840
841        /* otherwise, move to the end of the word just found */
842        c_class = c_to_class(*p);
843        while ((p < high) && c_class == c_to_class(*p))
844            p++;
845    }
846
847    p--;
848    return(p);
849}
850
851/* Set the max length of the kill ring */
852void
853SetKillRing(int max)
854{
855    CStr *new;
856    int count, i, j;
857
858    if (max < 1)
859	max = 1;		/* no ring, but always one buffer */
860    if (max == KillRingMax)
861	return;
862    new = xcalloc(max, sizeof(CStr));
863    if (KillRing != NULL) {
864	if (KillRingLen != 0) {
865	    if (max >= KillRingLen) {
866		count = KillRingLen;
867		j = KillPos;
868	    } else {
869		count = max;
870		j = (KillPos - count + KillRingLen) % KillRingLen;
871	    }
872	    for (i = 0; i < KillRingLen; i++) {
873		if (i < count)	/* copy latest */
874		    new[i] = KillRing[j];
875		else		/* free the others */
876		    xfree(KillRing[j].buf);
877		j = (j + 1) % KillRingLen;
878	    }
879	    KillRingLen = count;
880	    KillPos = count % max;
881	    YankPos = count - 1;
882	}
883	xfree(KillRing);
884    }
885    KillRing = new;
886    KillRingMax = max;
887}
888
889/* Push string from start upto (but not including) end onto kill ring */
890static void
891c_push_kill(Char *start, Char *end)
892{
893    CStr save, *pos;
894    Char *dp, *cp, *kp;
895    int len = end - start, i, j, k;
896
897    /* Check for duplicates? */
898    if (KillRingLen > 0 && (dp = varval(STRkilldup)) != STRNULL) {
899	YankPos = (KillPos - 1 + KillRingLen) % KillRingLen;
900	if (eq(dp, STRerase)) {	/* erase earlier one (actually move up) */
901	    j = YankPos;
902	    for (i = 0; i < KillRingLen; i++) {
903		if (Strncmp(KillRing[j].buf, start, (size_t) len) == 0 &&
904		    KillRing[j].buf[len] == '\0') {
905		    save = KillRing[j];
906		    for ( ; i > 0; i--) {
907			k = j;
908			j = (j + 1) % KillRingLen;
909			KillRing[k] = KillRing[j];
910		    }
911		    KillRing[j] = save;
912		    return;
913		}
914		j = (j - 1 + KillRingLen) % KillRingLen;
915	    }
916	} else if (eq(dp, STRall)) { /* skip if any earlier */
917	    for (i = 0; i < KillRingLen; i++)
918		if (Strncmp(KillRing[i].buf, start, (size_t) len) == 0 &&
919		    KillRing[i].buf[len] == '\0')
920		    return;
921	} else if (eq(dp, STRprev)) { /* skip if immediately previous */
922	    j = YankPos;
923	    if (Strncmp(KillRing[j].buf, start, (size_t) len) == 0 &&
924		KillRing[j].buf[len] == '\0')
925		return;
926	}
927    }
928
929    /* No duplicate, go ahead and push */
930    len++;			/* need space for '\0' */
931    YankPos = KillPos;
932    if (KillRingLen < KillRingMax)
933	KillRingLen++;
934    pos = &KillRing[KillPos];
935    KillPos = (KillPos + 1) % KillRingMax;
936    if (pos->len < len) {
937	pos->buf = xrealloc(pos->buf, len * sizeof(Char));
938	pos->len = len;
939    }
940    cp = start;
941    kp = pos->buf;
942    while (cp < end)
943	*kp++ = *cp++;
944    *kp = '\0';
945}
946
947/* Save InputBuf etc in SavedBuf etc for restore after cmd exec */
948static void
949c_save_inputbuf(void)
950{
951    SavedBuf.len = 0;
952    Strbuf_append(&SavedBuf, InputBuf);
953    Strbuf_terminate(&SavedBuf);
954    LastSaved = LastChar - InputBuf;
955    CursSaved = Cursor - InputBuf;
956    HistSaved = Hist_num;
957    RestoreSaved = 1;
958}
959
960CCRETVAL
961GetHistLine(void)
962{
963    struct Hist *hp;
964    int     h;
965
966    if (Hist_num == 0) {	/* if really the current line */
967	if (HistBuf.s != NULL)
968	    copyn(InputBuf, HistBuf.s, INBUFSIZE);/*FIXBUF*/
969	else
970	    *InputBuf = '\0';
971	LastChar = InputBuf + HistBuf.len;
972
973#ifdef KSHVI
974    if (VImode)
975	Cursor = InputBuf;
976    else
977#endif /* KSHVI */
978	Cursor = LastChar;
979
980	return(CC_REFRESH);
981    }
982
983    hp = Histlist.Hnext;
984    if (hp == NULL)
985	return(CC_ERROR);
986
987    for (h = 1; h < Hist_num; h++) {
988	if ((hp->Hnext) == NULL) {
989	    Hist_num = h;
990	    return(CC_ERROR);
991	}
992	hp = hp->Hnext;
993    }
994
995    if (HistLit && hp->histline) {
996	copyn(InputBuf, hp->histline, INBUFSIZE);/*FIXBUF*/
997	CurrentHistLit = 1;
998    }
999    else {
1000	Char *p;
1001
1002	p = sprlex(&hp->Hlex);
1003	copyn(InputBuf, p, sizeof(InputBuf) / sizeof(Char));/*FIXBUF*/
1004	xfree(p);
1005	CurrentHistLit = 0;
1006    }
1007    LastChar = Strend(InputBuf);
1008
1009    if (LastChar > InputBuf) {
1010	if (LastChar[-1] == '\n')
1011	    LastChar--;
1012#if 0
1013	if (LastChar[-1] == ' ')
1014	    LastChar--;
1015#endif
1016	if (LastChar < InputBuf)
1017	    LastChar = InputBuf;
1018    }
1019
1020#ifdef KSHVI
1021    if (VImode)
1022	Cursor = InputBuf;
1023    else
1024#endif /* KSHVI */
1025	Cursor = LastChar;
1026
1027    return(CC_REFRESH);
1028}
1029
1030static CCRETVAL
1031c_search_line(Char *pattern, int dir)
1032{
1033    Char *cp;
1034    size_t len;
1035
1036    len = Strlen(pattern);
1037
1038    if (dir == F_UP_SEARCH_HIST) {
1039	for (cp = Cursor; cp >= InputBuf; cp--)
1040	    if (Strncmp(cp, pattern, len) == 0 ||
1041		Gmatch(cp, pattern)) {
1042		Cursor = cp;
1043		return(CC_NORM);
1044	    }
1045	return(CC_ERROR);
1046    } else {
1047	for (cp = Cursor; *cp != '\0' && cp < InputLim; cp++)
1048	    if (Strncmp(cp, pattern, len) == 0 ||
1049		Gmatch(cp, pattern)) {
1050		Cursor = cp;
1051		return(CC_NORM);
1052	    }
1053	return(CC_ERROR);
1054    }
1055}
1056
1057static CCRETVAL
1058e_inc_search(int dir)
1059{
1060    static const Char STRfwd[] = { 'f', 'w', 'd', '\0' },
1061		      STRbck[] = { 'b', 'c', 'k', '\0' };
1062    static Char pchar = ':';	/* ':' = normal, '?' = failed */
1063    static Char endcmd[2];
1064    const Char *cp;
1065    Char ch,
1066	*oldCursor = Cursor,
1067	oldpchar = pchar;
1068    CCRETVAL ret = CC_NORM;
1069    int oldHist_num = Hist_num,
1070	oldpatlen = patbuf.len,
1071	newdir = dir,
1072        done, redo;
1073
1074    if (LastChar + sizeof(STRfwd)/sizeof(Char) + 2 + patbuf.len >= InputLim)
1075	return(CC_ERROR);
1076
1077    for (;;) {
1078
1079	if (patbuf.len == 0) {	/* first round */
1080	    pchar = ':';
1081	    Strbuf_append1(&patbuf, '*');
1082	}
1083	done = redo = 0;
1084	*LastChar++ = '\n';
1085	for (cp = newdir == F_UP_SEARCH_HIST ? STRbck : STRfwd;
1086	     *cp; *LastChar++ = *cp++)
1087	    continue;
1088	*LastChar++ = pchar;
1089	for (cp = &patbuf.s[1]; cp < &patbuf.s[patbuf.len];
1090	     *LastChar++ = *cp++)
1091	    continue;
1092	*LastChar = '\0';
1093	if (adrof(STRhighlight) && pchar == ':') {
1094	    /* if the no-glob-search patch is applied, remove the - 1 below */
1095	    IncMatchLen = patbuf.len - 1;
1096	    ClearLines();
1097	    ClearDisp();
1098	}
1099	Refresh();
1100
1101	if (GetNextChar(&ch) != 1)
1102	    return(e_send_eof(0));
1103
1104	switch (GetCmdChar(ch)) {
1105	case F_INSERT:
1106	case F_DIGIT:
1107	case F_MAGIC_SPACE:
1108	    if (LastChar + 1 >= InputLim) /*FIXBUF*/
1109		SoundBeep();
1110	    else {
1111		Strbuf_append1(&patbuf, ch);
1112		*LastChar++ = ch;
1113		*LastChar = '\0';
1114		Refresh();
1115	    }
1116	    break;
1117
1118	case F_INC_FWD:
1119	    newdir = F_DOWN_SEARCH_HIST;
1120	    redo++;
1121	    break;
1122
1123	case F_INC_BACK:
1124	    newdir = F_UP_SEARCH_HIST;
1125	    redo++;
1126	    break;
1127
1128	case F_DELPREV:
1129	    if (patbuf.len > 1)
1130		done++;
1131	    else
1132		SoundBeep();
1133	    break;
1134
1135	default:
1136	    switch (ASC(ch)) {
1137	    case 0007:		/* ^G: Abort */
1138		ret = CC_ERROR;
1139		done++;
1140		break;
1141
1142	    case 0027:		/* ^W: Append word */
1143		/* No can do if globbing characters in pattern */
1144		for (cp = &patbuf.s[1]; ; cp++)
1145		    if (cp >= &patbuf.s[patbuf.len]) {
1146			Cursor += patbuf.len - 1;
1147			cp = c_next_word(Cursor, LastChar, 1);
1148			while (Cursor < cp && *Cursor != '\n') {
1149			    if (LastChar + 1 >= InputLim) {/*FIXBUF*/
1150				SoundBeep();
1151				break;
1152			    }
1153			    Strbuf_append1(&patbuf, *Cursor);
1154			    *LastChar++ = *Cursor++;
1155			}
1156			Cursor = oldCursor;
1157			*LastChar = '\0';
1158			Refresh();
1159			break;
1160		    } else if (isglob(*cp)) {
1161			SoundBeep();
1162			break;
1163		    }
1164		break;
1165
1166	    default:		/* Terminate and execute cmd */
1167		endcmd[0] = ch;
1168		PushMacro(endcmd);
1169		/*FALLTHROUGH*/
1170
1171	    case 0033:		/* ESC: Terminate */
1172		ret = CC_REFRESH;
1173		done++;
1174		break;
1175	    }
1176	    break;
1177	}
1178
1179	while (LastChar > InputBuf && *LastChar != '\n')
1180	    *LastChar-- = '\0';
1181	*LastChar = '\0';
1182
1183	if (!done) {
1184
1185	    /* Can't search if unmatched '[' */
1186	    for (cp = &patbuf.s[patbuf.len - 1], ch = ']'; cp > patbuf.s; cp--)
1187		if (*cp == '[' || *cp == ']') {
1188		    ch = *cp;
1189		    break;
1190		}
1191
1192	    if (patbuf.len > 1 && ch != '[') {
1193		if (redo && newdir == dir) {
1194		    if (pchar == '?') {	/* wrap around */
1195			Hist_num = newdir == F_UP_SEARCH_HIST ? 0 : INT_MAX;
1196			if (GetHistLine() == CC_ERROR)
1197			    /* Hist_num was fixed by first call */
1198			    (void) GetHistLine();
1199			Cursor = newdir == F_UP_SEARCH_HIST ?
1200			    LastChar : InputBuf;
1201		    } else
1202			Cursor += newdir == F_UP_SEARCH_HIST ? -1 : 1;
1203		}
1204		Strbuf_append1(&patbuf, '*');
1205		Strbuf_terminate(&patbuf);
1206		if (Cursor < InputBuf || Cursor > LastChar ||
1207		    (ret = c_search_line(&patbuf.s[1], newdir)) == CC_ERROR) {
1208		    LastCmd = (KEYCMD) newdir; /* avoid c_hsetpat */
1209		    ret = newdir == F_UP_SEARCH_HIST ?
1210			e_up_search_hist(0) : e_down_search_hist(0);
1211		    if (ret != CC_ERROR) {
1212			Cursor = newdir == F_UP_SEARCH_HIST ?
1213			    LastChar : InputBuf;
1214			(void) c_search_line(&patbuf.s[1], newdir);
1215		    }
1216		}
1217		patbuf.s[--patbuf.len] = '\0';
1218		if (ret == CC_ERROR) {
1219		    SoundBeep();
1220		    if (Hist_num != oldHist_num) {
1221			Hist_num = oldHist_num;
1222			if (GetHistLine() == CC_ERROR)
1223			    return(CC_ERROR);
1224		    }
1225		    Cursor = oldCursor;
1226		    pchar = '?';
1227		} else {
1228		    pchar = ':';
1229		}
1230	    }
1231
1232	    ret = e_inc_search(newdir);
1233
1234	    if (ret == CC_ERROR && pchar == '?' && oldpchar == ':') {
1235		/* break abort of failed search at last non-failed */
1236		ret = CC_NORM;
1237	    }
1238
1239	}
1240
1241	if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) {
1242	    /* restore on normal return or error exit */
1243	    pchar = oldpchar;
1244	    patbuf.len = oldpatlen;
1245	    if (Hist_num != oldHist_num) {
1246		Hist_num = oldHist_num;
1247		if (GetHistLine() == CC_ERROR)
1248		    return(CC_ERROR);
1249	    }
1250	    Cursor = oldCursor;
1251	    if (ret == CC_ERROR)
1252		Refresh();
1253	}
1254	if (done || ret != CC_NORM)
1255	    return(ret);
1256
1257    }
1258
1259}
1260
1261static CCRETVAL
1262v_search(int dir)
1263{
1264    struct Strbuf tmpbuf = Strbuf_INIT;
1265    Char ch;
1266    Char *oldbuf;
1267    Char *oldlc, *oldc;
1268
1269    cleanup_push(&tmpbuf, Strbuf_cleanup);
1270    oldbuf = Strsave(InputBuf);
1271    cleanup_push(oldbuf, xfree);
1272    oldlc = LastChar;
1273    oldc = Cursor;
1274    Strbuf_append1(&tmpbuf, '*');
1275
1276    InputBuf[0] = '\0';
1277    LastChar = InputBuf;
1278    Cursor = InputBuf;
1279    searchdir = dir;
1280
1281    c_insert(2);	/* prompt + '\n' */
1282    *Cursor++ = '\n';
1283    *Cursor++ = dir == F_UP_SEARCH_HIST ? '?' : '/';
1284    Refresh();
1285    for (ch = 0;ch == 0;) {
1286	if (GetNextChar(&ch) != 1) {
1287	    cleanup_until(&tmpbuf);
1288	    return(e_send_eof(0));
1289	}
1290	switch (ASC(ch)) {
1291	case 0010:	/* Delete and backspace */
1292	case 0177:
1293	    if (tmpbuf.len > 1) {
1294		*Cursor-- = '\0';
1295		LastChar = Cursor;
1296		tmpbuf.len--;
1297	    }
1298	    else {
1299		copyn(InputBuf, oldbuf, INBUFSIZE);/*FIXBUF*/
1300		LastChar = oldlc;
1301		Cursor = oldc;
1302		cleanup_until(&tmpbuf);
1303		return(CC_REFRESH);
1304	    }
1305	    Refresh();
1306	    ch = 0;
1307	    break;
1308
1309	case 0033:	/* ESC */
1310#ifdef IS_ASCII
1311	case '\r':	/* Newline */
1312	case '\n':
1313#else
1314	case '\012':    /* ASCII Line feed */
1315	case '\015':    /* ASCII (or EBCDIC) Return */
1316#endif
1317	    break;
1318
1319	default:
1320	    Strbuf_append1(&tmpbuf, ch);
1321	    *Cursor++ = ch;
1322	    LastChar = Cursor;
1323	    Refresh();
1324	    ch = 0;
1325	    break;
1326	}
1327    }
1328    cleanup_until(oldbuf);
1329
1330    if (tmpbuf.len == 1) {
1331	/*
1332	 * Use the old pattern, but wild-card it.
1333	 */
1334	if (patbuf.len == 0) {
1335	    InputBuf[0] = '\0';
1336	    LastChar = InputBuf;
1337	    Cursor = InputBuf;
1338	    Refresh();
1339	    cleanup_until(&tmpbuf);
1340	    return(CC_ERROR);
1341	}
1342	if (patbuf.s[0] != '*') {
1343	    oldbuf = Strsave(patbuf.s);
1344	    patbuf.len = 0;
1345	    Strbuf_append1(&patbuf, '*');
1346	    Strbuf_append(&patbuf, oldbuf);
1347	    xfree(oldbuf);
1348	    Strbuf_append1(&patbuf, '*');
1349	    Strbuf_terminate(&patbuf);
1350	}
1351    }
1352    else {
1353	Strbuf_append1(&tmpbuf, '*');
1354	Strbuf_terminate(&tmpbuf);
1355	patbuf.len = 0;
1356	Strbuf_append(&patbuf, tmpbuf.s);
1357	Strbuf_terminate(&patbuf);
1358    }
1359    cleanup_until(&tmpbuf);
1360    LastCmd = (KEYCMD) dir; /* avoid c_hsetpat */
1361    Cursor = LastChar = InputBuf;
1362    if ((dir == F_UP_SEARCH_HIST ? e_up_search_hist(0) :
1363				   e_down_search_hist(0)) == CC_ERROR) {
1364	Refresh();
1365	return(CC_ERROR);
1366    }
1367    else {
1368	if (ASC(ch) == 0033) {
1369	    Refresh();
1370	    *LastChar++ = '\n';
1371	    *LastChar = '\0';
1372	    PastBottom();
1373	    return(CC_NEWLINE);
1374	}
1375	else
1376	    return(CC_REFRESH);
1377    }
1378}
1379
1380/*
1381 * semi-PUBLIC routines.  Any routine that is of type CCRETVAL is an
1382 * entry point, called from the CcKeyMap indirected into the
1383 * CcFuncTbl array.
1384 */
1385
1386/*ARGSUSED*/
1387CCRETVAL
1388v_cmd_mode(Char c)
1389{
1390    USE(c);
1391    InsertPos = 0;
1392    ActionFlag = TCSHOP_NOP;	/* [Esc] cancels pending action */
1393    ActionPos = 0;
1394    DoingArg = 0;
1395    if (UndoPtr > Cursor)
1396	UndoSize = (int)(UndoPtr - Cursor);
1397    else
1398	UndoSize = (int)(Cursor - UndoPtr);
1399
1400    inputmode = MODE_INSERT;
1401    c_alternativ_key_map(1);
1402#ifdef notdef
1403    /*
1404     * We don't want to move the cursor, because all the editing
1405     * commands don't include the character under the cursor.
1406     */
1407    if (Cursor > InputBuf)
1408	Cursor--;
1409#endif
1410    RefCursor();
1411    return(CC_NORM);
1412}
1413
1414/*ARGSUSED*/
1415CCRETVAL
1416e_unassigned(Char c)
1417{				/* bound to keys that arn't really assigned */
1418    USE(c);
1419    SoundBeep();
1420    flush();
1421    return(CC_NORM);
1422}
1423
1424#ifdef notyet
1425static CCRETVAL
1426e_insert_str(Char *c)
1427{
1428    int i, n;
1429
1430    n = Strlen(c);
1431    if (LastChar + Argument * n >= InputLim)
1432	return(CC_ERROR);	/* end of buffer space */
1433    if (inputmode != MODE_INSERT) {
1434	c_delafter(Argument * Strlen(c));
1435    }
1436    c_insert(Argument * n);
1437    while (Argument--) {
1438	for (i = 0; i < n; i++)
1439	    *Cursor++ = c[i];
1440    }
1441    Refresh();
1442    return(CC_NORM);
1443}
1444#endif
1445
1446CCRETVAL
1447e_insert(Char c)
1448{
1449#ifndef SHORT_STRINGS
1450    c &= ASCII;			/* no meta chars ever */
1451#endif
1452
1453    if (!c)
1454	return(CC_ERROR);	/* no NULs in the input ever!! */
1455
1456    if (LastChar + Argument >= InputLim)
1457	return(CC_ERROR);	/* end of buffer space */
1458
1459    if (Argument == 1) {  	/* How was this optimized ???? */
1460
1461	if (inputmode != MODE_INSERT) {
1462	    UndoBuf[UndoSize++] = *Cursor;
1463	    UndoBuf[UndoSize] = '\0';
1464	    c_delafter(1);   /* Do NOT use the saving ONE */
1465    	}
1466
1467        c_insert(1);
1468	*Cursor++ = (Char) c;
1469	DoingArg = 0;		/* just in case */
1470	RefPlusOne(1);		/* fast refresh for one char. */
1471    }
1472    else {
1473	if (inputmode != MODE_INSERT) {
1474	    int i;
1475	    for(i = 0; i < Argument; i++)
1476		UndoBuf[UndoSize++] = *(Cursor + i);
1477
1478	    UndoBuf[UndoSize] = '\0';
1479	    c_delafter(Argument);   /* Do NOT use the saving ONE */
1480    	}
1481
1482        c_insert(Argument);
1483
1484	while (Argument--)
1485	    *Cursor++ = (Char) c;
1486	Refresh();
1487    }
1488
1489    if (inputmode == MODE_REPLACE_1)
1490	(void) v_cmd_mode(0);
1491
1492    return(CC_NORM);
1493}
1494
1495int
1496InsertStr(Char *s)		/* insert ASCIZ s at cursor (for complete) */
1497{
1498    int len;
1499
1500    if ((len = (int) Strlen(s)) <= 0)
1501	return -1;
1502    if (LastChar + len >= InputLim)
1503	return -1;		/* end of buffer space */
1504
1505    c_insert(len);
1506    while (len--)
1507	*Cursor++ = *s++;
1508    return 0;
1509}
1510
1511void
1512DeleteBack(int n)		/* delete the n characters before . */
1513{
1514    if (n <= 0)
1515	return;
1516    if (Cursor >= &InputBuf[n]) {
1517	c_delbefore(n);		/* delete before dot */
1518    }
1519}
1520
1521CCRETVAL
1522e_digit(Char c)			/* gray magic here */
1523{
1524    if (!Isdigit(c))
1525	return(CC_ERROR);	/* no NULs in the input ever!! */
1526
1527    if (DoingArg) {		/* if doing an arg, add this in... */
1528	if (LastCmd == F_ARGFOUR)	/* if last command was ^U */
1529	    Argument = c - '0';
1530	else {
1531	    if (Argument > 1000000)
1532		return CC_ERROR;
1533	    Argument = (Argument * 10) + (c - '0');
1534	}
1535	return(CC_ARGHACK);
1536    }
1537    else {
1538	if (LastChar + 1 >= InputLim)
1539	    return CC_ERROR;	/* end of buffer space */
1540
1541	if (inputmode != MODE_INSERT) {
1542	    UndoBuf[UndoSize++] = *Cursor;
1543	    UndoBuf[UndoSize] = '\0';
1544	    c_delafter(1);   /* Do NOT use the saving ONE */
1545    	}
1546	c_insert(1);
1547	*Cursor++ = (Char) c;
1548	DoingArg = 0;		/* just in case */
1549	RefPlusOne(1);		/* fast refresh for one char. */
1550    }
1551    return(CC_NORM);
1552}
1553
1554CCRETVAL
1555e_argdigit(Char c)		/* for ESC-n */
1556{
1557#ifdef IS_ASCII
1558    c &= ASCII;
1559#else
1560    c = CTL_ESC(ASC(c) & ASCII); /* stripping for EBCDIC done the ASCII way */
1561#endif
1562
1563    if (!Isdigit(c))
1564	return(CC_ERROR);	/* no NULs in the input ever!! */
1565
1566    if (DoingArg) {		/* if doing an arg, add this in... */
1567	if (Argument > 1000000)
1568	    return CC_ERROR;
1569	Argument = (Argument * 10) + (c - '0');
1570    }
1571    else {			/* else starting an argument */
1572	Argument = c - '0';
1573	DoingArg = 1;
1574    }
1575    return(CC_ARGHACK);
1576}
1577
1578CCRETVAL
1579v_zero(Char c)			/* command mode 0 for vi */
1580{
1581    if (DoingArg) {		/* if doing an arg, add this in... */
1582	if (Argument > 1000000)
1583	    return CC_ERROR;
1584	Argument = (Argument * 10) + (c - '0');
1585	return(CC_ARGHACK);
1586    }
1587    else {			/* else starting an argument */
1588	Cursor = InputBuf;
1589	if (ActionFlag & TCSHOP_DELETE) {
1590	   c_delfini();
1591	   return(CC_REFRESH);
1592        }
1593	RefCursor();		/* move the cursor */
1594	return(CC_NORM);
1595    }
1596}
1597
1598/*ARGSUSED*/
1599CCRETVAL
1600e_newline(Char c)
1601{				/* always ignore argument */
1602    USE(c);
1603    if (adrof(STRhighlight) && MarkIsSet) {
1604	MarkIsSet = 0;
1605	ClearLines();
1606	ClearDisp();
1607	Refresh();
1608    }
1609    MarkIsSet = 0;
1610
1611  /*  PastBottom();  NOW done in ed.inputl.c */
1612    *LastChar++ = '\n';		/* for the benefit of CSH */
1613    *LastChar = '\0';		/* just in case */
1614    if (VImode)
1615	InsertPos = InputBuf;	/* Reset editing position */
1616    return(CC_NEWLINE);
1617}
1618
1619/*ARGSUSED*/
1620CCRETVAL
1621e_newline_hold(Char c)
1622{
1623    USE(c);
1624    c_save_inputbuf();
1625    HistSaved = 0;
1626    *LastChar++ = '\n';		/* for the benefit of CSH */
1627    *LastChar = '\0';		/* just in case */
1628    return(CC_NEWLINE);
1629}
1630
1631/*ARGSUSED*/
1632CCRETVAL
1633e_newline_down_hist(Char c)
1634{
1635    USE(c);
1636    if (Hist_num > 1) {
1637	HistSaved = Hist_num;
1638    }
1639    *LastChar++ = '\n';		/* for the benefit of CSH */
1640    *LastChar = '\0';		/* just in case */
1641    return(CC_NEWLINE);
1642}
1643
1644/*ARGSUSED*/
1645CCRETVAL
1646e_send_eof(Char c)
1647{				/* for when ^D is ONLY send-eof */
1648    USE(c);
1649    PastBottom();
1650    *LastChar = '\0';		/* just in case */
1651    return(CC_EOF);
1652}
1653
1654/*ARGSUSED*/
1655CCRETVAL
1656e_complete(Char c)
1657{
1658    USE(c);
1659    *LastChar = '\0';		/* just in case */
1660    return(CC_COMPLETE);
1661}
1662
1663/*ARGSUSED*/
1664CCRETVAL
1665e_complete_back(Char c)
1666{
1667    USE(c);
1668    *LastChar = '\0';		/* just in case */
1669    return(CC_COMPLETE_BACK);
1670}
1671
1672/*ARGSUSED*/
1673CCRETVAL
1674e_complete_fwd(Char c)
1675{
1676    USE(c);
1677    *LastChar = '\0';		/* just in case */
1678    return(CC_COMPLETE_FWD);
1679}
1680
1681/*ARGSUSED*/
1682CCRETVAL
1683e_complete_all(Char c)
1684{
1685    USE(c);
1686    *LastChar = '\0';		/* just in case */
1687    return(CC_COMPLETE_ALL);
1688}
1689
1690/*ARGSUSED*/
1691CCRETVAL
1692v_cm_complete(Char c)
1693{
1694    USE(c);
1695    if (Cursor < LastChar)
1696	Cursor++;
1697    *LastChar = '\0';		/* just in case */
1698    return(CC_COMPLETE);
1699}
1700
1701/*ARGSUSED*/
1702CCRETVAL
1703e_toggle_hist(Char c)
1704{
1705    struct Hist *hp;
1706    int     h;
1707
1708    USE(c);
1709    *LastChar = '\0';		/* just in case */
1710
1711    if (Hist_num <= 0) {
1712	return CC_ERROR;
1713    }
1714
1715    hp = Histlist.Hnext;
1716    if (hp == NULL) {	/* this is only if no history */
1717	return(CC_ERROR);
1718    }
1719
1720    for (h = 1; h < Hist_num; h++)
1721	hp = hp->Hnext;
1722
1723    if (!CurrentHistLit) {
1724	if (hp->histline) {
1725	    copyn(InputBuf, hp->histline, INBUFSIZE);/*FIXBUF*/
1726	    CurrentHistLit = 1;
1727	}
1728	else {
1729	    return CC_ERROR;
1730	}
1731    }
1732    else {
1733	Char *p;
1734
1735	p = sprlex(&hp->Hlex);
1736	copyn(InputBuf, p, sizeof(InputBuf) / sizeof(Char));/*FIXBUF*/
1737	xfree(p);
1738	CurrentHistLit = 0;
1739    }
1740
1741    LastChar = Strend(InputBuf);
1742    if (LastChar > InputBuf) {
1743	if (LastChar[-1] == '\n')
1744	    LastChar--;
1745	if (LastChar[-1] == ' ')
1746	    LastChar--;
1747	if (LastChar < InputBuf)
1748	    LastChar = InputBuf;
1749    }
1750
1751#ifdef KSHVI
1752    if (VImode)
1753	Cursor = InputBuf;
1754    else
1755#endif /* KSHVI */
1756	Cursor = LastChar;
1757
1758    return(CC_REFRESH);
1759}
1760
1761/*ARGSUSED*/
1762CCRETVAL
1763e_up_hist(Char c)
1764{
1765    Char    beep = 0;
1766
1767    USE(c);
1768    UndoAction = TCSHOP_NOP;
1769    *LastChar = '\0';		/* just in case */
1770
1771    if (Hist_num == 0) {	/* save the current buffer away */
1772	HistBuf.len = 0;
1773	Strbuf_append(&HistBuf, InputBuf);
1774	Strbuf_terminate(&HistBuf);
1775    }
1776
1777    Hist_num += Argument;
1778
1779    if (GetHistLine() == CC_ERROR) {
1780	beep = 1;
1781	(void) GetHistLine(); /* Hist_num was fixed by first call */
1782    }
1783
1784    Refresh();
1785    if (beep)
1786	return(CC_ERROR);
1787    else
1788	return(CC_NORM);	/* was CC_UP_HIST */
1789}
1790
1791/*ARGSUSED*/
1792CCRETVAL
1793e_down_hist(Char c)
1794{
1795    USE(c);
1796    UndoAction = TCSHOP_NOP;
1797    *LastChar = '\0';		/* just in case */
1798
1799    Hist_num -= Argument;
1800
1801    if (Hist_num < 0) {
1802	Hist_num = 0;
1803	return(CC_ERROR);	/* make it beep */
1804    }
1805
1806    return(GetHistLine());
1807}
1808
1809
1810
1811/*
1812 * c_hmatch() return True if the pattern matches the prefix
1813 */
1814static int
1815c_hmatch(Char *str)
1816{
1817    if (Strncmp(patbuf.s, str, patbuf.len) == 0)
1818	return 1;
1819    return Gmatch(str, patbuf.s);
1820}
1821
1822/*
1823 * c_hsetpat(): Set the history seatch pattern
1824 */
1825static void
1826c_hsetpat(void)
1827{
1828    if (LastCmd != F_UP_SEARCH_HIST && LastCmd != F_DOWN_SEARCH_HIST) {
1829	patbuf.len = 0;
1830	Strbuf_appendn(&patbuf, InputBuf, Cursor - InputBuf);
1831	Strbuf_terminate(&patbuf);
1832    }
1833#ifdef SDEBUG
1834    xprintf("\nHist_num = %d\n", Hist_num);
1835    xprintf("patlen = %d\n", (int)patbuf.len);
1836    xprintf("patbuf = \"%S\"\n", patbuf.s);
1837    xprintf("Cursor %d LastChar %d\n", Cursor - InputBuf, LastChar - InputBuf);
1838#endif
1839}
1840
1841/*ARGSUSED*/
1842CCRETVAL
1843e_up_search_hist(Char c)
1844{
1845    struct Hist *hp;
1846    int h;
1847    int    found = 0;
1848
1849    USE(c);
1850    ActionFlag = TCSHOP_NOP;
1851    UndoAction = TCSHOP_NOP;
1852    *LastChar = '\0';		/* just in case */
1853    if (Hist_num < 0) {
1854#ifdef DEBUG_EDIT
1855	xprintf("%s: e_up_search_hist(): Hist_num < 0; resetting.\n", progname);
1856#endif
1857	Hist_num = 0;
1858	return(CC_ERROR);
1859    }
1860
1861    if (Hist_num == 0) {
1862	HistBuf.len = 0;
1863	Strbuf_append(&HistBuf, InputBuf);
1864	Strbuf_terminate(&HistBuf);
1865    }
1866
1867
1868    hp = Histlist.Hnext;
1869    if (hp == NULL)
1870	return(CC_ERROR);
1871
1872    c_hsetpat();		/* Set search pattern !! */
1873
1874    for (h = 1; h <= Hist_num; h++)
1875	hp = hp->Hnext;
1876
1877    while (hp != NULL) {
1878	Char *hl;
1879	int matched;
1880
1881	if (hp->histline == NULL)
1882	    hp->histline = sprlex(&hp->Hlex);
1883	if (HistLit)
1884	    hl = hp->histline;
1885	else {
1886	    hl = sprlex(&hp->Hlex);
1887	    cleanup_push(hl, xfree);
1888	}
1889#ifdef SDEBUG
1890	xprintf("Comparing with \"%S\"\n", hl);
1891#endif
1892	matched = (Strncmp(hl, InputBuf, (size_t) (LastChar - InputBuf)) ||
1893		   hl[LastChar-InputBuf]) && c_hmatch(hl);
1894	if (!HistLit)
1895	    cleanup_until(hl);
1896	if (matched) {
1897	    found++;
1898	    break;
1899	}
1900	h++;
1901	hp = hp->Hnext;
1902    }
1903
1904    if (!found) {
1905#ifdef SDEBUG
1906	xprintf("not found\n");
1907#endif
1908	return(CC_ERROR);
1909    }
1910
1911    Hist_num = h;
1912
1913    return(GetHistLine());
1914}
1915
1916/*ARGSUSED*/
1917CCRETVAL
1918e_down_search_hist(Char c)
1919{
1920    struct Hist *hp;
1921    int h;
1922    int    found = 0;
1923
1924    USE(c);
1925    ActionFlag = TCSHOP_NOP;
1926    UndoAction = TCSHOP_NOP;
1927    *LastChar = '\0';		/* just in case */
1928
1929    if (Hist_num == 0)
1930	return(CC_ERROR);
1931
1932    hp = Histlist.Hnext;
1933    if (hp == 0)
1934	return(CC_ERROR);
1935
1936    c_hsetpat();		/* Set search pattern !! */
1937
1938    for (h = 1; h < Hist_num && hp; h++) {
1939	Char *hl;
1940	if (hp->histline == NULL)
1941	    hp->histline = sprlex(&hp->Hlex);
1942	if (HistLit)
1943	    hl = hp->histline;
1944	else {
1945	    hl = sprlex(&hp->Hlex);
1946	    cleanup_push(hl, xfree);
1947	}
1948#ifdef SDEBUG
1949	xprintf("Comparing with \"%S\"\n", hl);
1950#endif
1951	if ((Strncmp(hl, InputBuf, (size_t) (LastChar - InputBuf)) ||
1952	     hl[LastChar-InputBuf]) && c_hmatch(hl))
1953	    found = h;
1954	if (!HistLit)
1955	    cleanup_until(hl);
1956	hp = hp->Hnext;
1957    }
1958
1959    if (!found) {		/* is it the current history number? */
1960	if (!c_hmatch(HistBuf.s)) {
1961#ifdef SDEBUG
1962	    xprintf("not found\n");
1963#endif
1964	    return(CC_ERROR);
1965	}
1966    }
1967
1968    Hist_num = found;
1969
1970    return(GetHistLine());
1971}
1972
1973/*ARGSUSED*/
1974CCRETVAL
1975e_helpme(Char c)
1976{
1977    USE(c);
1978    PastBottom();
1979    *LastChar = '\0';		/* just in case */
1980    return(CC_HELPME);
1981}
1982
1983/*ARGSUSED*/
1984CCRETVAL
1985e_correct(Char c)
1986{
1987    USE(c);
1988    *LastChar = '\0';		/* just in case */
1989    return(CC_CORRECT);
1990}
1991
1992/*ARGSUSED*/
1993CCRETVAL
1994e_correctl(Char c)
1995{
1996    USE(c);
1997    *LastChar = '\0';		/* just in case */
1998    return(CC_CORRECT_L);
1999}
2000
2001/*ARGSUSED*/
2002CCRETVAL
2003e_run_fg_editor(Char c)
2004{
2005    struct process *pp;
2006
2007    USE(c);
2008    if ((pp = find_stop_ed()) != NULL) {
2009	/* save our editor state so we can restore it */
2010	c_save_inputbuf();
2011	Hist_num = 0;		/* for the history commands */
2012
2013	/* put the tty in a sane mode */
2014	PastBottom();
2015	(void) Cookedmode();	/* make sure the tty is set up correctly */
2016
2017	/* do it! */
2018	fg_proc_entry(pp);
2019
2020	(void) Rawmode();	/* go on */
2021	Refresh();
2022	RestoreSaved = 0;
2023	HistSaved = 0;
2024    }
2025    return(CC_NORM);
2026}
2027
2028/*ARGSUSED*/
2029CCRETVAL
2030e_list_choices(Char c)
2031{
2032    USE(c);
2033    PastBottom();
2034    *LastChar = '\0';		/* just in case */
2035    return(CC_LIST_CHOICES);
2036}
2037
2038/*ARGSUSED*/
2039CCRETVAL
2040e_list_all(Char c)
2041{
2042    USE(c);
2043    PastBottom();
2044    *LastChar = '\0';		/* just in case */
2045    return(CC_LIST_ALL);
2046}
2047
2048/*ARGSUSED*/
2049CCRETVAL
2050e_list_glob(Char c)
2051{
2052    USE(c);
2053    PastBottom();
2054    *LastChar = '\0';		/* just in case */
2055    return(CC_LIST_GLOB);
2056}
2057
2058/*ARGSUSED*/
2059CCRETVAL
2060e_expand_glob(Char c)
2061{
2062    USE(c);
2063    *LastChar = '\0';		/* just in case */
2064    return(CC_EXPAND_GLOB);
2065}
2066
2067/*ARGSUSED*/
2068CCRETVAL
2069e_normalize_path(Char c)
2070{
2071    USE(c);
2072    *LastChar = '\0';		/* just in case */
2073    return(CC_NORMALIZE_PATH);
2074}
2075
2076/*ARGSUSED*/
2077CCRETVAL
2078e_normalize_command(Char c)
2079{
2080    USE(c);
2081    *LastChar = '\0';		/* just in case */
2082    return(CC_NORMALIZE_COMMAND);
2083}
2084
2085/*ARGSUSED*/
2086CCRETVAL
2087e_expand_vars(Char c)
2088{
2089    USE(c);
2090    *LastChar = '\0';		/* just in case */
2091    return(CC_EXPAND_VARS);
2092}
2093
2094/*ARGSUSED*/
2095CCRETVAL
2096e_which(Char c)
2097{				/* do a fast command line which(1) */
2098    USE(c);
2099    c_save_inputbuf();
2100    Hist_num = 0;		/* for the history commands */
2101    PastBottom();
2102    *LastChar = '\0';		/* just in case */
2103    return(CC_WHICH);
2104}
2105
2106/*ARGSUSED*/
2107CCRETVAL
2108e_last_item(Char c)
2109{				/* insert the last element of the prev. cmd */
2110    struct Hist *hp;
2111    struct wordent *wp, *firstp;
2112    int i;
2113    Char *expanded;
2114
2115    USE(c);
2116    if (Argument <= 0)
2117	return(CC_ERROR);
2118
2119    hp = Histlist.Hnext;
2120    if (hp == NULL) {	/* this is only if no history */
2121	return(CC_ERROR);
2122    }
2123
2124    wp = (hp->Hlex).prev;
2125
2126    if (wp->prev == (struct wordent *) NULL)
2127	return(CC_ERROR);	/* an empty history entry */
2128
2129    firstp = (hp->Hlex).next;
2130
2131    /* back up arg words in lex */
2132    for (i = 0; i < Argument && wp != firstp; i++) {
2133	wp = wp->prev;
2134    }
2135
2136    expanded = expand_lex(wp->prev, 0, i - 1);
2137    if (InsertStr(expanded)) {
2138	xfree(expanded);
2139	return(CC_ERROR);
2140    }
2141
2142    xfree(expanded);
2143    return(CC_REFRESH);
2144}
2145
2146/*ARGSUSED*/
2147CCRETVAL
2148e_dabbrev_expand(Char c)
2149{				/* expand to preceding word matching prefix */
2150    Char *cp, *ncp, *bp;
2151    struct Hist *hp;
2152    int arg = 0, i;
2153    size_t len = 0;
2154    int found = 0;
2155    Char *hbuf;
2156    static int oldevent, hist, word;
2157    static Char *start, *oldcursor;
2158
2159    USE(c);
2160    if (Argument <= 0)
2161	return(CC_ERROR);
2162
2163    cp = c_preword(Cursor, InputBuf, 1, STRshwordsep);
2164    if (cp == Cursor || Isspace(*cp))
2165	return(CC_ERROR);
2166
2167    hbuf = NULL;
2168    hp = Histlist.Hnext;
2169    bp = InputBuf;
2170    if (Argument == 1 && eventno == oldevent && cp == start &&
2171	Cursor == oldcursor && patbuf.len > 0
2172	&& Strncmp(patbuf.s, cp, patbuf.len) == 0){
2173	/* continue previous search - go to last match (hist/word) */
2174	if (hist != 0) {		/* need to move up history */
2175	    for (i = 1; i < hist && hp != NULL; i++)
2176		hp = hp->Hnext;
2177	    if (hp == NULL)	/* "can't happen" */
2178		goto err_hbuf;
2179	    hbuf = expand_lex(&hp->Hlex, 0, INT_MAX);
2180	    cp = Strend(hbuf);
2181	    bp = hbuf;
2182	    hp = hp->Hnext;
2183	}
2184	cp = c_preword(cp, bp, word, STRshwordsep);
2185    } else {			/* starting new search */
2186	oldevent = eventno;
2187	start = cp;
2188	patbuf.len = 0;
2189	Strbuf_appendn(&patbuf, cp, Cursor - cp);
2190	hist = 0;
2191	word = 0;
2192    }
2193
2194    while (!found) {
2195	ncp = c_preword(cp, bp, 1, STRshwordsep);
2196	if (ncp == cp || Isspace(*ncp)) { /* beginning of line */
2197	    hist++;
2198	    word = 0;
2199	    if (hp == NULL)
2200		goto err_hbuf;
2201	    hbuf = expand_lex(&hp->Hlex, 0, INT_MAX);
2202	    cp = Strend(hbuf);
2203	    bp = hbuf;
2204	    hp = hp->Hnext;
2205	    continue;
2206	} else {
2207	    word++;
2208	    len = c_endword(ncp-1, cp, 1, STRshwordsep) - ncp + 1;
2209	    cp = ncp;
2210	}
2211	if (len > patbuf.len && Strncmp(cp, patbuf.s, patbuf.len) == 0) {
2212	    /* We don't fully check distinct matches as Gnuemacs does: */
2213	    if (Argument > 1) {	/* just count matches */
2214		if (++arg >= Argument)
2215		    found++;
2216	    } else {		/* match if distinct from previous */
2217		if (len != (size_t)(Cursor - start)
2218		    || Strncmp(cp, start, len) != 0)
2219		    found++;
2220	    }
2221	}
2222    }
2223
2224    if (LastChar + len - (Cursor - start) >= InputLim)
2225	goto err_hbuf;	/* no room */
2226    DeleteBack(Cursor - start);
2227    c_insert(len);
2228    while (len--)
2229	*Cursor++ = *cp++;
2230    oldcursor = Cursor;
2231    xfree(hbuf);
2232    return(CC_REFRESH);
2233
2234 err_hbuf:
2235    xfree(hbuf);
2236    return CC_ERROR;
2237}
2238
2239/*ARGSUSED*/
2240CCRETVAL
2241e_yank_kill(Char c)
2242{				/* almost like GnuEmacs */
2243    int len;
2244    Char *kp, *cp;
2245
2246    USE(c);
2247    if (KillRingLen == 0)	/* nothing killed */
2248	return(CC_ERROR);
2249    len = Strlen(KillRing[YankPos].buf);
2250    if (LastChar + len >= InputLim)
2251	return(CC_ERROR);	/* end of buffer space */
2252
2253    /* else */
2254    cp = Cursor;		/* for speed */
2255
2256    c_insert(len);		/* open the space, */
2257    for (kp = KillRing[YankPos].buf; *kp; kp++)	/* copy the chars */
2258	*cp++ = *kp;
2259
2260    if (Argument == 1) {	/* if no arg */
2261	Mark = Cursor;		/* mark at beginning, cursor at end */
2262	Cursor = cp;
2263    } else {
2264	Mark = cp;		/* else cursor at beginning, mark at end */
2265    }
2266
2267    if (adrof(STRhighlight) && MarkIsSet) {
2268	ClearLines();
2269	ClearDisp();
2270    }
2271    MarkIsSet = 0;
2272    return(CC_REFRESH);
2273}
2274
2275/*ARGSUSED*/
2276CCRETVAL
2277e_yank_pop(Char c)
2278{				/* almost like GnuEmacs */
2279    int m_bef_c, del_len, ins_len;
2280    Char *kp, *cp;
2281
2282    USE(c);
2283
2284#if 0
2285    /* XXX This "should" be here, but doesn't work, since LastCmd
2286       gets set on CC_ERROR and CC_ARGHACK, which it shouldn't(?).
2287       (But what about F_ARGFOUR?) I.e. if you hit M-y twice the
2288       second one will "succeed" even if the first one wasn't preceded
2289       by a yank, and giving an argument is impossible. Now we "succeed"
2290       regardless of previous command, which is wrong too of course. */
2291    if (LastCmd != F_YANK_KILL && LastCmd != F_YANK_POP)
2292	return(CC_ERROR);
2293#endif
2294
2295    if (KillRingLen == 0)	/* nothing killed */
2296	return(CC_ERROR);
2297    YankPos -= Argument;
2298    while (YankPos < 0)
2299	YankPos += KillRingLen;
2300    YankPos %= KillRingLen;
2301
2302    if (Cursor > Mark) {
2303	del_len = Cursor - Mark;
2304	m_bef_c = 1;
2305    } else {
2306	del_len = Mark - Cursor;
2307	m_bef_c = 0;
2308    }
2309    ins_len = Strlen(KillRing[YankPos].buf);
2310    if (LastChar + ins_len - del_len >= InputLim)
2311	return(CC_ERROR);	/* end of buffer space */
2312
2313    if (m_bef_c) {
2314	c_delbefore(del_len);
2315    } else {
2316	c_delafter(del_len);
2317    }
2318    cp = Cursor;		/* for speed */
2319
2320    c_insert(ins_len);		/* open the space, */
2321    for (kp = KillRing[YankPos].buf; *kp; kp++)	/* copy the chars */
2322	*cp++ = *kp;
2323
2324    if (m_bef_c) {
2325	Mark = Cursor;		/* mark at beginning, cursor at end */
2326	Cursor = cp;
2327    } else {
2328	Mark = cp;		/* else cursor at beginning, mark at end */
2329    }
2330
2331    if (adrof(STRhighlight) && MarkIsSet) {
2332	ClearLines();
2333	ClearDisp();
2334    }
2335    MarkIsSet = 0;
2336    return(CC_REFRESH);
2337}
2338
2339/*ARGSUSED*/
2340CCRETVAL
2341v_delprev(Char c) 		/* Backspace key in insert mode */
2342{
2343    int rc;
2344
2345    USE(c);
2346    rc = CC_ERROR;
2347
2348    if (InsertPos != 0) {
2349	if (Argument <= Cursor - InsertPos) {
2350	    c_delbefore(Argument);	/* delete before */
2351	    rc = CC_REFRESH;
2352	}
2353    }
2354    return(rc);
2355}   /* v_delprev  */
2356
2357/*ARGSUSED*/
2358CCRETVAL
2359e_delprev(Char c)
2360{
2361    USE(c);
2362    if (Cursor > InputBuf) {
2363	c_delbefore(Argument);	/* delete before dot */
2364	return(CC_REFRESH);
2365    }
2366    else {
2367	return(CC_ERROR);
2368    }
2369}
2370
2371/*ARGSUSED*/
2372CCRETVAL
2373e_delwordprev(Char c)
2374{
2375    Char *cp;
2376
2377    USE(c);
2378    if (Cursor == InputBuf)
2379	return(CC_ERROR);
2380    /* else */
2381
2382    cp = c_prev_word(Cursor, InputBuf, Argument);
2383
2384    c_push_kill(cp, Cursor);	/* save the text */
2385
2386    c_delbefore((int)(Cursor - cp));	/* delete before dot */
2387    return(CC_REFRESH);
2388}
2389
2390/* DCS <dcs@neutron.chem.yale.edu>, 9 Oct 93
2391 *
2392 * Changed the names of some of the ^D family of editor functions to
2393 * correspond to what they actually do and created new e_delnext_list
2394 * for completeness.
2395 *
2396 *   Old names:			New names:
2397 *
2398 *   delete-char		delete-char-or-eof
2399 *     F_DELNEXT		  F_DELNEXT_EOF
2400 *     e_delnext		  e_delnext_eof
2401 *     edelnxt			  edelnxteof
2402 *   delete-char-or-eof		delete-char
2403 *     F_DELNEXT_EOF		  F_DELNEXT
2404 *     e_delnext_eof		  e_delnext
2405 *     edelnxteof		  edelnxt
2406 *   delete-char-or-list	delete-char-or-list-or-eof
2407 *     F_LIST_DELNEXT		  F_DELNEXT_LIST_EOF
2408 *     e_list_delnext		  e_delnext_list_eof
2409 *   				  edellsteof
2410 *   (no old equivalent)	delete-char-or-list
2411 *   				  F_DELNEXT_LIST
2412 *   				  e_delnext_list
2413 *   				  e_delnxtlst
2414 */
2415
2416/* added by mtk@ari.ncl.omron.co.jp (920818) */
2417/* rename e_delnext() -> e_delnext_eof() */
2418/*ARGSUSED*/
2419CCRETVAL
2420e_delnext(Char c)
2421{
2422    USE(c);
2423    if (Cursor == LastChar) {/* if I'm at the end */
2424	if (!VImode) {
2425		return(CC_ERROR);
2426	}
2427	else {
2428	    if (Cursor != InputBuf)
2429		Cursor--;
2430	    else
2431		return(CC_ERROR);
2432	}
2433    }
2434    c_delafter(Argument);	/* delete after dot */
2435    if (Cursor > LastChar)
2436	Cursor = LastChar;	/* bounds check */
2437    return(CC_REFRESH);
2438}
2439
2440
2441/*ARGSUSED*/
2442CCRETVAL
2443e_delnext_eof(Char c)
2444{
2445    USE(c);
2446    if (Cursor == LastChar) {/* if I'm at the end */
2447	if (!VImode) {
2448	    if (Cursor == InputBuf) {
2449		/* if I'm also at the beginning */
2450		so_write(STReof, 4);/* then do a EOF */
2451		flush();
2452		return(CC_EOF);
2453	    }
2454	    else
2455		return(CC_ERROR);
2456	}
2457	else {
2458	    if (Cursor != InputBuf)
2459		Cursor--;
2460	    else
2461		return(CC_ERROR);
2462	}
2463    }
2464    c_delafter(Argument);	/* delete after dot */
2465    if (Cursor > LastChar)
2466	Cursor = LastChar;	/* bounds check */
2467    return(CC_REFRESH);
2468}
2469
2470/*ARGSUSED*/
2471CCRETVAL
2472e_delnext_list(Char c)
2473{
2474    USE(c);
2475    if (Cursor == LastChar) {	/* if I'm at the end */
2476	PastBottom();
2477	*LastChar = '\0';	/* just in case */
2478	return(CC_LIST_CHOICES);
2479    }
2480    else {
2481	c_delafter(Argument);	/* delete after dot */
2482	if (Cursor > LastChar)
2483	    Cursor = LastChar;	/* bounds check */
2484	return(CC_REFRESH);
2485    }
2486}
2487
2488/*ARGSUSED*/
2489CCRETVAL
2490e_delnext_list_eof(Char c)
2491{
2492    USE(c);
2493    if (Cursor == LastChar) {	/* if I'm at the end */
2494	if (Cursor == InputBuf) {	/* if I'm also at the beginning */
2495	    so_write(STReof, 4);/* then do a EOF */
2496	    flush();
2497	    return(CC_EOF);
2498	}
2499	else {
2500	    PastBottom();
2501	    *LastChar = '\0';	/* just in case */
2502	    return(CC_LIST_CHOICES);
2503	}
2504    }
2505    else {
2506	c_delafter(Argument);	/* delete after dot */
2507	if (Cursor > LastChar)
2508	    Cursor = LastChar;	/* bounds check */
2509	return(CC_REFRESH);
2510    }
2511}
2512
2513/*ARGSUSED*/
2514CCRETVAL
2515e_list_eof(Char c)
2516{
2517    CCRETVAL rv;
2518
2519    USE(c);
2520    if (Cursor == LastChar && Cursor == InputBuf) {
2521	so_write(STReof, 4);	/* then do a EOF */
2522	flush();
2523	rv = CC_EOF;
2524    }
2525    else {
2526	PastBottom();
2527	*LastChar = '\0';	/* just in case */
2528	rv = CC_LIST_CHOICES;
2529    }
2530    return rv;
2531}
2532
2533/*ARGSUSED*/
2534CCRETVAL
2535e_delwordnext(Char c)
2536{
2537    Char *cp;
2538
2539    USE(c);
2540    if (Cursor == LastChar)
2541	return(CC_ERROR);
2542    /* else */
2543
2544    cp = c_next_word(Cursor, LastChar, Argument);
2545
2546    c_push_kill(Cursor, cp);	/* save the text */
2547
2548    c_delafter((int)(cp - Cursor));	/* delete after dot */
2549    if (Cursor > LastChar)
2550	Cursor = LastChar;	/* bounds check */
2551    return(CC_REFRESH);
2552}
2553
2554/*ARGSUSED*/
2555CCRETVAL
2556e_toend(Char c)
2557{
2558    USE(c);
2559    Cursor = LastChar;
2560    if (VImode)
2561	if (ActionFlag & TCSHOP_DELETE) {
2562	    c_delfini();
2563	    return(CC_REFRESH);
2564	}
2565    RefCursor();		/* move the cursor */
2566    return(CC_NORM);
2567}
2568
2569/*ARGSUSED*/
2570CCRETVAL
2571e_tobeg(Char c)
2572{
2573    USE(c);
2574    Cursor = InputBuf;
2575
2576    if (VImode) {
2577       while (Isspace(*Cursor)) /* We want FIRST non space character */
2578	Cursor++;
2579	if (ActionFlag & TCSHOP_DELETE) {
2580	    c_delfini();
2581	    return(CC_REFRESH);
2582	}
2583    }
2584
2585    RefCursor();		/* move the cursor */
2586    return(CC_NORM);
2587}
2588
2589/*ARGSUSED*/
2590CCRETVAL
2591e_killend(Char c)
2592{
2593    USE(c);
2594    c_push_kill(Cursor, LastChar); /* copy it */
2595    LastChar = Cursor;		/* zap! -- delete to end */
2596    if (Mark > Cursor)
2597        Mark = Cursor;
2598    MarkIsSet = 0;
2599    return(CC_REFRESH);
2600}
2601
2602
2603/*ARGSUSED*/
2604CCRETVAL
2605e_killbeg(Char c)
2606{
2607    USE(c);
2608    c_push_kill(InputBuf, Cursor); /* copy it */
2609    c_delbefore((int)(Cursor - InputBuf));
2610    if (Mark && Mark > Cursor)
2611        Mark -= Cursor-InputBuf;
2612    return(CC_REFRESH);
2613}
2614
2615/*ARGSUSED*/
2616CCRETVAL
2617e_killall(Char c)
2618{
2619    USE(c);
2620    c_push_kill(InputBuf, LastChar); /* copy it */
2621    Cursor = Mark = LastChar = InputBuf;	/* zap! -- delete all of it */
2622    MarkIsSet = 0;
2623    return(CC_REFRESH);
2624}
2625
2626/*ARGSUSED*/
2627CCRETVAL
2628e_killregion(Char c)
2629{
2630    USE(c);
2631    if (!Mark)
2632	return(CC_ERROR);
2633
2634    if (Mark > Cursor) {
2635	c_push_kill(Cursor, Mark); /* copy it */
2636	c_delafter((int)(Mark - Cursor)); /* delete it - UNUSED BY VI mode */
2637	Mark = Cursor;
2638    }
2639    else {			/* mark is before cursor */
2640	c_push_kill(Mark, Cursor); /* copy it */
2641	c_delbefore((int)(Cursor - Mark));
2642    }
2643    if (adrof(STRhighlight) && MarkIsSet) {
2644	ClearLines();
2645	ClearDisp();
2646    }
2647    MarkIsSet = 0;
2648    return(CC_REFRESH);
2649}
2650
2651/*ARGSUSED*/
2652CCRETVAL
2653e_copyregion(Char c)
2654{
2655    USE(c);
2656    if (!Mark)
2657	return(CC_ERROR);
2658
2659    if (Mark > Cursor) {
2660	c_push_kill(Cursor, Mark); /* copy it */
2661    }
2662    else {			/* mark is before cursor */
2663	c_push_kill(Mark, Cursor); /* copy it */
2664    }
2665    return(CC_NORM);		/* don't even need to Refresh() */
2666}
2667
2668/*ARGSUSED*/
2669CCRETVAL
2670e_charswitch(Char cc)
2671{
2672    Char c;
2673
2674    USE(cc);
2675
2676    /* do nothing if we are at beginning of line or have only one char */
2677    if (Cursor == &InputBuf[0] || LastChar == &InputBuf[1]) {
2678	return(CC_ERROR);
2679    }
2680
2681    if (Cursor < LastChar) {
2682	Cursor++;
2683    }
2684    c = Cursor[-2];
2685    Cursor[-2] = Cursor[-1];
2686    Cursor[-1] = c;
2687    return(CC_REFRESH);
2688}
2689
2690/*ARGSUSED*/
2691CCRETVAL
2692e_gcharswitch(Char cc)
2693{				/* gosmacs style ^T */
2694    Char c;
2695
2696    USE(cc);
2697    if (Cursor > &InputBuf[1]) {/* must have at least two chars entered */
2698	c = Cursor[-2];
2699	Cursor[-2] = Cursor[-1];
2700	Cursor[-1] = c;
2701	return(CC_REFRESH);
2702    }
2703    else {
2704	return(CC_ERROR);
2705    }
2706}
2707
2708/*ARGSUSED*/
2709CCRETVAL
2710e_charback(Char c)
2711{
2712    USE(c);
2713    if (Cursor > InputBuf) {
2714	if (Argument > Cursor - InputBuf)
2715	    Cursor = InputBuf;
2716	else
2717	    Cursor -= Argument;
2718
2719	if (VImode)
2720	    if (ActionFlag & TCSHOP_DELETE) {
2721		c_delfini();
2722		return(CC_REFRESH);
2723	    }
2724
2725	RefCursor();
2726	return(CC_NORM);
2727    }
2728    else {
2729	return(CC_ERROR);
2730    }
2731}
2732
2733/*ARGSUSED*/
2734CCRETVAL
2735v_wordback(Char c)
2736{
2737    USE(c);
2738    if (Cursor == InputBuf)
2739	return(CC_ERROR);
2740    /* else */
2741
2742    Cursor = c_preword(Cursor, InputBuf, Argument, STRshwspace); /* bounds check */
2743
2744    if (ActionFlag & TCSHOP_DELETE) {
2745	c_delfini();
2746	return(CC_REFRESH);
2747    }
2748
2749    RefCursor();
2750    return(CC_NORM);
2751}
2752
2753/*ARGSUSED*/
2754CCRETVAL
2755e_wordback(Char c)
2756{
2757    USE(c);
2758    if (Cursor == InputBuf)
2759	return(CC_ERROR);
2760    /* else */
2761
2762    Cursor = c_prev_word(Cursor, InputBuf, Argument); /* bounds check */
2763
2764    if (VImode)
2765	if (ActionFlag & TCSHOP_DELETE) {
2766	    c_delfini();
2767	    return(CC_REFRESH);
2768	}
2769
2770    RefCursor();
2771    return(CC_NORM);
2772}
2773
2774/*ARGSUSED*/
2775CCRETVAL
2776e_charfwd(Char c)
2777{
2778    USE(c);
2779    if (Cursor < LastChar) {
2780	Cursor += Argument;
2781	if (Cursor > LastChar)
2782	    Cursor = LastChar;
2783
2784	if (VImode)
2785	    if (ActionFlag & TCSHOP_DELETE) {
2786		c_delfini();
2787		return(CC_REFRESH);
2788	    }
2789
2790	RefCursor();
2791	return(CC_NORM);
2792    }
2793    else {
2794	return(CC_ERROR);
2795    }
2796}
2797
2798/*ARGSUSED*/
2799CCRETVAL
2800e_wordfwd(Char c)
2801{
2802    USE(c);
2803    if (Cursor == LastChar)
2804	return(CC_ERROR);
2805    /* else */
2806
2807    Cursor = c_next_word(Cursor, LastChar, Argument);
2808
2809    if (VImode)
2810	if (ActionFlag & TCSHOP_DELETE) {
2811	    c_delfini();
2812	    return(CC_REFRESH);
2813	}
2814
2815    RefCursor();
2816    return(CC_NORM);
2817}
2818
2819/*ARGSUSED*/
2820CCRETVAL
2821v_wordfwd(Char c)
2822{
2823    USE(c);
2824    if (Cursor == LastChar)
2825	return(CC_ERROR);
2826    /* else */
2827
2828    Cursor = c_nexword(Cursor, LastChar, Argument);
2829
2830    if (VImode)
2831	if (ActionFlag & TCSHOP_DELETE) {
2832	    c_delfini();
2833	    return(CC_REFRESH);
2834	}
2835
2836    RefCursor();
2837    return(CC_NORM);
2838}
2839
2840/*ARGSUSED*/
2841CCRETVAL
2842v_wordbegnext(Char c)
2843{
2844    USE(c);
2845    if (Cursor == LastChar)
2846	return(CC_ERROR);
2847    /* else */
2848
2849    Cursor = c_next_word(Cursor, LastChar, Argument);
2850    if (Cursor < LastChar)
2851	Cursor++;
2852
2853    if (VImode)
2854	if (ActionFlag & TCSHOP_DELETE) {
2855	    c_delfini();
2856	    return(CC_REFRESH);
2857	}
2858
2859    RefCursor();
2860    return(CC_NORM);
2861}
2862
2863/*ARGSUSED*/
2864static CCRETVAL
2865v_repeat_srch(int c)
2866{
2867    CCRETVAL rv = CC_ERROR;
2868#ifdef SDEBUG
2869    xprintf("dir %d patlen %d patbuf %S\n",
2870	    c, (int)patbuf.len, patbuf.s);
2871#endif
2872
2873    LastCmd = (KEYCMD) c;  /* Hack to stop c_hsetpat */
2874    LastChar = InputBuf;
2875    switch (c) {
2876    case F_DOWN_SEARCH_HIST:
2877	rv = e_down_search_hist(0);
2878	break;
2879    case F_UP_SEARCH_HIST:
2880	rv = e_up_search_hist(0);
2881	break;
2882    default:
2883	break;
2884    }
2885    return rv;
2886}
2887
2888static CCRETVAL
2889v_csearch_back(Char ch, int count, int tflag)
2890{
2891    Char *cp;
2892
2893    cp = Cursor;
2894    while (count--) {
2895	if (*cp == ch)
2896	    cp--;
2897	while (cp > InputBuf && *cp != ch)
2898	    cp--;
2899    }
2900
2901    if (cp < InputBuf || (cp == InputBuf && *cp != ch))
2902	return(CC_ERROR);
2903
2904    if (*cp == ch && tflag)
2905	cp++;
2906
2907    Cursor = cp;
2908
2909    if (ActionFlag & TCSHOP_DELETE) {
2910	Cursor++;
2911	c_delfini();
2912	return(CC_REFRESH);
2913    }
2914
2915    RefCursor();
2916    return(CC_NORM);
2917}
2918
2919static CCRETVAL
2920v_csearch_fwd(Char ch, int count, int tflag)
2921{
2922    Char *cp;
2923
2924    cp = Cursor;
2925    while (count--) {
2926	if(*cp == ch)
2927	    cp++;
2928	while (cp < LastChar && *cp != ch)
2929	    cp++;
2930    }
2931
2932    if (cp >= LastChar)
2933	return(CC_ERROR);
2934
2935    if (*cp == ch && tflag)
2936	cp--;
2937
2938    Cursor = cp;
2939
2940    if (ActionFlag & TCSHOP_DELETE) {
2941	Cursor++;
2942	c_delfini();
2943	return(CC_REFRESH);
2944    }
2945    RefCursor();
2946    return(CC_NORM);
2947}
2948
2949/*ARGSUSED*/
2950static CCRETVAL
2951v_action(int c)
2952{
2953    Char *cp, *kp;
2954
2955    if (ActionFlag == TCSHOP_DELETE) {
2956	ActionFlag = TCSHOP_NOP;
2957	ActionPos = 0;
2958
2959	UndoSize = 0;
2960	kp = UndoBuf;
2961	for (cp = InputBuf; cp < LastChar; cp++) {
2962	    *kp++ = *cp;
2963	    UndoSize++;
2964	}
2965
2966	UndoAction = TCSHOP_INSERT;
2967	UndoPtr  = InputBuf;
2968	LastChar = InputBuf;
2969	Cursor   = InputBuf;
2970	if (c & TCSHOP_INSERT)
2971	    c_alternativ_key_map(0);
2972
2973	return(CC_REFRESH);
2974    }
2975#ifdef notdef
2976    else if (ActionFlag == TCSHOP_NOP) {
2977#endif
2978	ActionPos = Cursor;
2979	ActionFlag = c;
2980	return(CC_ARGHACK);  /* Do NOT clear out argument */
2981#ifdef notdef
2982    }
2983    else {
2984	ActionFlag = 0;
2985	ActionPos = 0;
2986	return(CC_ERROR);
2987    }
2988#endif
2989}
2990
2991#ifdef COMMENT
2992/* by: Brian Allison <uiucdcs!convex!allison@RUTGERS.EDU> */
2993static void
2994c_get_word(Char **begin, Char **end)
2995{
2996    Char   *cp;
2997
2998    cp = &Cursor[0];
2999    while (Argument--) {
3000	while ((cp <= LastChar) && (isword(*cp)))
3001	    cp++;
3002	*end = --cp;
3003	while ((cp >= InputBuf) && (isword(*cp)))
3004	    cp--;
3005	*begin = ++cp;
3006    }
3007}
3008#endif /* COMMENT */
3009
3010/*ARGSUSED*/
3011CCRETVAL
3012e_uppercase(Char c)
3013{
3014    Char   *cp, *end;
3015
3016    USE(c);
3017    end = c_next_word(Cursor, LastChar, Argument);
3018
3019    for (cp = Cursor; cp < end; cp++)	/* PWP: was cp=begin */
3020	if (Islower(*cp))
3021	    *cp = Toupper(*cp);
3022
3023    Cursor = end;
3024    if (Cursor > LastChar)
3025	Cursor = LastChar;
3026    return(CC_REFRESH);
3027}
3028
3029
3030/*ARGSUSED*/
3031CCRETVAL
3032e_capitalcase(Char c)
3033{
3034    Char   *cp, *end;
3035
3036    USE(c);
3037    end = c_next_word(Cursor, LastChar, Argument);
3038
3039    cp = Cursor;
3040    for (; cp < end; cp++) {
3041	if (Isalpha(*cp)) {
3042	    if (Islower(*cp))
3043		*cp = Toupper(*cp);
3044	    cp++;
3045	    break;
3046	}
3047    }
3048    for (; cp < end; cp++)
3049	if (Isupper(*cp))
3050	    *cp = Tolower(*cp);
3051
3052    Cursor = end;
3053    if (Cursor > LastChar)
3054	Cursor = LastChar;
3055    return(CC_REFRESH);
3056}
3057
3058/*ARGSUSED*/
3059CCRETVAL
3060e_lowercase(Char c)
3061{
3062    Char   *cp, *end;
3063
3064    USE(c);
3065    end = c_next_word(Cursor, LastChar, Argument);
3066
3067    for (cp = Cursor; cp < end; cp++)
3068	if (Isupper(*cp))
3069	    *cp = Tolower(*cp);
3070
3071    Cursor = end;
3072    if (Cursor > LastChar)
3073	Cursor = LastChar;
3074    return(CC_REFRESH);
3075}
3076
3077
3078/*ARGSUSED*/
3079CCRETVAL
3080e_set_mark(Char c)
3081{
3082    USE(c);
3083    if (adrof(STRhighlight) && MarkIsSet && Mark != Cursor) {
3084	ClearLines();
3085	ClearDisp();
3086	Refresh();
3087    }
3088    Mark = Cursor;
3089    MarkIsSet = 1;
3090    return(CC_NORM);
3091}
3092
3093/*ARGSUSED*/
3094CCRETVAL
3095e_exchange_mark(Char c)
3096{
3097    Char *cp;
3098
3099    USE(c);
3100    cp = Cursor;
3101    Cursor = Mark;
3102    Mark = cp;
3103    RefCursor();
3104    return(CC_NORM);
3105}
3106
3107/*ARGSUSED*/
3108CCRETVAL
3109e_argfour(Char c)
3110{				/* multiply current argument by 4 */
3111    USE(c);
3112    if (Argument > 1000000)
3113	return CC_ERROR;
3114    DoingArg = 1;
3115    Argument *= 4;
3116    return(CC_ARGHACK);
3117}
3118
3119static void
3120quote_mode_cleanup(void *unused)
3121{
3122    USE(unused);
3123    QuoteModeOff();
3124}
3125
3126/*ARGSUSED*/
3127CCRETVAL
3128e_quote(Char c)
3129{
3130    Char    ch;
3131    int     num;
3132
3133    USE(c);
3134    QuoteModeOn();
3135    cleanup_push(&c, quote_mode_cleanup); /* Using &c just as a mark */
3136    num = GetNextChar(&ch);
3137    cleanup_until(&c);
3138    if (num == 1)
3139	return e_insert(ch);
3140    else
3141	return e_send_eof(0);
3142}
3143
3144/*ARGSUSED*/
3145CCRETVAL
3146e_metanext(Char c)
3147{
3148    USE(c);
3149    MetaNext = 1;
3150    return(CC_ARGHACK);	/* preserve argument */
3151}
3152
3153#ifdef notdef
3154/*ARGSUSED*/
3155CCRETVAL
3156e_extendnext(Char c)
3157{
3158    CurrentKeyMap = CcAltMap;
3159    return(CC_ARGHACK);	/* preserve argument */
3160}
3161
3162#endif
3163
3164/*ARGSUSED*/
3165CCRETVAL
3166v_insbeg(Char c)
3167{				/* move to beginning of line and start vi
3168				 * insert mode */
3169    USE(c);
3170    Cursor = InputBuf;
3171    InsertPos = Cursor;
3172
3173    UndoPtr  = Cursor;
3174    UndoAction = TCSHOP_DELETE;
3175
3176    RefCursor();		/* move the cursor */
3177    c_alternativ_key_map(0);
3178    return(CC_NORM);
3179}
3180
3181/*ARGSUSED*/
3182CCRETVAL
3183v_replone(Char c)
3184{				/* vi mode overwrite one character */
3185    USE(c);
3186    c_alternativ_key_map(0);
3187    inputmode = MODE_REPLACE_1;
3188    UndoAction = TCSHOP_CHANGE;	/* Set Up for VI undo command */
3189    UndoPtr = Cursor;
3190    UndoSize = 0;
3191    return(CC_NORM);
3192}
3193
3194/*ARGSUSED*/
3195CCRETVAL
3196v_replmode(Char c)
3197{				/* vi mode start overwriting */
3198    USE(c);
3199    c_alternativ_key_map(0);
3200    inputmode = MODE_REPLACE;
3201    UndoAction = TCSHOP_CHANGE;	/* Set Up for VI undo command */
3202    UndoPtr = Cursor;
3203    UndoSize = 0;
3204    return(CC_NORM);
3205}
3206
3207/*ARGSUSED*/
3208CCRETVAL
3209v_substchar(Char c)
3210{				/* vi mode substitute for one char */
3211    USE(c);
3212    c_delafter(Argument);
3213    c_alternativ_key_map(0);
3214    return(CC_REFRESH);
3215}
3216
3217/*ARGSUSED*/
3218CCRETVAL
3219v_substline(Char c)
3220{				/* vi mode replace whole line */
3221    USE(c);
3222    (void) e_killall(0);
3223    c_alternativ_key_map(0);
3224    return(CC_REFRESH);
3225}
3226
3227/*ARGSUSED*/
3228CCRETVAL
3229v_chgtoend(Char c)
3230{				/* vi mode change to end of line */
3231    USE(c);
3232    (void) e_killend(0);
3233    c_alternativ_key_map(0);
3234    return(CC_REFRESH);
3235}
3236
3237/*ARGSUSED*/
3238CCRETVAL
3239v_insert(Char c)
3240{				/* vi mode start inserting */
3241    USE(c);
3242    c_alternativ_key_map(0);
3243
3244    InsertPos = Cursor;
3245    UndoPtr = Cursor;
3246    UndoAction = TCSHOP_DELETE;
3247
3248    return(CC_NORM);
3249}
3250
3251/*ARGSUSED*/
3252CCRETVAL
3253v_add(Char c)
3254{				/* vi mode start adding */
3255    USE(c);
3256    c_alternativ_key_map(0);
3257    if (Cursor < LastChar)
3258    {
3259	Cursor++;
3260	if (Cursor > LastChar)
3261	    Cursor = LastChar;
3262	RefCursor();
3263    }
3264
3265    InsertPos = Cursor;
3266    UndoPtr = Cursor;
3267    UndoAction = TCSHOP_DELETE;
3268
3269    return(CC_NORM);
3270}
3271
3272/*ARGSUSED*/
3273CCRETVAL
3274v_addend(Char c)
3275{				/* vi mode to add at end of line */
3276    USE(c);
3277    c_alternativ_key_map(0);
3278    Cursor = LastChar;
3279
3280    InsertPos = LastChar;	/* Mark where insertion begins */
3281    UndoPtr = LastChar;
3282    UndoAction = TCSHOP_DELETE;
3283
3284    RefCursor();
3285    return(CC_NORM);
3286}
3287
3288/*ARGSUSED*/
3289CCRETVAL
3290v_change_case(Char cc)
3291{
3292    Char    c;
3293
3294    USE(cc);
3295    if (Cursor < LastChar) {
3296#ifndef WINNT_NATIVE
3297	c = *Cursor;
3298#else
3299	c = CHAR & *Cursor;
3300#endif /* WINNT_NATIVE */
3301	if (Isupper(c))
3302	    *Cursor++ = Tolower(c);
3303	else if (Islower(c))
3304	    *Cursor++ = Toupper(c);
3305	else
3306	    Cursor++;
3307	RefPlusOne(1);		/* fast refresh for one char */
3308	return(CC_NORM);
3309    }
3310    return(CC_ERROR);
3311}
3312
3313/*ARGSUSED*/
3314CCRETVAL
3315e_expand(Char c)
3316{
3317    Char *p;
3318
3319    USE(c);
3320    for (p = InputBuf; Isspace(*p); p++)
3321	continue;
3322    if (p == LastChar)
3323	return(CC_ERROR);
3324
3325    justpr++;
3326    Expand++;
3327    return(e_newline(0));
3328}
3329
3330/*ARGSUSED*/
3331CCRETVAL
3332e_startover(Char c)
3333{				/* erase all of current line, start again */
3334    USE(c);
3335    ResetInLine(0);		/* reset the input pointers */
3336    return(CC_REFRESH);
3337}
3338
3339/*ARGSUSED*/
3340CCRETVAL
3341e_redisp(Char c)
3342{
3343    USE(c);
3344    ClearLines();
3345    ClearDisp();
3346    return(CC_REFRESH);
3347}
3348
3349/*ARGSUSED*/
3350CCRETVAL
3351e_cleardisp(Char c)
3352{
3353    USE(c);
3354    ClearScreen();		/* clear the whole real screen */
3355    ClearDisp();		/* reset everything */
3356    return(CC_REFRESH);
3357}
3358
3359/*ARGSUSED*/
3360CCRETVAL
3361e_tty_int(Char c)
3362{
3363    USE(c);
3364#if defined(_MINIX) || defined(WINNT_NATIVE)
3365    /* SAK PATCH: erase all of current line, start again */
3366    ResetInLine(0);		/* reset the input pointers */
3367    xputchar('\n');
3368    ClearDisp();
3369    return (CC_REFRESH);
3370#else /* !_MINIX && !WINNT_NATIVE */
3371    /* do no editing */
3372    return (CC_NORM);
3373#endif /* _MINIX || WINNT_NATIVE */
3374}
3375
3376/*
3377 * From: ghazi@cesl.rutgers.edu (Kaveh R. Ghazi)
3378 * Function to send a character back to the input stream in cooked
3379 * mode. Only works if we have TIOCSTI
3380 */
3381/*ARGSUSED*/
3382CCRETVAL
3383e_stuff_char(Char c)
3384{
3385#ifdef TIOCSTI
3386     int was_raw = Tty_raw_mode;
3387     char buf[MB_LEN_MAX];
3388     size_t i, len;
3389
3390     if (was_raw)
3391         (void) Cookedmode();
3392
3393     (void) xwrite(SHIN, "\n", 1);
3394     len = one_wctomb(buf, c);
3395     for (i = 0; i < len; i++)
3396	 (void) ioctl(SHIN, TIOCSTI, (ioctl_t) &buf[i]);
3397
3398     if (was_raw)
3399	 (void) Rawmode();
3400     return(e_redisp(c));
3401#else /* !TIOCSTI */
3402     return(CC_ERROR);
3403#endif /* !TIOCSTI */
3404}
3405
3406/*ARGSUSED*/
3407CCRETVAL
3408e_insovr(Char c)
3409{
3410    USE(c);
3411    inputmode = (inputmode == MODE_INSERT ? MODE_REPLACE : MODE_INSERT);
3412    return(CC_NORM);
3413}
3414
3415/*ARGSUSED*/
3416CCRETVAL
3417e_tty_dsusp(Char c)
3418{
3419    USE(c);
3420    /* do no editing */
3421    return(CC_NORM);
3422}
3423
3424/*ARGSUSED*/
3425CCRETVAL
3426e_tty_flusho(Char c)
3427{
3428    USE(c);
3429    /* do no editing */
3430    return(CC_NORM);
3431}
3432
3433/*ARGSUSED*/
3434CCRETVAL
3435e_tty_quit(Char c)
3436{
3437    USE(c);
3438    /* do no editing */
3439    return(CC_NORM);
3440}
3441
3442/*ARGSUSED*/
3443CCRETVAL
3444e_tty_tsusp(Char c)
3445{
3446    USE(c);
3447    /* do no editing */
3448    return(CC_NORM);
3449}
3450
3451/*ARGSUSED*/
3452CCRETVAL
3453e_tty_stopo(Char c)
3454{
3455    USE(c);
3456    /* do no editing */
3457    return(CC_NORM);
3458}
3459
3460/* returns the number of (attempted) expansions */
3461int
3462ExpandHistory(void)
3463{
3464    *LastChar = '\0';		/* just in case */
3465    return c_substitute();
3466}
3467
3468/*ARGSUSED*/
3469CCRETVAL
3470e_expand_history(Char c)
3471{
3472    USE(c);
3473    (void)ExpandHistory();
3474    return(CC_NORM);
3475}
3476
3477/*ARGSUSED*/
3478CCRETVAL
3479e_magic_space(Char c)
3480{
3481    USE(c);
3482    *LastChar = '\0';		/* just in case */
3483    (void)c_substitute();
3484    return(e_insert(' '));
3485}
3486
3487/*ARGSUSED*/
3488CCRETVAL
3489e_inc_fwd(Char c)
3490{
3491    CCRETVAL ret;
3492
3493    USE(c);
3494    patbuf.len = 0;
3495    MarkIsSet = 0;
3496    ret = e_inc_search(F_DOWN_SEARCH_HIST);
3497    if (adrof(STRhighlight) && IncMatchLen) {
3498	IncMatchLen = 0;
3499	ClearLines();
3500	ClearDisp();
3501	Refresh();
3502    }
3503    IncMatchLen = 0;
3504    return ret;
3505}
3506
3507
3508/*ARGSUSED*/
3509CCRETVAL
3510e_inc_back(Char c)
3511{
3512    CCRETVAL ret;
3513
3514    USE(c);
3515    patbuf.len = 0;
3516    MarkIsSet = 0;
3517    ret = e_inc_search(F_UP_SEARCH_HIST);
3518    if (adrof(STRhighlight) && IncMatchLen) {
3519	IncMatchLen = 0;
3520	ClearLines();
3521	ClearDisp();
3522	Refresh();
3523    }
3524    IncMatchLen = 0;
3525    return ret;
3526}
3527
3528/*ARGSUSED*/
3529CCRETVAL
3530e_copyprev(Char c)
3531{
3532    Char *cp, *oldc, *dp;
3533
3534    USE(c);
3535    if (Cursor == InputBuf)
3536	return(CC_ERROR);
3537    /* else */
3538
3539    oldc = Cursor;
3540    /* does a bounds check */
3541    cp = c_prev_word(Cursor, InputBuf, Argument);
3542
3543    c_insert((int)(oldc - cp));
3544    for (dp = oldc; cp < oldc && dp < LastChar; cp++)
3545	*dp++ = *cp;
3546
3547    Cursor = dp;		/* put cursor at end */
3548
3549    return(CC_REFRESH);
3550}
3551
3552/*ARGSUSED*/
3553CCRETVAL
3554e_tty_starto(Char c)
3555{
3556    USE(c);
3557    /* do no editing */
3558    return(CC_NORM);
3559}
3560
3561/*ARGSUSED*/
3562CCRETVAL
3563e_load_average(Char c)
3564{
3565    USE(c);
3566    PastBottom();
3567#ifdef TIOCSTAT
3568    /*
3569     * Here we pass &c to the ioctl because some os's (NetBSD) expect it
3570     * there even if they don't use it. (lukem@netbsd.org)
3571     */
3572    if (ioctl(SHIN, TIOCSTAT, (ioctl_t) &c) < 0)
3573#endif
3574	xprintf("%s", CGETS(5, 1, "Load average unavailable\n"));
3575    return(CC_REFRESH);
3576}
3577
3578/*ARGSUSED*/
3579CCRETVAL
3580v_chgmeta(Char c)
3581{
3582    USE(c);
3583    /*
3584     * Delete with insert == change: first we delete and then we leave in
3585     * insert mode.
3586     */
3587    return(v_action(TCSHOP_DELETE|TCSHOP_INSERT));
3588}
3589
3590/*ARGSUSED*/
3591CCRETVAL
3592v_delmeta(Char c)
3593{
3594    USE(c);
3595    return(v_action(TCSHOP_DELETE));
3596}
3597
3598
3599/*ARGSUSED*/
3600CCRETVAL
3601v_endword(Char c)
3602{
3603    USE(c);
3604    if (Cursor == LastChar)
3605	return(CC_ERROR);
3606    /* else */
3607
3608    Cursor = c_endword(Cursor, LastChar, Argument, STRshwspace);
3609
3610    if (ActionFlag & TCSHOP_DELETE)
3611    {
3612	Cursor++;
3613	c_delfini();
3614	return(CC_REFRESH);
3615    }
3616
3617    RefCursor();
3618    return(CC_NORM);
3619}
3620
3621/*ARGSUSED*/
3622CCRETVAL
3623v_eword(Char c)
3624{
3625    USE(c);
3626    if (Cursor == LastChar)
3627	return(CC_ERROR);
3628    /* else */
3629
3630    Cursor = c_eword(Cursor, LastChar, Argument);
3631
3632    if (ActionFlag & TCSHOP_DELETE) {
3633	Cursor++;
3634	c_delfini();
3635	return(CC_REFRESH);
3636    }
3637
3638    RefCursor();
3639    return(CC_NORM);
3640}
3641
3642/*ARGSUSED*/
3643CCRETVAL
3644v_char_fwd(Char c)
3645{
3646    Char ch;
3647
3648    USE(c);
3649    if (GetNextChar(&ch) != 1)
3650	return e_send_eof(0);
3651
3652    srch_dir = CHAR_FWD;
3653    srch_char = ch;
3654
3655    return v_csearch_fwd(ch, Argument, 0);
3656
3657}
3658
3659/*ARGSUSED*/
3660CCRETVAL
3661v_char_back(Char c)
3662{
3663    Char ch;
3664
3665    USE(c);
3666    if (GetNextChar(&ch) != 1)
3667	return e_send_eof(0);
3668
3669    srch_dir = CHAR_BACK;
3670    srch_char = ch;
3671
3672    return v_csearch_back(ch, Argument, 0);
3673}
3674
3675/*ARGSUSED*/
3676CCRETVAL
3677v_charto_fwd(Char c)
3678{
3679    Char ch;
3680
3681    USE(c);
3682    if (GetNextChar(&ch) != 1)
3683	return e_send_eof(0);
3684
3685    return v_csearch_fwd(ch, Argument, 1);
3686
3687}
3688
3689/*ARGSUSED*/
3690CCRETVAL
3691v_charto_back(Char c)
3692{
3693    Char ch;
3694
3695    USE(c);
3696    if (GetNextChar(&ch) != 1)
3697	return e_send_eof(0);
3698
3699    return v_csearch_back(ch, Argument, 1);
3700}
3701
3702/*ARGSUSED*/
3703CCRETVAL
3704v_rchar_fwd(Char c)
3705{
3706    USE(c);
3707    if (srch_char == 0)
3708	return CC_ERROR;
3709
3710    return srch_dir == CHAR_FWD ? v_csearch_fwd(srch_char, Argument, 0) :
3711			          v_csearch_back(srch_char, Argument, 0);
3712}
3713
3714/*ARGSUSED*/
3715CCRETVAL
3716v_rchar_back(Char c)
3717{
3718    USE(c);
3719    if (srch_char == 0)
3720	return CC_ERROR;
3721
3722    return srch_dir == CHAR_BACK ? v_csearch_fwd(srch_char, Argument, 0) :
3723			           v_csearch_back(srch_char, Argument, 0);
3724}
3725
3726/*ARGSUSED*/
3727CCRETVAL
3728v_undo(Char c)
3729{
3730    int  loop;
3731    Char *kp, *cp;
3732    Char temp;
3733    int	 size;
3734
3735    USE(c);
3736    switch (UndoAction) {
3737    case TCSHOP_DELETE|TCSHOP_INSERT:
3738    case TCSHOP_DELETE:
3739	if (UndoSize == 0) return(CC_NORM);
3740	cp = UndoPtr;
3741	kp = UndoBuf;
3742	for (loop=0; loop < UndoSize; loop++)	/* copy the chars */
3743	    *kp++ = *cp++;			/* into UndoBuf   */
3744
3745	for (cp = UndoPtr; cp <= LastChar; cp++)
3746	    *cp = cp[UndoSize];
3747
3748	LastChar -= UndoSize;
3749	Cursor   =  UndoPtr;
3750
3751	UndoAction = TCSHOP_INSERT;
3752	break;
3753
3754    case TCSHOP_INSERT:
3755	if (UndoSize == 0) return(CC_NORM);
3756	cp = UndoPtr;
3757	Cursor = UndoPtr;
3758	kp = UndoBuf;
3759	c_insert(UndoSize);		/* open the space, */
3760	for (loop = 0; loop < UndoSize; loop++)	/* copy the chars */
3761	    *cp++ = *kp++;
3762
3763	UndoAction = TCSHOP_DELETE;
3764	break;
3765
3766    case TCSHOP_CHANGE:
3767	if (UndoSize == 0) return(CC_NORM);
3768	cp = UndoPtr;
3769	Cursor = UndoPtr;
3770	kp = UndoBuf;
3771	size = (int)(Cursor-LastChar); /*  NOT NSL independant */
3772	if (size < UndoSize)
3773	    size = UndoSize;
3774	for(loop = 0; loop < size; loop++) {
3775	    temp = *kp;
3776	    *kp++ = *cp;
3777	    *cp++ = temp;
3778	}
3779	break;
3780
3781    default:
3782	return(CC_ERROR);
3783    }
3784
3785    return(CC_REFRESH);
3786}
3787
3788/*ARGSUSED*/
3789CCRETVAL
3790v_ush_meta(Char c)
3791{
3792    USE(c);
3793    return v_search(F_UP_SEARCH_HIST);
3794}
3795
3796/*ARGSUSED*/
3797CCRETVAL
3798v_dsh_meta(Char c)
3799{
3800    USE(c);
3801    return v_search(F_DOWN_SEARCH_HIST);
3802}
3803
3804/*ARGSUSED*/
3805CCRETVAL
3806v_rsrch_fwd(Char c)
3807{
3808    USE(c);
3809    if (patbuf.len == 0) return(CC_ERROR);
3810    return(v_repeat_srch(searchdir));
3811}
3812
3813/*ARGSUSED*/
3814CCRETVAL
3815v_rsrch_back(Char c)
3816{
3817    USE(c);
3818    if (patbuf.len == 0) return(CC_ERROR);
3819    return(v_repeat_srch(searchdir == F_UP_SEARCH_HIST ?
3820			 F_DOWN_SEARCH_HIST : F_UP_SEARCH_HIST));
3821}
3822
3823#ifndef WINNT_NATIVE
3824/* Since ed.defns.h  is generated from ed.defns.c, these empty
3825   functions will keep the F_NUM_FNS consistent
3826 */
3827CCRETVAL
3828e_copy_to_clipboard(Char c)
3829{
3830    USE(c);
3831    return CC_ERROR;
3832}
3833
3834CCRETVAL
3835e_paste_from_clipboard(Char c)
3836{
3837    USE(c);
3838    return (CC_ERROR);
3839}
3840
3841CCRETVAL
3842e_dosify_next(Char c)
3843{
3844    USE(c);
3845    return (CC_ERROR);
3846}
3847CCRETVAL
3848e_dosify_prev(Char c)
3849{
3850    USE(c);
3851    return (CC_ERROR);
3852}
3853CCRETVAL
3854e_page_up(Char c)
3855{
3856    USE(c);
3857    return (CC_ERROR);
3858}
3859CCRETVAL
3860e_page_down(Char c)
3861{
3862    USE(c);
3863    return (CC_ERROR);
3864}
3865#endif /* !WINNT_NATIVE */
3866
3867#ifdef notdef
3868void
3869MoveCursor(int n)		/* move cursor + right - left char */
3870{
3871    Cursor = Cursor + n;
3872    if (Cursor < InputBuf)
3873	Cursor = InputBuf;
3874    if (Cursor > LastChar)
3875	Cursor = LastChar;
3876    return;
3877}
3878
3879Char *
3880GetCursor(void)
3881{
3882    return(Cursor);
3883}
3884
3885int
3886PutCursor(Char *p)
3887{
3888    if (p < InputBuf || p > LastChar)
3889	return 1;		/* Error */
3890    Cursor = p;
3891    return 0;
3892}
3893#endif
3894