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