ed.chared.c revision 167465
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
75RCSID("$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
99static Char *InsertPos = InputBuf; /* Where insertion starts */
100static Char *ActionPos = 0;	   /* Where action begins  */
101static int  ActionFlag = TCSHOP_NOP;	   /* What delayed action to take */
102/*
103 * Word search state
104 */
105static int  searchdir = F_UP_SEARCH_HIST; 	/* Direction of last search */
106static struct Strbuf patbuf; /* = Strbuf_INIT; Search target */
107/*
108 * Char search state
109 */
110static int  srch_dir = CHAR_FWD;		/* Direction of last search */
111static Char srch_char = 0;			/* Search target */
112
113/* all routines that start with c_ are private to this set of routines */
114static	void	 c_alternativ_key_map	(int);
115void	 c_insert		(int);
116void	 c_delafter		(int);
117void	 c_delbefore		(int);
118static 	int	 c_to_class		(Char);
119static	Char	*c_prev_word		(Char *, Char *, int);
120static	Char	*c_next_word		(Char *, Char *, int);
121static	Char	*c_number		(Char *, int *, int);
122static	Char	*c_expand		(Char *);
123static	void	 c_excl			(Char *);
124static	void	 c_substitute		(void);
125static	void	 c_delfini		(void);
126static	int	 c_hmatch		(Char *);
127static	void	 c_hsetpat		(void);
128#ifdef COMMENT
129static	void	 c_get_word		(Char **, Char **);
130#endif
131static	Char	*c_preword		(Char *, Char *, int, Char *);
132static	Char	*c_nexword		(Char *, Char *, int);
133static	Char	*c_endword		(Char *, Char *, int, Char *);
134static	Char	*c_eword		(Char *, Char *, int);
135static	void	 c_push_kill		(Char *, Char *);
136static	void	 c_save_inputbuf	(void);
137static  CCRETVAL c_search_line		(Char *, int);
138static  CCRETVAL v_repeat_srch		(int);
139static	CCRETVAL e_inc_search		(int);
140#ifdef notyet
141static  CCRETVAL e_insert_str		(Char *);
142#endif
143static	CCRETVAL v_search		(int);
144static	CCRETVAL v_csearch_fwd		(Char, int, int);
145static	CCRETVAL v_action		(int);
146static	CCRETVAL v_csearch_back		(Char, int, int);
147
148static void
149c_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
165void
166c_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
182void
183c_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
223void
224c_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
256static Char *
257c_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 */
302static int
303c_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
314static Char *
315c_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
361static Char *
362c_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
402static Char *
403c_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
434static Char *
435c_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
464static Char *
465c_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;
481excl_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;
674excl_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
687static void
688c_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
729static void
730c_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
750static void
751c_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
782static Char *
783c_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
813static Char *
814c_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 */
835void
836SetKillRing(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 */
873static void
874c_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 */
931static void
932c_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
943CCRETVAL
944GetHistLine()
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
1013static CCRETVAL
1014c_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
1040static CCRETVAL
1041e_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
1245static CCRETVAL
1246v_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*/
1371CCRETVAL
1372v_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*/
1399CCRETVAL
1400e_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
1409static CCRETVAL
1410e_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
1430CCRETVAL
1431e_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
1479int
1480InsertStr(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
1495void
1496DeleteBack(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
1505CCRETVAL
1506e_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
1538CCRETVAL
1539e_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
1562CCRETVAL
1563v_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*/
1583CCRETVAL
1584e_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*/
1604CCRETVAL
1605e_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*/
1616CCRETVAL
1617e_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*/
1629CCRETVAL
1630e_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*/
1639CCRETVAL
1640e_complete(Char c)
1641{
1642    USE(c);
1643    *LastChar = '\0';		/* just in case */
1644    return(CC_COMPLETE);
1645}
1646
1647/*ARGSUSED*/
1648CCRETVAL
1649e_complete_back(Char c)
1650{
1651    USE(c);
1652    *LastChar = '\0';		/* just in case */
1653    return(CC_COMPLETE_BACK);
1654}
1655
1656/*ARGSUSED*/
1657CCRETVAL
1658e_complete_fwd(Char c)
1659{
1660    USE(c);
1661    *LastChar = '\0';		/* just in case */
1662    return(CC_COMPLETE_FWD);
1663}
1664
1665/*ARGSUSED*/
1666CCRETVAL
1667e_complete_all(Char c)
1668{
1669    USE(c);
1670    *LastChar = '\0';		/* just in case */
1671    return(CC_COMPLETE_ALL);
1672}
1673
1674/*ARGSUSED*/
1675CCRETVAL
1676v_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*/
1686CCRETVAL
1687e_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*/
1746CCRETVAL
1747e_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*/
1776CCRETVAL
1777e_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 */
1798static int
1799c_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 */
1809static void
1810c_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*/
1826CCRETVAL
1827e_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*/
1901CCRETVAL
1902e_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*/
1958CCRETVAL
1959e_helpme(Char c)
1960{
1961    USE(c);
1962    PastBottom();
1963    *LastChar = '\0';		/* just in case */
1964    return(CC_HELPME);
1965}
1966
1967/*ARGSUSED*/
1968CCRETVAL
1969e_correct(Char c)
1970{
1971    USE(c);
1972    *LastChar = '\0';		/* just in case */
1973    return(CC_CORRECT);
1974}
1975
1976/*ARGSUSED*/
1977CCRETVAL
1978e_correctl(Char c)
1979{
1980    USE(c);
1981    *LastChar = '\0';		/* just in case */
1982    return(CC_CORRECT_L);
1983}
1984
1985/*ARGSUSED*/
1986CCRETVAL
1987e_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*/
2013CCRETVAL
2014e_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*/
2023CCRETVAL
2024e_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*/
2033CCRETVAL
2034e_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*/
2043CCRETVAL
2044e_expand_glob(Char c)
2045{
2046    USE(c);
2047    *LastChar = '\0';		/* just in case */
2048    return(CC_EXPAND_GLOB);
2049}
2050
2051/*ARGSUSED*/
2052CCRETVAL
2053e_normalize_path(Char c)
2054{
2055    USE(c);
2056    *LastChar = '\0';		/* just in case */
2057    return(CC_NORMALIZE_PATH);
2058}
2059
2060/*ARGSUSED*/
2061CCRETVAL
2062e_normalize_command(Char c)
2063{
2064    USE(c);
2065    *LastChar = '\0';		/* just in case */
2066    return(CC_NORMALIZE_COMMAND);
2067}
2068
2069/*ARGSUSED*/
2070CCRETVAL
2071e_expand_vars(Char c)
2072{
2073    USE(c);
2074    *LastChar = '\0';		/* just in case */
2075    return(CC_EXPAND_VARS);
2076}
2077
2078/*ARGSUSED*/
2079CCRETVAL
2080e_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*/
2091CCRETVAL
2092e_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*/
2131CCRETVAL
2132e_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*/
2224CCRETVAL
2225e_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*/
2260CCRETVAL
2261e_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*/
2324CCRETVAL
2325v_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*/
2342CCRETVAL
2343e_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*/
2356CCRETVAL
2357e_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*/
2403CCRETVAL
2404e_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*/
2426CCRETVAL
2427e_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*/
2455CCRETVAL
2456e_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*/
2473CCRETVAL
2474e_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*/
2498CCRETVAL
2499e_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*/
2518CCRETVAL
2519e_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*/
2539CCRETVAL
2540e_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*/
2554CCRETVAL
2555e_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*/
2574CCRETVAL
2575e_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*/
2588CCRETVAL
2589e_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*/
2600CCRETVAL
2601e_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*/
2611CCRETVAL
2612e_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*/
2636CCRETVAL
2637e_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*/
2653CCRETVAL
2654e_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*/
2675CCRETVAL
2676e_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*/
2693CCRETVAL
2694e_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*/
2718CCRETVAL
2719v_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*/
2738CCRETVAL
2739e_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*/
2759CCRETVAL
2760e_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*/
2783CCRETVAL
2784e_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*/
2804CCRETVAL
2805v_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*/
2825CCRETVAL
2826v_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*/
2848static CCRETVAL
2849v_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
2872static CCRETVAL
2873v_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
2903static CCRETVAL
2904v_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*/
2934static CCRETVAL
2935v_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> */
2977static void
2978c_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*/
2995CCRETVAL
2996e_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*/
3015CCRETVAL
3016e_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*/
3043CCRETVAL
3044e_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*/
3063CCRETVAL
3064e_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*/
3078CCRETVAL
3079e_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*/
3092CCRETVAL
3093e_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
3103static void
3104quote_mode_cleanup(void *unused)
3105{
3106    USE(unused);
3107    QuoteModeOff();
3108}
3109
3110/*ARGSUSED*/
3111CCRETVAL
3112e_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*/
3129CCRETVAL
3130e_metanext(Char c)
3131{
3132    USE(c);
3133    MetaNext = 1;
3134    return(CC_ARGHACK);	/* preserve argument */
3135}
3136
3137#ifdef notdef
3138/*ARGSUSED*/
3139CCRETVAL
3140e_extendnext(Char c)
3141{
3142    CurrentKeyMap = CcAltMap;
3143    return(CC_ARGHACK);	/* preserve argument */
3144}
3145
3146#endif
3147
3148/*ARGSUSED*/
3149CCRETVAL
3150v_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*/
3166CCRETVAL
3167v_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*/
3179CCRETVAL
3180v_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*/
3192CCRETVAL
3193v_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*/
3202CCRETVAL
3203v_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*/
3212CCRETVAL
3213v_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*/
3222CCRETVAL
3223v_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*/
3236CCRETVAL
3237v_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*/
3257CCRETVAL
3258v_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*/
3273CCRETVAL
3274v_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*/
3298CCRETVAL
3299e_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*/
3315CCRETVAL
3316e_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*/
3324CCRETVAL
3325e_redisp(Char c)
3326{
3327    USE(c);
3328    ClearLines();
3329    ClearDisp();
3330    return(CC_REFRESH);
3331}
3332
3333/*ARGSUSED*/
3334CCRETVAL
3335e_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*/
3344CCRETVAL
3345e_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*/
3366CCRETVAL
3367e_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*/
3391CCRETVAL
3392e_insovr(Char c)
3393{
3394    USE(c);
3395    inputmode = (inputmode == MODE_INSERT ? MODE_REPLACE : MODE_INSERT);
3396    return(CC_NORM);
3397}
3398
3399/*ARGSUSED*/
3400CCRETVAL
3401e_tty_dsusp(Char c)
3402{
3403    USE(c);
3404    /* do no editing */
3405    return(CC_NORM);
3406}
3407
3408/*ARGSUSED*/
3409CCRETVAL
3410e_tty_flusho(Char c)
3411{
3412    USE(c);
3413    /* do no editing */
3414    return(CC_NORM);
3415}
3416
3417/*ARGSUSED*/
3418CCRETVAL
3419e_tty_quit(Char c)
3420{
3421    USE(c);
3422    /* do no editing */
3423    return(CC_NORM);
3424}
3425
3426/*ARGSUSED*/
3427CCRETVAL
3428e_tty_tsusp(Char c)
3429{
3430    USE(c);
3431    /* do no editing */
3432    return(CC_NORM);
3433}
3434
3435/*ARGSUSED*/
3436CCRETVAL
3437e_tty_stopo(Char c)
3438{
3439    USE(c);
3440    /* do no editing */
3441    return(CC_NORM);
3442}
3443
3444/*ARGSUSED*/
3445CCRETVAL
3446e_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*/
3455CCRETVAL
3456e_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*/
3465CCRETVAL
3466e_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*/
3486CCRETVAL
3487e_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*/
3506CCRETVAL
3507e_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*/
3530CCRETVAL
3531e_tty_starto(Char c)
3532{
3533    USE(c);
3534    /* do no editing */
3535    return(CC_NORM);
3536}
3537
3538/*ARGSUSED*/
3539CCRETVAL
3540e_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*/
3556CCRETVAL
3557v_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*/
3568CCRETVAL
3569v_delmeta(Char c)
3570{
3571    USE(c);
3572    return(v_action(TCSHOP_DELETE));
3573}
3574
3575
3576/*ARGSUSED*/
3577CCRETVAL
3578v_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*/
3599CCRETVAL
3600v_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*/
3620CCRETVAL
3621v_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*/
3637CCRETVAL
3638v_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*/
3653CCRETVAL
3654v_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*/
3667CCRETVAL
3668v_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*/
3680CCRETVAL
3681v_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*/
3692CCRETVAL
3693v_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*/
3704CCRETVAL
3705v_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*/
3766CCRETVAL
3767v_ush_meta(Char c)
3768{
3769    USE(c);
3770    return v_search(F_UP_SEARCH_HIST);
3771}
3772
3773/*ARGSUSED*/
3774CCRETVAL
3775v_dsh_meta(Char c)
3776{
3777    USE(c);
3778    return v_search(F_DOWN_SEARCH_HIST);
3779}
3780
3781/*ARGSUSED*/
3782CCRETVAL
3783v_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*/
3791CCRETVAL
3792v_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 */
3804CCRETVAL
3805e_copy_to_clipboard(Char c)
3806{
3807    USE(c);
3808    return CC_ERROR;
3809}
3810
3811CCRETVAL
3812e_paste_from_clipboard(Char c)
3813{
3814    USE(c);
3815    return (CC_ERROR);
3816}
3817
3818CCRETVAL
3819e_dosify_next(Char c)
3820{
3821    USE(c);
3822    return (CC_ERROR);
3823}
3824CCRETVAL
3825e_dosify_prev(Char c)
3826{
3827    USE(c);
3828    return (CC_ERROR);
3829}
3830CCRETVAL
3831e_page_up(Char c)
3832{
3833    USE(c);
3834    return (CC_ERROR);
3835}
3836CCRETVAL
3837e_page_down(Char c)
3838{
3839    USE(c);
3840    return (CC_ERROR);
3841}
3842#endif /* !WINNT_NATIVE */
3843
3844#ifdef notdef
3845void
3846MoveCursor(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
3856Char *
3857GetCursor(void)
3858{
3859    return(Cursor);
3860}
3861
3862int
3863PutCursor(Char *p)
3864{
3865    if (p < InputBuf || p > LastChar)
3866	return 1;		/* Error */
3867    Cursor = p;
3868    return 0;
3869}
3870#endif
3871