freebsd-skq/contrib/tcsh/ed.chared.c
dchagin 9454a5ff86 MFV r315950:
Update vendor/tcsh to git b605cb561d

Vendor changes:

1. PR/471: Daiki Ueno: Delay interpreting arginp until we've processed
our startup files (which can change the NLS environment).

2. Fix type of read in prompt confirmation (eg. rmstar) (David Kaspar).

3. Fix out of bounds read (Brooks Davis)
(reproduce by starting tcsh and hitting tab at the prompt).

4. Don't play pointer tricks that are undefined in modern c
(Brooks Davis).
2017-03-25 14:14:11 +00:00

3899 lines
75 KiB
C

/* $Header: /p/tcsh/cvsroot/tcsh/ed.chared.c,v 3.103 2015/08/19 14:29:55 christos Exp $ */
/*
* ed.chared.c: Character editing functions.
*/
/*-
* Copyright (c) 1980, 1991 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
Bjorn Knutsson @ Thu Jun 24 19:02:17 1999
e_dabbrev_expand() did not do proper completion if quoted spaces were present
in the string being completed. Exemple:
# echo hello\ world
hello world
# echo h<press key bound to dabbrev-expande>
# echo hello\<cursor>
Correct behavior is:
# echo h<press key bound to dabbrev-expande>
# echo hello\ world<cursor>
The same problem occured if spaces were present in a string withing quotation
marks. Example:
# echo "hello world"
hello world
# echo "h<press key bound to dabbrev-expande>
# echo "hello<cursor>
The former problem could be solved with minor modifications of c_preword()
and c_endword(). The latter, however, required a significant rewrite of
c_preword(), since quoted strings must be parsed from start to end to
determine if a given character is inside or outside the quotation marks.
Compare the following two strings:
# echo \"" 'foo \' bar\"
" 'foo \' bar\
# echo '\"" 'foo \' bar\"
\"" foo ' bar"
The only difference between the two echo lines is in the first character
after the echo command. The result is either one or three arguments.
*/
#include "sh.h"
RCSID("$tcsh: ed.chared.c,v 3.103 2015/08/19 14:29:55 christos Exp $")
#include "ed.h"
#include "tw.h"
#include "ed.defns.h"
/* #define SDEBUG */
#define TCSHOP_NOP 0x00
#define TCSHOP_DELETE 0x01
#define TCSHOP_INSERT 0x02
#define TCSHOP_CHANGE 0x04
#define CHAR_FWD 0
#define CHAR_BACK 1
/*
* vi word treatment
* from: Gert-Jan Vons <vons@cesar.crbca1.sinet.slb.com>
*/
#define C_CLASS_WHITE 1
#define C_CLASS_WORD 2
#define C_CLASS_OTHER 3
static Char *InsertPos = InputBuf; /* Where insertion starts */
static Char *ActionPos = 0; /* Where action begins */
static int ActionFlag = TCSHOP_NOP; /* What delayed action to take */
/*
* Word search state
*/
static int searchdir = F_UP_SEARCH_HIST; /* Direction of last search */
static struct Strbuf patbuf; /* = Strbuf_INIT; Search target */
/*
* Char search state
*/
static int srch_dir = CHAR_FWD; /* Direction of last search */
static Char srch_char = 0; /* Search target */
/* all routines that start with c_ are private to this set of routines */
static void c_alternativ_key_map (int);
void c_insert (int);
void c_delafter (int);
void c_delbefore (int);
static int c_to_class (Char);
static Char *c_prev_word (Char *, Char *, int);
static Char *c_next_word (Char *, Char *, int);
static Char *c_number (Char *, int *, int);
static Char *c_expand (Char *);
static int c_excl (Char *);
static int c_substitute (void);
static void c_delfini (void);
static int c_hmatch (Char *);
static void c_hsetpat (void);
#ifdef COMMENT
static void c_get_word (Char **, Char **);
#endif
static Char *c_preword (Char *, Char *, int, Char *);
static Char *c_nexword (Char *, Char *, int);
static Char *c_endword (Char *, Char *, int, Char *);
static Char *c_eword (Char *, Char *, int);
static void c_push_kill (Char *, Char *);
static void c_save_inputbuf (void);
static CCRETVAL c_search_line (Char *, int);
static CCRETVAL v_repeat_srch (int);
static CCRETVAL e_inc_search (int);
#ifdef notyet
static CCRETVAL e_insert_str (Char *);
#endif
static CCRETVAL v_search (int);
static CCRETVAL v_csearch_fwd (Char, int, int);
static CCRETVAL v_action (int);
static CCRETVAL v_csearch_back (Char, int, int);
static void
c_alternativ_key_map(int state)
{
switch (state) {
case 0:
CurrentKeyMap = CcKeyMap;
break;
case 1:
CurrentKeyMap = CcAltMap;
break;
default:
return;
}
AltKeyMap = (Char) state;
}
void
c_insert(int num)
{
Char *cp;
if (LastChar + num >= InputLim)
return; /* can't go past end of buffer */
if (Cursor < LastChar) { /* if I must move chars */
for (cp = LastChar; cp >= Cursor; cp--)
cp[num] = *cp;
if (Mark && Mark > Cursor)
Mark += num;
}
LastChar += num;
}
void
c_delafter(int num)
{
Char *cp, *kp = NULL;
if (num > LastChar - Cursor)
num = (int) (LastChar - Cursor); /* bounds check */
if (num > 0) { /* if I can delete anything */
if (VImode) {
kp = UndoBuf; /* Set Up for VI undo command */
UndoAction = TCSHOP_INSERT;
UndoSize = num;
UndoPtr = Cursor;
for (cp = Cursor; cp <= LastChar; cp++) {
*kp++ = *cp; /* Save deleted chars into undobuf */
*cp = cp[num];
}
}
else
for (cp = Cursor; cp + num <= LastChar; cp++)
*cp = cp[num];
LastChar -= num;
/* Mark was within the range of the deleted word? */
if (Mark && Mark > Cursor && Mark <= Cursor+num)
Mark = Cursor;
/* Mark after the deleted word? */
else if (Mark && Mark > Cursor)
Mark -= num;
}
#ifdef notdef
else {
/*
* XXX: We don't want to do that. In emacs mode overwrite should be
* sticky. I am not sure how that affects vi mode
*/
inputmode = MODE_INSERT;
}
#endif /* notdef */
}
void
c_delbefore(int num) /* delete before dot, with bounds checking */
{
Char *cp, *kp = NULL;
if (num > Cursor - InputBuf)
num = (int) (Cursor - InputBuf); /* bounds check */
if (num > 0) { /* if I can delete anything */
if (VImode) {
kp = UndoBuf; /* Set Up for VI undo command */
UndoAction = TCSHOP_INSERT;
UndoSize = num;
UndoPtr = Cursor - num;
for (cp = Cursor - num; cp <= LastChar; cp++) {
*kp++ = *cp;
*cp = cp[num];
}
}
else
for (cp = Cursor - num; cp + num <= LastChar; cp++)
*cp = cp[num];
LastChar -= num;
Cursor -= num;
/* Mark was within the range of the deleted word? */
if (Mark && Mark > Cursor && Mark <= Cursor+num)
Mark = Cursor;
/* Mark after the deleted word? */
else if (Mark && Mark > Cursor)
Mark -= num;
}
}
static Char *
c_preword(Char *p, Char *low, int n, Char *delim)
{
while (n--) {
Char *prev = low;
Char *new;
while (prev < p) { /* Skip initial non-word chars */
if (!Strchr(delim, *prev) || *(prev-1) == (Char)'\\')
break;
prev++;
}
new = prev;
while (new < p) {
prev = new;
new = c_endword(prev-1, p, 1, delim); /* Skip to next non-word char */
new++; /* Step away from end of word */
while (new <= p) { /* Skip trailing non-word chars */
if (!Strchr(delim, *new) || *(new-1) == (Char)'\\')
break;
new++;
}
}
p = prev; /* Set to previous word start */
}
if (p < low)
p = low;
return (p);
}
/*
* c_to_class() returns the class of the given character.
*
* This is used to make the c_prev_word(), c_next_word() and c_eword() functions
* work like vi's, which classify characters. A word is a sequence of
* characters belonging to the same class, classes being defined as
* follows:
*
* 1/ whitespace
* 2/ alphanumeric chars, + underscore
* 3/ others
*/
static int
c_to_class(Char ch)
{
if (Isspace(ch))
return C_CLASS_WHITE;
if (isword(ch))
return C_CLASS_WORD;
return C_CLASS_OTHER;
}
static Char *
c_prev_word(Char *p, Char *low, int n)
{
p--;
if (!VImode) {
while (n--) {
while ((p >= low) && !isword(*p))
p--;
while ((p >= low) && isword(*p))
p--;
}
/* cp now points to one character before the word */
p++;
if (p < low)
p = low;
/* cp now points where we want it */
return(p);
}
while (n--) {
int c_class;
if (p < low)
break;
/* scan until beginning of current word (may be all whitespace!) */
c_class = c_to_class(*p);
while ((p >= low) && c_class == c_to_class(*p))
p--;
/* if this was a non_whitespace word, we're ready */
if (c_class != C_CLASS_WHITE)
continue;
/* otherwise, move back to beginning of the word just found */
c_class = c_to_class(*p);
while ((p >= low) && c_class == c_to_class(*p))
p--;
}
p++; /* correct overshoot */
return (p);
}
static Char *
c_next_word(Char *p, Char *high, int n)
{
if (!VImode) {
while (n--) {
while ((p < high) && !isword(*p))
p++;
while ((p < high) && isword(*p))
p++;
}
if (p > high)
p = high;
/* p now points where we want it */
return(p);
}
while (n--) {
int c_class;
if (p >= high)
break;
/* scan until end of current word (may be all whitespace!) */
c_class = c_to_class(*p);
while ((p < high) && c_class == c_to_class(*p))
p++;
/* if this was all whitespace, we're ready */
if (c_class == C_CLASS_WHITE)
continue;
/* if we've found white-space at the end of the word, skip it */
while ((p < high) && c_to_class(*p) == C_CLASS_WHITE)
p++;
}
p--; /* correct overshoot */
return (p);
}
static Char *
c_nexword(Char *p, Char *high, int n)
{
while (n--) {
while ((p < high) && !Isspace(*p))
p++;
while ((p < high) && Isspace(*p))
p++;
}
if (p > high)
p = high;
/* p now points where we want it */
return(p);
}
/*
* Expand-History (originally "Magic-Space") code added by
* Ray Moody <ray@gibbs.physics.purdue.edu>
* this is a neat, but odd, addition.
*/
/*
* c_number: Ignore character p points to, return number appearing after that.
* A '$' by itself means a big number; "$-" is for negative; '^' means 1.
* Return p pointing to last char used.
*/
/*
* dval is the number to subtract from for things like $-3
*/
static Char *
c_number(Char *p, int *num, int dval)
{
int i;
int sign = 1;
if (*++p == '^') {
*num = 1;
return(p);
}
if (*p == '$') {
if (*++p != '-') {
*num = INT_MAX; /* Handle $ */
return(--p);
}
sign = -1; /* Handle $- */
++p;
}
for (i = 0; *p >= '0' && *p <= '9'; i = 10 * i + *p++ - '0')
continue;
*num = (sign < 0 ? dval - i : i);
return(--p);
}
/*
* excl_expand: There is an excl to be expanded to p -- do the right thing
* with it and return a version of p advanced over the expanded stuff. Also,
* update tsh_cur and related things as appropriate...
*/
static Char *
c_expand(Char *p)
{
Char *q;
struct Hist *h = Histlist.Hnext;
struct wordent *l;
int i, from, to, dval;
int all_dig;
int been_once = 0;
Char *op = p;
Char *buf;
size_t buf_len;
Char *modbuf;
buf = NULL;
if (!h)
goto excl_err;
excl_sw:
switch (*(q = p + 1)) {
case '^':
buf = expand_lex(&h->Hlex, 1, 1);
break;
case '$':
if ((l = (h->Hlex).prev) != 0)
buf = expand_lex(l->prev->prev, 0, 0);
break;
case '*':
buf = expand_lex(&h->Hlex, 1, INT_MAX);
break;
default:
if (been_once) { /* unknown argument */
/* assume it's a modifier, e.g. !foo:h, and get whole cmd */
buf = expand_lex(&h->Hlex, 0, INT_MAX);
q -= 2;
break;
}
been_once = 1;
if (*q == ':') /* short form: !:arg */
--q;
if (HIST != '\0' && *q != HIST) {
/*
* Search for a space, tab, or colon. See if we have a number (as
* in !1234:xyz). Remember the number.
*/
for (i = 0, all_dig = 1;
*q != ' ' && *q != '\t' && *q != ':' && q < Cursor; q++) {
/*
* PWP: !-4 is a valid history argument too, therefore the test
* is if not a digit, or not a - as the first character.
*/
if ((*q < '0' || *q > '9') && (*q != '-' || q != p + 1))
all_dig = 0;
else if (*q == '-')
all_dig = 2;/* we are sneeky about this */
else
i = 10 * i + *q - '0';
}
--q;
/*
* If we have a number, search for event i. Otherwise, search for
* a named event (as in !foo). (In this case, I is the length of
* the named event).
*/
if (all_dig) {
if (all_dig == 2)
i = -i; /* make it negitive */
if (i < 0) /* if !-4 (for example) */
i = eventno + 1 + i; /* remember: i is < 0 */
for (; h; h = h->Hnext) {
if (h->Hnum == i)
break;
}
}
else {
for (i = (int) (q - p); h; h = h->Hnext) {
if ((l = &h->Hlex) != 0) {
if (!Strncmp(p + 1, l->next->word, (size_t) i))
break;
}
}
}
}
if (!h)
goto excl_err;
if (q[1] == ':' || q[1] == '-' || q[1] == '*' ||
q[1] == '$' || q[1] == '^') { /* get some args */
p = q[1] == ':' ? ++q : q;
/*
* Go handle !foo:*
*/
if ((q[1] < '0' || q[1] > '9') &&
q[1] != '-' && q[1] != '$' && q[1] != '^')
goto excl_sw;
/*
* Go handle !foo:$
*/
if (q[1] == '$' && (q[2] != '-' || q[3] < '0' || q[3] > '9'))
goto excl_sw;
/*
* Count up the number of words in this event. Store it in dval.
* Dval will be fed to number.
*/
dval = 0;
if ((l = h->Hlex.prev) != 0) {
for (l = l->prev; l != h->Hlex.next; l = l->prev, dval++)
continue;
}
if (!dval)
goto excl_err;
if (q[1] == '-')
from = 0;
else
q = c_number(q, &from, dval);
if (q[1] == '-') {
++q;
if ((q[1] < '0' || q[1] > '9') && q[1] != '$')
to = dval - 1;
else
q = c_number(q, &to, dval);
}
else if (q[1] == '*') {
++q;
to = INT_MAX;
}
else {
to = from;
}
if (from < 0 || to < from)
goto excl_err;
buf = expand_lex(&h->Hlex, from, to);
}
else /* get whole cmd */
buf = expand_lex(&h->Hlex, 0, INT_MAX);
break;
}
if (buf == NULL)
buf = SAVE("");
/*
* Apply modifiers, if any.
*/
if (q[1] == ':') {
modbuf = buf;
while (q[1] == ':' && modbuf != NULL) {
switch (q[2]) {
case 'r':
case 'e':
case 'h':
case 't':
case 'q':
case 'x':
case 'u':
case 'l':
if ((modbuf = domod(buf, (int) q[2])) != NULL) {
xfree(buf);
buf = modbuf;
}
++q;
break;
case 'a':
case 'g':
/* Not implemented; this needs to be done before expanding
* lex. We don't have the words available to us anymore.
*/
++q;
break;
case 'p':
/* Ok */
++q;
break;
case '\0':
break;
default:
++q;
break;
}
if (q[1])
++q;
}
}
buf_len = Strlen(buf);
/*
* Now replace the text from op to q inclusive with the text from buf.
*/
q++;
/*
* Now replace text non-inclusively like a real CS major!
*/
if (LastChar + buf_len - (q - op) >= InputLim)
goto excl_err;
(void) memmove(op + buf_len, q, (LastChar - q) * sizeof(Char));
LastChar += buf_len - (q - op);
Cursor += buf_len - (q - op);
(void) memcpy(op, buf, buf_len * sizeof(Char));
*LastChar = '\0';
xfree(buf);
return op + buf_len;
excl_err:
xfree(buf);
SoundBeep();
return(op + 1);
}
/*
* c_excl: An excl has been found at point p -- back up and find some white
* space (or the beginning of the buffer) and properly expand all the excl's
* from there up to the current cursor position. We also avoid (trying to)
* expanding '>!'
* Returns number of expansions attempted (doesn't matter whether they succeeded
* or not).
*/
static int
c_excl(Char *p)
{
int i;
Char *q;
int nr_exp;
/*
* if />[SPC TAB]*![SPC TAB]/, back up p to just after the >. otherwise,
* back p up to just before the current word.
*/
if ((p[1] == ' ' || p[1] == '\t') &&
(p[-1] == ' ' || p[-1] == '\t' || p[-1] == '>')) {
for (q = p - 1; q > InputBuf && (*q == ' ' || *q == '\t'); --q)
continue;
if (*q == '>')
++p;
}
else {
while (*p != ' ' && *p != '\t' && p > InputBuf)
--p;
}
/*
* Forever: Look for history char. (Stop looking when we find the cursor.)
* Count backslashes. If odd, skip history char. Expand if even number of
* backslashes.
*/
nr_exp = 0;
for (;;) {
if (HIST != '\0')
while (*p != HIST && p < Cursor)
++p;
for (i = 1; (p - i) >= InputBuf && p[-i] == '\\'; i++)
continue;
if (i % 2 == 0)
++p;
if (p >= Cursor) /* all done */
return nr_exp;
if (i % 2 == 1) {
p = c_expand(p);
++nr_exp;
}
}
}
static int
c_substitute(void)
{
Char *p;
int nr_exp;
/*
* Start p out one character before the cursor. Move it backwards looking
* for white space, the beginning of the line, or a history character.
*/
for (p = Cursor - 1;
p > InputBuf && *p != ' ' && *p != '\t' && *p && *p != HIST; --p)
continue;
/*
* If we found a history character, go expand it.
*/
if (p >= InputBuf && HIST != '\0' && *p == HIST)
nr_exp = c_excl(p);
else
nr_exp = 0;
Refresh();
return nr_exp;
}
static void
c_delfini(void) /* Finish up delete action */
{
int Size;
if (ActionFlag & TCSHOP_INSERT)
c_alternativ_key_map(0);
ActionFlag = TCSHOP_NOP;
if (ActionPos == 0)
return;
UndoAction = TCSHOP_INSERT;
if (Cursor > ActionPos) {
Size = (int) (Cursor-ActionPos);
c_delbefore(Size);
RefCursor();
}
else if (Cursor < ActionPos) {
Size = (int)(ActionPos-Cursor);
c_delafter(Size);
}
else {
Size = 1;
c_delafter(Size);
}
UndoPtr = Cursor;
UndoSize = Size;
}
static Char *
c_endword(Char *p, Char *high, int n, Char *delim)
{
Char inquote = 0;
p++;
while (n--) {
while (p < high) { /* Skip non-word chars */
if (!Strchr(delim, *p) || *(p-1) == (Char)'\\')
break;
p++;
}
while (p < high) { /* Skip string */
if ((*p == (Char)'\'' || *p == (Char)'"')) { /* Quotation marks? */
if (inquote || *(p-1) != (Char)'\\') { /* Should it be honored? */
if (inquote == 0) inquote = *p;
else if (inquote == *p) inquote = 0;
}
}
/* Break if unquoted non-word char */
if (!inquote && Strchr(delim, *p) && *(p-1) != (Char)'\\')
break;
p++;
}
}
p--;
return(p);
}
static Char *
c_eword(Char *p, Char *high, int n)
{
p++;
while (n--) {
int c_class;
if (p >= high)
break;
/* scan until end of current word (may be all whitespace!) */
c_class = c_to_class(*p);
while ((p < high) && c_class == c_to_class(*p))
p++;
/* if this was a non_whitespace word, we're ready */
if (c_class != C_CLASS_WHITE)
continue;
/* otherwise, move to the end of the word just found */
c_class = c_to_class(*p);
while ((p < high) && c_class == c_to_class(*p))
p++;
}
p--;
return(p);
}
/* Set the max length of the kill ring */
void
SetKillRing(int max)
{
CStr *new;
int count, i, j;
if (max < 1)
max = 1; /* no ring, but always one buffer */
if (max == KillRingMax)
return;
new = xcalloc(max, sizeof(CStr));
if (KillRing != NULL) {
if (KillRingLen != 0) {
if (max >= KillRingLen) {
count = KillRingLen;
j = KillPos;
} else {
count = max;
j = (KillPos - count + KillRingLen) % KillRingLen;
}
for (i = 0; i < KillRingLen; i++) {
if (i < count) /* copy latest */
new[i] = KillRing[j];
else /* free the others */
xfree(KillRing[j].buf);
j = (j + 1) % KillRingLen;
}
KillRingLen = count;
KillPos = count % max;
YankPos = count - 1;
}
xfree(KillRing);
}
KillRing = new;
KillRingMax = max;
}
/* Push string from start upto (but not including) end onto kill ring */
static void
c_push_kill(Char *start, Char *end)
{
CStr save, *pos;
Char *dp, *cp, *kp;
int len = end - start, i, j, k;
/* Check for duplicates? */
if (KillRingLen > 0 && (dp = varval(STRkilldup)) != STRNULL) {
YankPos = (KillPos - 1 + KillRingLen) % KillRingLen;
if (eq(dp, STRerase)) { /* erase earlier one (actually move up) */
j = YankPos;
for (i = 0; i < KillRingLen; i++) {
if (Strncmp(KillRing[j].buf, start, (size_t) len) == 0 &&
KillRing[j].buf[len] == '\0') {
save = KillRing[j];
for ( ; i > 0; i--) {
k = j;
j = (j + 1) % KillRingLen;
KillRing[k] = KillRing[j];
}
KillRing[j] = save;
return;
}
j = (j - 1 + KillRingLen) % KillRingLen;
}
} else if (eq(dp, STRall)) { /* skip if any earlier */
for (i = 0; i < KillRingLen; i++)
if (Strncmp(KillRing[i].buf, start, (size_t) len) == 0 &&
KillRing[i].buf[len] == '\0')
return;
} else if (eq(dp, STRprev)) { /* skip if immediately previous */
j = YankPos;
if (Strncmp(KillRing[j].buf, start, (size_t) len) == 0 &&
KillRing[j].buf[len] == '\0')
return;
}
}
/* No duplicate, go ahead and push */
len++; /* need space for '\0' */
YankPos = KillPos;
if (KillRingLen < KillRingMax)
KillRingLen++;
pos = &KillRing[KillPos];
KillPos = (KillPos + 1) % KillRingMax;
if (pos->len < len) {
pos->buf = xrealloc(pos->buf, len * sizeof(Char));
pos->len = len;
}
cp = start;
kp = pos->buf;
while (cp < end)
*kp++ = *cp++;
*kp = '\0';
}
/* Save InputBuf etc in SavedBuf etc for restore after cmd exec */
static void
c_save_inputbuf(void)
{
SavedBuf.len = 0;
Strbuf_append(&SavedBuf, InputBuf);
Strbuf_terminate(&SavedBuf);
LastSaved = LastChar - InputBuf;
CursSaved = Cursor - InputBuf;
HistSaved = Hist_num;
RestoreSaved = 1;
}
CCRETVAL
GetHistLine(void)
{
struct Hist *hp;
int h;
if (Hist_num == 0) { /* if really the current line */
if (HistBuf.s != NULL)
copyn(InputBuf, HistBuf.s, INBUFSIZE);/*FIXBUF*/
else
*InputBuf = '\0';
LastChar = InputBuf + HistBuf.len;
#ifdef KSHVI
if (VImode)
Cursor = InputBuf;
else
#endif /* KSHVI */
Cursor = LastChar;
return(CC_REFRESH);
}
hp = Histlist.Hnext;
if (hp == NULL)
return(CC_ERROR);
for (h = 1; h < Hist_num; h++) {
if ((hp->Hnext) == NULL) {
Hist_num = h;
return(CC_ERROR);
}
hp = hp->Hnext;
}
if (HistLit && hp->histline) {
copyn(InputBuf, hp->histline, INBUFSIZE);/*FIXBUF*/
CurrentHistLit = 1;
}
else {
Char *p;
p = sprlex(&hp->Hlex);
copyn(InputBuf, p, sizeof(InputBuf) / sizeof(Char));/*FIXBUF*/
xfree(p);
CurrentHistLit = 0;
}
LastChar = Strend(InputBuf);
if (LastChar > InputBuf) {
if (LastChar[-1] == '\n')
LastChar--;
#if 0
if (LastChar[-1] == ' ')
LastChar--;
#endif
if (LastChar < InputBuf)
LastChar = InputBuf;
}
#ifdef KSHVI
if (VImode)
Cursor = InputBuf;
else
#endif /* KSHVI */
Cursor = LastChar;
return(CC_REFRESH);
}
static CCRETVAL
c_search_line(Char *pattern, int dir)
{
Char *cp;
size_t len;
len = Strlen(pattern);
if (dir == F_UP_SEARCH_HIST) {
for (cp = Cursor; cp >= InputBuf; cp--)
if (Strncmp(cp, pattern, len) == 0 ||
Gmatch(cp, pattern)) {
Cursor = cp;
return(CC_NORM);
}
return(CC_ERROR);
} else {
for (cp = Cursor; *cp != '\0' && cp < InputLim; cp++)
if (Strncmp(cp, pattern, len) == 0 ||
Gmatch(cp, pattern)) {
Cursor = cp;
return(CC_NORM);
}
return(CC_ERROR);
}
}
static CCRETVAL
e_inc_search(int dir)
{
static const Char STRfwd[] = { 'f', 'w', 'd', '\0' },
STRbck[] = { 'b', 'c', 'k', '\0' };
static Char pchar = ':'; /* ':' = normal, '?' = failed */
static Char endcmd[2];
const Char *cp;
Char ch,
*oldCursor = Cursor,
oldpchar = pchar;
CCRETVAL ret = CC_NORM;
int oldHist_num = Hist_num,
oldpatlen = patbuf.len,
newdir = dir,
done, redo;
if (LastChar + sizeof(STRfwd)/sizeof(Char) + 2 + patbuf.len >= InputLim)
return(CC_ERROR);
for (;;) {
if (patbuf.len == 0) { /* first round */
pchar = ':';
Strbuf_append1(&patbuf, '*');
}
done = redo = 0;
*LastChar++ = '\n';
for (cp = newdir == F_UP_SEARCH_HIST ? STRbck : STRfwd;
*cp; *LastChar++ = *cp++)
continue;
*LastChar++ = pchar;
for (cp = &patbuf.s[1]; cp < &patbuf.s[patbuf.len];
*LastChar++ = *cp++)
continue;
*LastChar = '\0';
if (adrof(STRhighlight) && pchar == ':') {
/* if the no-glob-search patch is applied, remove the - 1 below */
IncMatchLen = patbuf.len - 1;
ClearLines();
ClearDisp();
}
Refresh();
if (GetNextChar(&ch) != 1)
return(e_send_eof(0));
switch (ch > NT_NUM_KEYS
? F_INSERT : CurrentKeyMap[(unsigned char) ch]) {
case F_INSERT:
case F_DIGIT:
case F_MAGIC_SPACE:
if (LastChar + 1 >= InputLim) /*FIXBUF*/
SoundBeep();
else {
Strbuf_append1(&patbuf, ch);
*LastChar++ = ch;
*LastChar = '\0';
Refresh();
}
break;
case F_INC_FWD:
newdir = F_DOWN_SEARCH_HIST;
redo++;
break;
case F_INC_BACK:
newdir = F_UP_SEARCH_HIST;
redo++;
break;
case F_DELPREV:
if (patbuf.len > 1)
done++;
else
SoundBeep();
break;
default:
switch (ASC(ch)) {
case 0007: /* ^G: Abort */
ret = CC_ERROR;
done++;
break;
case 0027: /* ^W: Append word */
/* No can do if globbing characters in pattern */
for (cp = &patbuf.s[1]; ; cp++)
if (cp >= &patbuf.s[patbuf.len]) {
Cursor += patbuf.len - 1;
cp = c_next_word(Cursor, LastChar, 1);
while (Cursor < cp && *Cursor != '\n') {
if (LastChar + 1 >= InputLim) {/*FIXBUF*/
SoundBeep();
break;
}
Strbuf_append1(&patbuf, *Cursor);
*LastChar++ = *Cursor++;
}
Cursor = oldCursor;
*LastChar = '\0';
Refresh();
break;
} else if (isglob(*cp)) {
SoundBeep();
break;
}
break;
default: /* Terminate and execute cmd */
endcmd[0] = ch;
PushMacro(endcmd);
/*FALLTHROUGH*/
case 0033: /* ESC: Terminate */
ret = CC_REFRESH;
done++;
break;
}
break;
}
while (LastChar > InputBuf && *LastChar != '\n')
*LastChar-- = '\0';
*LastChar = '\0';
if (!done) {
/* Can't search if unmatched '[' */
for (cp = &patbuf.s[patbuf.len - 1], ch = ']'; cp > patbuf.s; cp--)
if (*cp == '[' || *cp == ']') {
ch = *cp;
break;
}
if (patbuf.len > 1 && ch != '[') {
if (redo && newdir == dir) {
if (pchar == '?') { /* wrap around */
Hist_num = newdir == F_UP_SEARCH_HIST ? 0 : INT_MAX;
if (GetHistLine() == CC_ERROR)
/* Hist_num was fixed by first call */
(void) GetHistLine();
Cursor = newdir == F_UP_SEARCH_HIST ?
LastChar : InputBuf;
} else
Cursor += newdir == F_UP_SEARCH_HIST ? -1 : 1;
}
Strbuf_append1(&patbuf, '*');
Strbuf_terminate(&patbuf);
if (Cursor < InputBuf || Cursor > LastChar ||
(ret = c_search_line(&patbuf.s[1], newdir)) == CC_ERROR) {
LastCmd = (KEYCMD) newdir; /* avoid c_hsetpat */
ret = newdir == F_UP_SEARCH_HIST ?
e_up_search_hist(0) : e_down_search_hist(0);
if (ret != CC_ERROR) {
Cursor = newdir == F_UP_SEARCH_HIST ?
LastChar : InputBuf;
(void) c_search_line(&patbuf.s[1], newdir);
}
}
patbuf.s[--patbuf.len] = '\0';
if (ret == CC_ERROR) {
SoundBeep();
if (Hist_num != oldHist_num) {
Hist_num = oldHist_num;
if (GetHistLine() == CC_ERROR)
return(CC_ERROR);
}
Cursor = oldCursor;
pchar = '?';
} else {
pchar = ':';
}
}
ret = e_inc_search(newdir);
if (ret == CC_ERROR && pchar == '?' && oldpchar == ':') {
/* break abort of failed search at last non-failed */
ret = CC_NORM;
}
}
if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) {
/* restore on normal return or error exit */
pchar = oldpchar;
patbuf.len = oldpatlen;
if (Hist_num != oldHist_num) {
Hist_num = oldHist_num;
if (GetHistLine() == CC_ERROR)
return(CC_ERROR);
}
Cursor = oldCursor;
if (ret == CC_ERROR)
Refresh();
}
if (done || ret != CC_NORM)
return(ret);
}
}
static CCRETVAL
v_search(int dir)
{
struct Strbuf tmpbuf = Strbuf_INIT;
Char ch;
Char *oldbuf;
Char *oldlc, *oldc;
cleanup_push(&tmpbuf, Strbuf_cleanup);
oldbuf = Strsave(InputBuf);
cleanup_push(oldbuf, xfree);
oldlc = LastChar;
oldc = Cursor;
Strbuf_append1(&tmpbuf, '*');
InputBuf[0] = '\0';
LastChar = InputBuf;
Cursor = InputBuf;
searchdir = dir;
c_insert(2); /* prompt + '\n' */
*Cursor++ = '\n';
*Cursor++ = dir == F_UP_SEARCH_HIST ? '?' : '/';
Refresh();
for (ch = 0;ch == 0;) {
if (GetNextChar(&ch) != 1) {
cleanup_until(&tmpbuf);
return(e_send_eof(0));
}
switch (ASC(ch)) {
case 0010: /* Delete and backspace */
case 0177:
if (tmpbuf.len > 1) {
*Cursor-- = '\0';
LastChar = Cursor;
tmpbuf.len--;
}
else {
copyn(InputBuf, oldbuf, INBUFSIZE);/*FIXBUF*/
LastChar = oldlc;
Cursor = oldc;
cleanup_until(&tmpbuf);
return(CC_REFRESH);
}
Refresh();
ch = 0;
break;
case 0033: /* ESC */
#ifdef IS_ASCII
case '\r': /* Newline */
case '\n':
#else
case '\012': /* ASCII Line feed */
case '\015': /* ASCII (or EBCDIC) Return */
#endif
break;
default:
Strbuf_append1(&tmpbuf, ch);
*Cursor++ = ch;
LastChar = Cursor;
Refresh();
ch = 0;
break;
}
}
cleanup_until(oldbuf);
if (tmpbuf.len == 1) {
/*
* Use the old pattern, but wild-card it.
*/
if (patbuf.len == 0) {
InputBuf[0] = '\0';
LastChar = InputBuf;
Cursor = InputBuf;
Refresh();
cleanup_until(&tmpbuf);
return(CC_ERROR);
}
if (patbuf.s[0] != '*') {
oldbuf = Strsave(patbuf.s);
patbuf.len = 0;
Strbuf_append1(&patbuf, '*');
Strbuf_append(&patbuf, oldbuf);
xfree(oldbuf);
Strbuf_append1(&patbuf, '*');
Strbuf_terminate(&patbuf);
}
}
else {
Strbuf_append1(&tmpbuf, '*');
Strbuf_terminate(&tmpbuf);
patbuf.len = 0;
Strbuf_append(&patbuf, tmpbuf.s);
Strbuf_terminate(&patbuf);
}
cleanup_until(&tmpbuf);
LastCmd = (KEYCMD) dir; /* avoid c_hsetpat */
Cursor = LastChar = InputBuf;
if ((dir == F_UP_SEARCH_HIST ? e_up_search_hist(0) :
e_down_search_hist(0)) == CC_ERROR) {
Refresh();
return(CC_ERROR);
}
else {
if (ASC(ch) == 0033) {
Refresh();
*LastChar++ = '\n';
*LastChar = '\0';
PastBottom();
return(CC_NEWLINE);
}
else
return(CC_REFRESH);
}
}
/*
* semi-PUBLIC routines. Any routine that is of type CCRETVAL is an
* entry point, called from the CcKeyMap indirected into the
* CcFuncTbl array.
*/
/*ARGSUSED*/
CCRETVAL
v_cmd_mode(Char c)
{
USE(c);
InsertPos = 0;
ActionFlag = TCSHOP_NOP; /* [Esc] cancels pending action */
ActionPos = 0;
DoingArg = 0;
if (UndoPtr > Cursor)
UndoSize = (int)(UndoPtr - Cursor);
else
UndoSize = (int)(Cursor - UndoPtr);
inputmode = MODE_INSERT;
c_alternativ_key_map(1);
#ifdef notdef
/*
* We don't want to move the cursor, because all the editing
* commands don't include the character under the cursor.
*/
if (Cursor > InputBuf)
Cursor--;
#endif
RefCursor();
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_unassigned(Char c)
{ /* bound to keys that arn't really assigned */
USE(c);
SoundBeep();
flush();
return(CC_NORM);
}
#ifdef notyet
static CCRETVAL
e_insert_str(Char *c)
{
int i, n;
n = Strlen(c);
if (LastChar + Argument * n >= InputLim)
return(CC_ERROR); /* end of buffer space */
if (inputmode != MODE_INSERT) {
c_delafter(Argument * Strlen(c));
}
c_insert(Argument * n);
while (Argument--) {
for (i = 0; i < n; i++)
*Cursor++ = c[i];
}
Refresh();
return(CC_NORM);
}
#endif
CCRETVAL
e_insert(Char c)
{
#ifndef SHORT_STRINGS
c &= ASCII; /* no meta chars ever */
#endif
if (!c)
return(CC_ERROR); /* no NULs in the input ever!! */
if (LastChar + Argument >= InputLim)
return(CC_ERROR); /* end of buffer space */
if (Argument == 1) { /* How was this optimized ???? */
if (inputmode != MODE_INSERT) {
UndoBuf[UndoSize++] = *Cursor;
UndoBuf[UndoSize] = '\0';
c_delafter(1); /* Do NOT use the saving ONE */
}
c_insert(1);
*Cursor++ = (Char) c;
DoingArg = 0; /* just in case */
RefPlusOne(1); /* fast refresh for one char. */
}
else {
if (inputmode != MODE_INSERT) {
int i;
for(i = 0; i < Argument; i++)
UndoBuf[UndoSize++] = *(Cursor + i);
UndoBuf[UndoSize] = '\0';
c_delafter(Argument); /* Do NOT use the saving ONE */
}
c_insert(Argument);
while (Argument--)
*Cursor++ = (Char) c;
Refresh();
}
if (inputmode == MODE_REPLACE_1)
(void) v_cmd_mode(0);
return(CC_NORM);
}
int
InsertStr(Char *s) /* insert ASCIZ s at cursor (for complete) */
{
int len;
if ((len = (int) Strlen(s)) <= 0)
return -1;
if (LastChar + len >= InputLim)
return -1; /* end of buffer space */
c_insert(len);
while (len--)
*Cursor++ = *s++;
return 0;
}
void
DeleteBack(int n) /* delete the n characters before . */
{
if (n <= 0)
return;
if (Cursor >= &InputBuf[n]) {
c_delbefore(n); /* delete before dot */
}
}
CCRETVAL
e_digit(Char c) /* gray magic here */
{
if (!Isdigit(c))
return(CC_ERROR); /* no NULs in the input ever!! */
if (DoingArg) { /* if doing an arg, add this in... */
if (LastCmd == F_ARGFOUR) /* if last command was ^U */
Argument = c - '0';
else {
if (Argument > 1000000)
return CC_ERROR;
Argument = (Argument * 10) + (c - '0');
}
return(CC_ARGHACK);
}
else {
if (LastChar + 1 >= InputLim)
return CC_ERROR; /* end of buffer space */
if (inputmode != MODE_INSERT) {
UndoBuf[UndoSize++] = *Cursor;
UndoBuf[UndoSize] = '\0';
c_delafter(1); /* Do NOT use the saving ONE */
}
c_insert(1);
*Cursor++ = (Char) c;
DoingArg = 0; /* just in case */
RefPlusOne(1); /* fast refresh for one char. */
}
return(CC_NORM);
}
CCRETVAL
e_argdigit(Char c) /* for ESC-n */
{
#ifdef IS_ASCII
c &= ASCII;
#else
c = CTL_ESC(ASC(c) & ASCII); /* stripping for EBCDIC done the ASCII way */
#endif
if (!Isdigit(c))
return(CC_ERROR); /* no NULs in the input ever!! */
if (DoingArg) { /* if doing an arg, add this in... */
if (Argument > 1000000)
return CC_ERROR;
Argument = (Argument * 10) + (c - '0');
}
else { /* else starting an argument */
Argument = c - '0';
DoingArg = 1;
}
return(CC_ARGHACK);
}
CCRETVAL
v_zero(Char c) /* command mode 0 for vi */
{
if (DoingArg) { /* if doing an arg, add this in... */
if (Argument > 1000000)
return CC_ERROR;
Argument = (Argument * 10) + (c - '0');
return(CC_ARGHACK);
}
else { /* else starting an argument */
Cursor = InputBuf;
if (ActionFlag & TCSHOP_DELETE) {
c_delfini();
return(CC_REFRESH);
}
RefCursor(); /* move the cursor */
return(CC_NORM);
}
}
/*ARGSUSED*/
CCRETVAL
e_newline(Char c)
{ /* always ignore argument */
USE(c);
if (adrof(STRhighlight) && MarkIsSet) {
MarkIsSet = 0;
ClearLines();
ClearDisp();
Refresh();
}
MarkIsSet = 0;
/* PastBottom(); NOW done in ed.inputl.c */
*LastChar++ = '\n'; /* for the benefit of CSH */
*LastChar = '\0'; /* just in case */
if (VImode)
InsertPos = InputBuf; /* Reset editing position */
return(CC_NEWLINE);
}
/*ARGSUSED*/
CCRETVAL
e_newline_hold(Char c)
{
USE(c);
c_save_inputbuf();
HistSaved = 0;
*LastChar++ = '\n'; /* for the benefit of CSH */
*LastChar = '\0'; /* just in case */
return(CC_NEWLINE);
}
/*ARGSUSED*/
CCRETVAL
e_newline_down_hist(Char c)
{
USE(c);
if (Hist_num > 1) {
HistSaved = Hist_num;
}
*LastChar++ = '\n'; /* for the benefit of CSH */
*LastChar = '\0'; /* just in case */
return(CC_NEWLINE);
}
/*ARGSUSED*/
CCRETVAL
e_send_eof(Char c)
{ /* for when ^D is ONLY send-eof */
USE(c);
PastBottom();
*LastChar = '\0'; /* just in case */
return(CC_EOF);
}
/*ARGSUSED*/
CCRETVAL
e_complete(Char c)
{
USE(c);
*LastChar = '\0'; /* just in case */
return(CC_COMPLETE);
}
/*ARGSUSED*/
CCRETVAL
e_complete_back(Char c)
{
USE(c);
*LastChar = '\0'; /* just in case */
return(CC_COMPLETE_BACK);
}
/*ARGSUSED*/
CCRETVAL
e_complete_fwd(Char c)
{
USE(c);
*LastChar = '\0'; /* just in case */
return(CC_COMPLETE_FWD);
}
/*ARGSUSED*/
CCRETVAL
e_complete_all(Char c)
{
USE(c);
*LastChar = '\0'; /* just in case */
return(CC_COMPLETE_ALL);
}
/*ARGSUSED*/
CCRETVAL
v_cm_complete(Char c)
{
USE(c);
if (Cursor < LastChar)
Cursor++;
*LastChar = '\0'; /* just in case */
return(CC_COMPLETE);
}
/*ARGSUSED*/
CCRETVAL
e_toggle_hist(Char c)
{
struct Hist *hp;
int h;
USE(c);
*LastChar = '\0'; /* just in case */
if (Hist_num <= 0) {
return CC_ERROR;
}
hp = Histlist.Hnext;
if (hp == NULL) { /* this is only if no history */
return(CC_ERROR);
}
for (h = 1; h < Hist_num; h++)
hp = hp->Hnext;
if (!CurrentHistLit) {
if (hp->histline) {
copyn(InputBuf, hp->histline, INBUFSIZE);/*FIXBUF*/
CurrentHistLit = 1;
}
else {
return CC_ERROR;
}
}
else {
Char *p;
p = sprlex(&hp->Hlex);
copyn(InputBuf, p, sizeof(InputBuf) / sizeof(Char));/*FIXBUF*/
xfree(p);
CurrentHistLit = 0;
}
LastChar = Strend(InputBuf);
if (LastChar > InputBuf) {
if (LastChar[-1] == '\n')
LastChar--;
if (LastChar[-1] == ' ')
LastChar--;
if (LastChar < InputBuf)
LastChar = InputBuf;
}
#ifdef KSHVI
if (VImode)
Cursor = InputBuf;
else
#endif /* KSHVI */
Cursor = LastChar;
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_up_hist(Char c)
{
Char beep = 0;
USE(c);
UndoAction = TCSHOP_NOP;
*LastChar = '\0'; /* just in case */
if (Hist_num == 0) { /* save the current buffer away */
HistBuf.len = 0;
Strbuf_append(&HistBuf, InputBuf);
Strbuf_terminate(&HistBuf);
}
Hist_num += Argument;
if (GetHistLine() == CC_ERROR) {
beep = 1;
(void) GetHistLine(); /* Hist_num was fixed by first call */
}
Refresh();
if (beep)
return(CC_ERROR);
else
return(CC_NORM); /* was CC_UP_HIST */
}
/*ARGSUSED*/
CCRETVAL
e_down_hist(Char c)
{
USE(c);
UndoAction = TCSHOP_NOP;
*LastChar = '\0'; /* just in case */
Hist_num -= Argument;
if (Hist_num < 0) {
Hist_num = 0;
return(CC_ERROR); /* make it beep */
}
return(GetHistLine());
}
/*
* c_hmatch() return True if the pattern matches the prefix
*/
static int
c_hmatch(Char *str)
{
if (Strncmp(patbuf.s, str, patbuf.len) == 0)
return 1;
return Gmatch(str, patbuf.s);
}
/*
* c_hsetpat(): Set the history seatch pattern
*/
static void
c_hsetpat(void)
{
if (LastCmd != F_UP_SEARCH_HIST && LastCmd != F_DOWN_SEARCH_HIST) {
patbuf.len = 0;
Strbuf_appendn(&patbuf, InputBuf, Cursor - InputBuf);
Strbuf_terminate(&patbuf);
}
#ifdef SDEBUG
xprintf("\nHist_num = %d\n", Hist_num);
xprintf("patlen = %d\n", (int)patbuf.len);
xprintf("patbuf = \"%S\"\n", patbuf.s);
xprintf("Cursor %d LastChar %d\n", Cursor - InputBuf, LastChar - InputBuf);
#endif
}
/*ARGSUSED*/
CCRETVAL
e_up_search_hist(Char c)
{
struct Hist *hp;
int h;
int found = 0;
USE(c);
ActionFlag = TCSHOP_NOP;
UndoAction = TCSHOP_NOP;
*LastChar = '\0'; /* just in case */
if (Hist_num < 0) {
#ifdef DEBUG_EDIT
xprintf("%s: e_up_search_hist(): Hist_num < 0; resetting.\n", progname);
#endif
Hist_num = 0;
return(CC_ERROR);
}
if (Hist_num == 0) {
HistBuf.len = 0;
Strbuf_append(&HistBuf, InputBuf);
Strbuf_terminate(&HistBuf);
}
hp = Histlist.Hnext;
if (hp == NULL)
return(CC_ERROR);
c_hsetpat(); /* Set search pattern !! */
for (h = 1; h <= Hist_num; h++)
hp = hp->Hnext;
while (hp != NULL) {
Char *hl;
int matched;
if (hp->histline == NULL)
hp->histline = sprlex(&hp->Hlex);
if (HistLit)
hl = hp->histline;
else {
hl = sprlex(&hp->Hlex);
cleanup_push(hl, xfree);
}
#ifdef SDEBUG
xprintf("Comparing with \"%S\"\n", hl);
#endif
matched = (Strncmp(hl, InputBuf, (size_t) (LastChar - InputBuf)) ||
hl[LastChar-InputBuf]) && c_hmatch(hl);
if (!HistLit)
cleanup_until(hl);
if (matched) {
found++;
break;
}
h++;
hp = hp->Hnext;
}
if (!found) {
#ifdef SDEBUG
xprintf("not found\n");
#endif
return(CC_ERROR);
}
Hist_num = h;
return(GetHistLine());
}
/*ARGSUSED*/
CCRETVAL
e_down_search_hist(Char c)
{
struct Hist *hp;
int h;
int found = 0;
USE(c);
ActionFlag = TCSHOP_NOP;
UndoAction = TCSHOP_NOP;
*LastChar = '\0'; /* just in case */
if (Hist_num == 0)
return(CC_ERROR);
hp = Histlist.Hnext;
if (hp == 0)
return(CC_ERROR);
c_hsetpat(); /* Set search pattern !! */
for (h = 1; h < Hist_num && hp; h++) {
Char *hl;
if (hp->histline == NULL)
hp->histline = sprlex(&hp->Hlex);
if (HistLit)
hl = hp->histline;
else {
hl = sprlex(&hp->Hlex);
cleanup_push(hl, xfree);
}
#ifdef SDEBUG
xprintf("Comparing with \"%S\"\n", hl);
#endif
if ((Strncmp(hl, InputBuf, (size_t) (LastChar - InputBuf)) ||
hl[LastChar-InputBuf]) && c_hmatch(hl))
found = h;
if (!HistLit)
cleanup_until(hl);
hp = hp->Hnext;
}
if (!found) { /* is it the current history number? */
if (!c_hmatch(HistBuf.s)) {
#ifdef SDEBUG
xprintf("not found\n");
#endif
return(CC_ERROR);
}
}
Hist_num = found;
return(GetHistLine());
}
/*ARGSUSED*/
CCRETVAL
e_helpme(Char c)
{
USE(c);
PastBottom();
*LastChar = '\0'; /* just in case */
return(CC_HELPME);
}
/*ARGSUSED*/
CCRETVAL
e_correct(Char c)
{
USE(c);
*LastChar = '\0'; /* just in case */
return(CC_CORRECT);
}
/*ARGSUSED*/
CCRETVAL
e_correctl(Char c)
{
USE(c);
*LastChar = '\0'; /* just in case */
return(CC_CORRECT_L);
}
/*ARGSUSED*/
CCRETVAL
e_run_fg_editor(Char c)
{
struct process *pp;
USE(c);
if ((pp = find_stop_ed()) != NULL) {
/* save our editor state so we can restore it */
c_save_inputbuf();
Hist_num = 0; /* for the history commands */
/* put the tty in a sane mode */
PastBottom();
(void) Cookedmode(); /* make sure the tty is set up correctly */
/* do it! */
fg_proc_entry(pp);
(void) Rawmode(); /* go on */
Refresh();
RestoreSaved = 0;
HistSaved = 0;
}
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_list_choices(Char c)
{
USE(c);
PastBottom();
*LastChar = '\0'; /* just in case */
return(CC_LIST_CHOICES);
}
/*ARGSUSED*/
CCRETVAL
e_list_all(Char c)
{
USE(c);
PastBottom();
*LastChar = '\0'; /* just in case */
return(CC_LIST_ALL);
}
/*ARGSUSED*/
CCRETVAL
e_list_glob(Char c)
{
USE(c);
PastBottom();
*LastChar = '\0'; /* just in case */
return(CC_LIST_GLOB);
}
/*ARGSUSED*/
CCRETVAL
e_expand_glob(Char c)
{
USE(c);
*LastChar = '\0'; /* just in case */
return(CC_EXPAND_GLOB);
}
/*ARGSUSED*/
CCRETVAL
e_normalize_path(Char c)
{
USE(c);
*LastChar = '\0'; /* just in case */
return(CC_NORMALIZE_PATH);
}
/*ARGSUSED*/
CCRETVAL
e_normalize_command(Char c)
{
USE(c);
*LastChar = '\0'; /* just in case */
return(CC_NORMALIZE_COMMAND);
}
/*ARGSUSED*/
CCRETVAL
e_expand_vars(Char c)
{
USE(c);
*LastChar = '\0'; /* just in case */
return(CC_EXPAND_VARS);
}
/*ARGSUSED*/
CCRETVAL
e_which(Char c)
{ /* do a fast command line which(1) */
USE(c);
c_save_inputbuf();
Hist_num = 0; /* for the history commands */
PastBottom();
*LastChar = '\0'; /* just in case */
return(CC_WHICH);
}
/*ARGSUSED*/
CCRETVAL
e_last_item(Char c)
{ /* insert the last element of the prev. cmd */
struct Hist *hp;
struct wordent *wp, *firstp;
int i;
Char *expanded;
USE(c);
if (Argument <= 0)
return(CC_ERROR);
hp = Histlist.Hnext;
if (hp == NULL) { /* this is only if no history */
return(CC_ERROR);
}
wp = (hp->Hlex).prev;
if (wp->prev == (struct wordent *) NULL)
return(CC_ERROR); /* an empty history entry */
firstp = (hp->Hlex).next;
/* back up arg words in lex */
for (i = 0; i < Argument && wp != firstp; i++) {
wp = wp->prev;
}
expanded = expand_lex(wp->prev, 0, i - 1);
if (InsertStr(expanded)) {
xfree(expanded);
return(CC_ERROR);
}
xfree(expanded);
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_dabbrev_expand(Char c)
{ /* expand to preceding word matching prefix */
Char *cp, *ncp, *bp;
struct Hist *hp;
int arg = 0, i;
size_t len = 0;
int found = 0;
Char *hbuf;
static int oldevent, hist, word;
static Char *start, *oldcursor;
USE(c);
if (Argument <= 0)
return(CC_ERROR);
cp = c_preword(Cursor, InputBuf, 1, STRshwordsep);
if (cp == Cursor || Isspace(*cp))
return(CC_ERROR);
hbuf = NULL;
hp = Histlist.Hnext;
bp = InputBuf;
if (Argument == 1 && eventno == oldevent && cp == start &&
Cursor == oldcursor && patbuf.len > 0
&& Strncmp(patbuf.s, cp, patbuf.len) == 0){
/* continue previous search - go to last match (hist/word) */
if (hist != 0) { /* need to move up history */
for (i = 1; i < hist && hp != NULL; i++)
hp = hp->Hnext;
if (hp == NULL) /* "can't happen" */
goto err_hbuf;
hbuf = expand_lex(&hp->Hlex, 0, INT_MAX);
cp = Strend(hbuf);
bp = hbuf;
hp = hp->Hnext;
}
cp = c_preword(cp, bp, word, STRshwordsep);
} else { /* starting new search */
oldevent = eventno;
start = cp;
patbuf.len = 0;
Strbuf_appendn(&patbuf, cp, Cursor - cp);
hist = 0;
word = 0;
}
while (!found) {
ncp = c_preword(cp, bp, 1, STRshwordsep);
if (ncp == cp || Isspace(*ncp)) { /* beginning of line */
hist++;
word = 0;
if (hp == NULL)
goto err_hbuf;
hbuf = expand_lex(&hp->Hlex, 0, INT_MAX);
cp = Strend(hbuf);
bp = hbuf;
hp = hp->Hnext;
continue;
} else {
word++;
len = c_endword(ncp-1, cp, 1, STRshwordsep) - ncp + 1;
cp = ncp;
}
if (len > patbuf.len && Strncmp(cp, patbuf.s, patbuf.len) == 0) {
/* We don't fully check distinct matches as Gnuemacs does: */
if (Argument > 1) { /* just count matches */
if (++arg >= Argument)
found++;
} else { /* match if distinct from previous */
if (len != (size_t)(Cursor - start)
|| Strncmp(cp, start, len) != 0)
found++;
}
}
}
if (LastChar + len - (Cursor - start) >= InputLim)
goto err_hbuf; /* no room */
DeleteBack(Cursor - start);
c_insert(len);
while (len--)
*Cursor++ = *cp++;
oldcursor = Cursor;
xfree(hbuf);
return(CC_REFRESH);
err_hbuf:
xfree(hbuf);
return CC_ERROR;
}
/*ARGSUSED*/
CCRETVAL
e_yank_kill(Char c)
{ /* almost like GnuEmacs */
int len;
Char *kp, *cp;
USE(c);
if (KillRingLen == 0) /* nothing killed */
return(CC_ERROR);
len = Strlen(KillRing[YankPos].buf);
if (LastChar + len >= InputLim)
return(CC_ERROR); /* end of buffer space */
/* else */
cp = Cursor; /* for speed */
c_insert(len); /* open the space, */
for (kp = KillRing[YankPos].buf; *kp; kp++) /* copy the chars */
*cp++ = *kp;
if (Argument == 1) { /* if no arg */
Mark = Cursor; /* mark at beginning, cursor at end */
Cursor = cp;
} else {
Mark = cp; /* else cursor at beginning, mark at end */
}
if (adrof(STRhighlight) && MarkIsSet) {
ClearLines();
ClearDisp();
}
MarkIsSet = 0;
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_yank_pop(Char c)
{ /* almost like GnuEmacs */
int m_bef_c, del_len, ins_len;
Char *kp, *cp;
USE(c);
#if 0
/* XXX This "should" be here, but doesn't work, since LastCmd
gets set on CC_ERROR and CC_ARGHACK, which it shouldn't(?).
(But what about F_ARGFOUR?) I.e. if you hit M-y twice the
second one will "succeed" even if the first one wasn't preceded
by a yank, and giving an argument is impossible. Now we "succeed"
regardless of previous command, which is wrong too of course. */
if (LastCmd != F_YANK_KILL && LastCmd != F_YANK_POP)
return(CC_ERROR);
#endif
if (KillRingLen == 0) /* nothing killed */
return(CC_ERROR);
YankPos -= Argument;
while (YankPos < 0)
YankPos += KillRingLen;
YankPos %= KillRingLen;
if (Cursor > Mark) {
del_len = Cursor - Mark;
m_bef_c = 1;
} else {
del_len = Mark - Cursor;
m_bef_c = 0;
}
ins_len = Strlen(KillRing[YankPos].buf);
if (LastChar + ins_len - del_len >= InputLim)
return(CC_ERROR); /* end of buffer space */
if (m_bef_c) {
c_delbefore(del_len);
} else {
c_delafter(del_len);
}
cp = Cursor; /* for speed */
c_insert(ins_len); /* open the space, */
for (kp = KillRing[YankPos].buf; *kp; kp++) /* copy the chars */
*cp++ = *kp;
if (m_bef_c) {
Mark = Cursor; /* mark at beginning, cursor at end */
Cursor = cp;
} else {
Mark = cp; /* else cursor at beginning, mark at end */
}
if (adrof(STRhighlight) && MarkIsSet) {
ClearLines();
ClearDisp();
}
MarkIsSet = 0;
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
v_delprev(Char c) /* Backspace key in insert mode */
{
int rc;
USE(c);
rc = CC_ERROR;
if (InsertPos != 0) {
if (Argument <= Cursor - InsertPos) {
c_delbefore(Argument); /* delete before */
rc = CC_REFRESH;
}
}
return(rc);
} /* v_delprev */
/*ARGSUSED*/
CCRETVAL
e_delprev(Char c)
{
USE(c);
if (Cursor > InputBuf) {
c_delbefore(Argument); /* delete before dot */
return(CC_REFRESH);
}
else {
return(CC_ERROR);
}
}
/*ARGSUSED*/
CCRETVAL
e_delwordprev(Char c)
{
Char *cp;
USE(c);
if (Cursor == InputBuf)
return(CC_ERROR);
/* else */
cp = c_prev_word(Cursor, InputBuf, Argument);
c_push_kill(cp, Cursor); /* save the text */
c_delbefore((int)(Cursor - cp)); /* delete before dot */
return(CC_REFRESH);
}
/* DCS <dcs@neutron.chem.yale.edu>, 9 Oct 93
*
* Changed the names of some of the ^D family of editor functions to
* correspond to what they actually do and created new e_delnext_list
* for completeness.
*
* Old names: New names:
*
* delete-char delete-char-or-eof
* F_DELNEXT F_DELNEXT_EOF
* e_delnext e_delnext_eof
* edelnxt edelnxteof
* delete-char-or-eof delete-char
* F_DELNEXT_EOF F_DELNEXT
* e_delnext_eof e_delnext
* edelnxteof edelnxt
* delete-char-or-list delete-char-or-list-or-eof
* F_LIST_DELNEXT F_DELNEXT_LIST_EOF
* e_list_delnext e_delnext_list_eof
* edellsteof
* (no old equivalent) delete-char-or-list
* F_DELNEXT_LIST
* e_delnext_list
* e_delnxtlst
*/
/* added by mtk@ari.ncl.omron.co.jp (920818) */
/* rename e_delnext() -> e_delnext_eof() */
/*ARGSUSED*/
CCRETVAL
e_delnext(Char c)
{
USE(c);
if (Cursor == LastChar) {/* if I'm at the end */
if (!VImode) {
return(CC_ERROR);
}
else {
if (Cursor != InputBuf)
Cursor--;
else
return(CC_ERROR);
}
}
c_delafter(Argument); /* delete after dot */
if (Cursor > LastChar)
Cursor = LastChar; /* bounds check */
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_delnext_eof(Char c)
{
USE(c);
if (Cursor == LastChar) {/* if I'm at the end */
if (!VImode) {
if (Cursor == InputBuf) {
/* if I'm also at the beginning */
so_write(STReof, 4);/* then do a EOF */
flush();
return(CC_EOF);
}
else
return(CC_ERROR);
}
else {
if (Cursor != InputBuf)
Cursor--;
else
return(CC_ERROR);
}
}
c_delafter(Argument); /* delete after dot */
if (Cursor > LastChar)
Cursor = LastChar; /* bounds check */
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_delnext_list(Char c)
{
USE(c);
if (Cursor == LastChar) { /* if I'm at the end */
PastBottom();
*LastChar = '\0'; /* just in case */
return(CC_LIST_CHOICES);
}
else {
c_delafter(Argument); /* delete after dot */
if (Cursor > LastChar)
Cursor = LastChar; /* bounds check */
return(CC_REFRESH);
}
}
/*ARGSUSED*/
CCRETVAL
e_delnext_list_eof(Char c)
{
USE(c);
if (Cursor == LastChar) { /* if I'm at the end */
if (Cursor == InputBuf) { /* if I'm also at the beginning */
so_write(STReof, 4);/* then do a EOF */
flush();
return(CC_EOF);
}
else {
PastBottom();
*LastChar = '\0'; /* just in case */
return(CC_LIST_CHOICES);
}
}
else {
c_delafter(Argument); /* delete after dot */
if (Cursor > LastChar)
Cursor = LastChar; /* bounds check */
return(CC_REFRESH);
}
}
/*ARGSUSED*/
CCRETVAL
e_list_eof(Char c)
{
CCRETVAL rv;
USE(c);
if (Cursor == LastChar && Cursor == InputBuf) {
so_write(STReof, 4); /* then do a EOF */
flush();
rv = CC_EOF;
}
else {
PastBottom();
*LastChar = '\0'; /* just in case */
rv = CC_LIST_CHOICES;
}
return rv;
}
/*ARGSUSED*/
CCRETVAL
e_delwordnext(Char c)
{
Char *cp;
USE(c);
if (Cursor == LastChar)
return(CC_ERROR);
/* else */
cp = c_next_word(Cursor, LastChar, Argument);
c_push_kill(Cursor, cp); /* save the text */
c_delafter((int)(cp - Cursor)); /* delete after dot */
if (Cursor > LastChar)
Cursor = LastChar; /* bounds check */
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_toend(Char c)
{
USE(c);
Cursor = LastChar;
if (VImode)
if (ActionFlag & TCSHOP_DELETE) {
c_delfini();
return(CC_REFRESH);
}
RefCursor(); /* move the cursor */
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_tobeg(Char c)
{
USE(c);
Cursor = InputBuf;
if (VImode) {
while (Isspace(*Cursor)) /* We want FIRST non space character */
Cursor++;
if (ActionFlag & TCSHOP_DELETE) {
c_delfini();
return(CC_REFRESH);
}
}
RefCursor(); /* move the cursor */
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_killend(Char c)
{
USE(c);
c_push_kill(Cursor, LastChar); /* copy it */
LastChar = Cursor; /* zap! -- delete to end */
if (Mark > Cursor)
Mark = Cursor;
MarkIsSet = 0;
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_killbeg(Char c)
{
USE(c);
c_push_kill(InputBuf, Cursor); /* copy it */
c_delbefore((int)(Cursor - InputBuf));
if (Mark && Mark > Cursor)
Mark -= Cursor-InputBuf;
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_killall(Char c)
{
USE(c);
c_push_kill(InputBuf, LastChar); /* copy it */
Cursor = Mark = LastChar = InputBuf; /* zap! -- delete all of it */
MarkIsSet = 0;
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_killregion(Char c)
{
USE(c);
if (!Mark)
return(CC_ERROR);
if (Mark > Cursor) {
c_push_kill(Cursor, Mark); /* copy it */
c_delafter((int)(Mark - Cursor)); /* delete it - UNUSED BY VI mode */
Mark = Cursor;
}
else { /* mark is before cursor */
c_push_kill(Mark, Cursor); /* copy it */
c_delbefore((int)(Cursor - Mark));
}
if (adrof(STRhighlight) && MarkIsSet) {
ClearLines();
ClearDisp();
}
MarkIsSet = 0;
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_copyregion(Char c)
{
USE(c);
if (!Mark)
return(CC_ERROR);
if (Mark > Cursor) {
c_push_kill(Cursor, Mark); /* copy it */
}
else { /* mark is before cursor */
c_push_kill(Mark, Cursor); /* copy it */
}
return(CC_NORM); /* don't even need to Refresh() */
}
/*ARGSUSED*/
CCRETVAL
e_charswitch(Char cc)
{
Char c;
USE(cc);
/* do nothing if we are at beginning of line or have only one char */
if (Cursor == &InputBuf[0] || LastChar == &InputBuf[1]) {
return(CC_ERROR);
}
if (Cursor < LastChar) {
Cursor++;
}
c = Cursor[-2];
Cursor[-2] = Cursor[-1];
Cursor[-1] = c;
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_gcharswitch(Char cc)
{ /* gosmacs style ^T */
Char c;
USE(cc);
if (Cursor > &InputBuf[1]) {/* must have at least two chars entered */
c = Cursor[-2];
Cursor[-2] = Cursor[-1];
Cursor[-1] = c;
return(CC_REFRESH);
}
else {
return(CC_ERROR);
}
}
/*ARGSUSED*/
CCRETVAL
e_charback(Char c)
{
USE(c);
if (Cursor > InputBuf) {
if (Argument > Cursor - InputBuf)
Cursor = InputBuf;
else
Cursor -= Argument;
if (VImode)
if (ActionFlag & TCSHOP_DELETE) {
c_delfini();
return(CC_REFRESH);
}
RefCursor();
return(CC_NORM);
}
else {
return(CC_ERROR);
}
}
/*ARGSUSED*/
CCRETVAL
v_wordback(Char c)
{
USE(c);
if (Cursor == InputBuf)
return(CC_ERROR);
/* else */
Cursor = c_preword(Cursor, InputBuf, Argument, STRshwspace); /* bounds check */
if (ActionFlag & TCSHOP_DELETE) {
c_delfini();
return(CC_REFRESH);
}
RefCursor();
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_wordback(Char c)
{
USE(c);
if (Cursor == InputBuf)
return(CC_ERROR);
/* else */
Cursor = c_prev_word(Cursor, InputBuf, Argument); /* bounds check */
if (VImode)
if (ActionFlag & TCSHOP_DELETE) {
c_delfini();
return(CC_REFRESH);
}
RefCursor();
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_charfwd(Char c)
{
USE(c);
if (Cursor < LastChar) {
Cursor += Argument;
if (Cursor > LastChar)
Cursor = LastChar;
if (VImode)
if (ActionFlag & TCSHOP_DELETE) {
c_delfini();
return(CC_REFRESH);
}
RefCursor();
return(CC_NORM);
}
else {
return(CC_ERROR);
}
}
/*ARGSUSED*/
CCRETVAL
e_wordfwd(Char c)
{
USE(c);
if (Cursor == LastChar)
return(CC_ERROR);
/* else */
Cursor = c_next_word(Cursor, LastChar, Argument);
if (VImode)
if (ActionFlag & TCSHOP_DELETE) {
c_delfini();
return(CC_REFRESH);
}
RefCursor();
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
v_wordfwd(Char c)
{
USE(c);
if (Cursor == LastChar)
return(CC_ERROR);
/* else */
Cursor = c_nexword(Cursor, LastChar, Argument);
if (VImode)
if (ActionFlag & TCSHOP_DELETE) {
c_delfini();
return(CC_REFRESH);
}
RefCursor();
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
v_wordbegnext(Char c)
{
USE(c);
if (Cursor == LastChar)
return(CC_ERROR);
/* else */
Cursor = c_next_word(Cursor, LastChar, Argument);
if (Cursor < LastChar)
Cursor++;
if (VImode)
if (ActionFlag & TCSHOP_DELETE) {
c_delfini();
return(CC_REFRESH);
}
RefCursor();
return(CC_NORM);
}
/*ARGSUSED*/
static CCRETVAL
v_repeat_srch(int c)
{
CCRETVAL rv = CC_ERROR;
#ifdef SDEBUG
xprintf("dir %d patlen %d patbuf %S\n",
c, (int)patbuf.len, patbuf.s);
#endif
LastCmd = (KEYCMD) c; /* Hack to stop c_hsetpat */
LastChar = InputBuf;
switch (c) {
case F_DOWN_SEARCH_HIST:
rv = e_down_search_hist(0);
break;
case F_UP_SEARCH_HIST:
rv = e_up_search_hist(0);
break;
default:
break;
}
return rv;
}
static CCRETVAL
v_csearch_back(Char ch, int count, int tflag)
{
Char *cp;
cp = Cursor;
while (count--) {
if (*cp == ch)
cp--;
while (cp > InputBuf && *cp != ch)
cp--;
}
if (cp < InputBuf || (cp == InputBuf && *cp != ch))
return(CC_ERROR);
if (*cp == ch && tflag)
cp++;
Cursor = cp;
if (ActionFlag & TCSHOP_DELETE) {
Cursor++;
c_delfini();
return(CC_REFRESH);
}
RefCursor();
return(CC_NORM);
}
static CCRETVAL
v_csearch_fwd(Char ch, int count, int tflag)
{
Char *cp;
cp = Cursor;
while (count--) {
if(*cp == ch)
cp++;
while (cp < LastChar && *cp != ch)
cp++;
}
if (cp >= LastChar)
return(CC_ERROR);
if (*cp == ch && tflag)
cp--;
Cursor = cp;
if (ActionFlag & TCSHOP_DELETE) {
Cursor++;
c_delfini();
return(CC_REFRESH);
}
RefCursor();
return(CC_NORM);
}
/*ARGSUSED*/
static CCRETVAL
v_action(int c)
{
Char *cp, *kp;
if (ActionFlag == TCSHOP_DELETE) {
ActionFlag = TCSHOP_NOP;
ActionPos = 0;
UndoSize = 0;
kp = UndoBuf;
for (cp = InputBuf; cp < LastChar; cp++) {
*kp++ = *cp;
UndoSize++;
}
UndoAction = TCSHOP_INSERT;
UndoPtr = InputBuf;
LastChar = InputBuf;
Cursor = InputBuf;
if (c & TCSHOP_INSERT)
c_alternativ_key_map(0);
return(CC_REFRESH);
}
#ifdef notdef
else if (ActionFlag == TCSHOP_NOP) {
#endif
ActionPos = Cursor;
ActionFlag = c;
return(CC_ARGHACK); /* Do NOT clear out argument */
#ifdef notdef
}
else {
ActionFlag = 0;
ActionPos = 0;
return(CC_ERROR);
}
#endif
}
#ifdef COMMENT
/* by: Brian Allison <uiucdcs!convex!allison@RUTGERS.EDU> */
static void
c_get_word(Char **begin, Char **end)
{
Char *cp;
cp = &Cursor[0];
while (Argument--) {
while ((cp <= LastChar) && (isword(*cp)))
cp++;
*end = --cp;
while ((cp >= InputBuf) && (isword(*cp)))
cp--;
*begin = ++cp;
}
}
#endif /* COMMENT */
/*ARGSUSED*/
CCRETVAL
e_uppercase(Char c)
{
Char *cp, *end;
USE(c);
end = c_next_word(Cursor, LastChar, Argument);
for (cp = Cursor; cp < end; cp++) /* PWP: was cp=begin */
if (Islower(*cp))
*cp = Toupper(*cp);
Cursor = end;
if (Cursor > LastChar)
Cursor = LastChar;
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_capitalcase(Char c)
{
Char *cp, *end;
USE(c);
end = c_next_word(Cursor, LastChar, Argument);
cp = Cursor;
for (; cp < end; cp++) {
if (Isalpha(*cp)) {
if (Islower(*cp))
*cp = Toupper(*cp);
cp++;
break;
}
}
for (; cp < end; cp++)
if (Isupper(*cp))
*cp = Tolower(*cp);
Cursor = end;
if (Cursor > LastChar)
Cursor = LastChar;
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_lowercase(Char c)
{
Char *cp, *end;
USE(c);
end = c_next_word(Cursor, LastChar, Argument);
for (cp = Cursor; cp < end; cp++)
if (Isupper(*cp))
*cp = Tolower(*cp);
Cursor = end;
if (Cursor > LastChar)
Cursor = LastChar;
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_set_mark(Char c)
{
USE(c);
if (adrof(STRhighlight) && MarkIsSet && Mark != Cursor) {
ClearLines();
ClearDisp();
Refresh();
}
Mark = Cursor;
MarkIsSet = 1;
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_exchange_mark(Char c)
{
Char *cp;
USE(c);
cp = Cursor;
Cursor = Mark;
Mark = cp;
RefCursor();
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_argfour(Char c)
{ /* multiply current argument by 4 */
USE(c);
if (Argument > 1000000)
return CC_ERROR;
DoingArg = 1;
Argument *= 4;
return(CC_ARGHACK);
}
static void
quote_mode_cleanup(void *unused)
{
USE(unused);
QuoteModeOff();
}
/*ARGSUSED*/
CCRETVAL
e_quote(Char c)
{
Char ch;
int num;
USE(c);
QuoteModeOn();
cleanup_push(&c, quote_mode_cleanup); /* Using &c just as a mark */
num = GetNextChar(&ch);
cleanup_until(&c);
if (num == 1)
return e_insert(ch);
else
return e_send_eof(0);
}
/*ARGSUSED*/
CCRETVAL
e_metanext(Char c)
{
USE(c);
MetaNext = 1;
return(CC_ARGHACK); /* preserve argument */
}
#ifdef notdef
/*ARGSUSED*/
CCRETVAL
e_extendnext(Char c)
{
CurrentKeyMap = CcAltMap;
return(CC_ARGHACK); /* preserve argument */
}
#endif
/*ARGSUSED*/
CCRETVAL
v_insbeg(Char c)
{ /* move to beginning of line and start vi
* insert mode */
USE(c);
Cursor = InputBuf;
InsertPos = Cursor;
UndoPtr = Cursor;
UndoAction = TCSHOP_DELETE;
RefCursor(); /* move the cursor */
c_alternativ_key_map(0);
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
v_replone(Char c)
{ /* vi mode overwrite one character */
USE(c);
c_alternativ_key_map(0);
inputmode = MODE_REPLACE_1;
UndoAction = TCSHOP_CHANGE; /* Set Up for VI undo command */
UndoPtr = Cursor;
UndoSize = 0;
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
v_replmode(Char c)
{ /* vi mode start overwriting */
USE(c);
c_alternativ_key_map(0);
inputmode = MODE_REPLACE;
UndoAction = TCSHOP_CHANGE; /* Set Up for VI undo command */
UndoPtr = Cursor;
UndoSize = 0;
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
v_substchar(Char c)
{ /* vi mode substitute for one char */
USE(c);
c_delafter(Argument);
c_alternativ_key_map(0);
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
v_substline(Char c)
{ /* vi mode replace whole line */
USE(c);
(void) e_killall(0);
c_alternativ_key_map(0);
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
v_chgtoend(Char c)
{ /* vi mode change to end of line */
USE(c);
(void) e_killend(0);
c_alternativ_key_map(0);
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
v_insert(Char c)
{ /* vi mode start inserting */
USE(c);
c_alternativ_key_map(0);
InsertPos = Cursor;
UndoPtr = Cursor;
UndoAction = TCSHOP_DELETE;
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
v_add(Char c)
{ /* vi mode start adding */
USE(c);
c_alternativ_key_map(0);
if (Cursor < LastChar)
{
Cursor++;
if (Cursor > LastChar)
Cursor = LastChar;
RefCursor();
}
InsertPos = Cursor;
UndoPtr = Cursor;
UndoAction = TCSHOP_DELETE;
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
v_addend(Char c)
{ /* vi mode to add at end of line */
USE(c);
c_alternativ_key_map(0);
Cursor = LastChar;
InsertPos = LastChar; /* Mark where insertion begins */
UndoPtr = LastChar;
UndoAction = TCSHOP_DELETE;
RefCursor();
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
v_change_case(Char cc)
{
Char c;
USE(cc);
if (Cursor < LastChar) {
#ifndef WINNT_NATIVE
c = *Cursor;
#else
c = CHAR & *Cursor;
#endif /* WINNT_NATIVE */
if (Isupper(c))
*Cursor++ = Tolower(c);
else if (Islower(c))
*Cursor++ = Toupper(c);
else
Cursor++;
RefPlusOne(1); /* fast refresh for one char */
return(CC_NORM);
}
return(CC_ERROR);
}
/*ARGSUSED*/
CCRETVAL
e_expand(Char c)
{
Char *p;
USE(c);
for (p = InputBuf; Isspace(*p); p++)
continue;
if (p == LastChar)
return(CC_ERROR);
justpr++;
Expand++;
return(e_newline(0));
}
/*ARGSUSED*/
CCRETVAL
e_startover(Char c)
{ /* erase all of current line, start again */
USE(c);
ResetInLine(0); /* reset the input pointers */
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_redisp(Char c)
{
USE(c);
ClearLines();
ClearDisp();
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_cleardisp(Char c)
{
USE(c);
ClearScreen(); /* clear the whole real screen */
ClearDisp(); /* reset everything */
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_tty_int(Char c)
{
USE(c);
#if defined(_MINIX) || defined(WINNT_NATIVE)
/* SAK PATCH: erase all of current line, start again */
ResetInLine(0); /* reset the input pointers */
xputchar('\n');
ClearDisp();
return (CC_REFRESH);
#else /* !_MINIX && !WINNT_NATIVE */
/* do no editing */
return (CC_NORM);
#endif /* _MINIX || WINNT_NATIVE */
}
/*
* From: ghazi@cesl.rutgers.edu (Kaveh R. Ghazi)
* Function to send a character back to the input stream in cooked
* mode. Only works if we have TIOCSTI
*/
/*ARGSUSED*/
CCRETVAL
e_stuff_char(Char c)
{
#ifdef TIOCSTI
int was_raw = Tty_raw_mode;
char buf[MB_LEN_MAX];
size_t i, len;
if (was_raw)
(void) Cookedmode();
(void) xwrite(SHIN, "\n", 1);
len = one_wctomb(buf, c);
for (i = 0; i < len; i++)
(void) ioctl(SHIN, TIOCSTI, (ioctl_t) &buf[i]);
if (was_raw)
(void) Rawmode();
return(e_redisp(c));
#else /* !TIOCSTI */
return(CC_ERROR);
#endif /* !TIOCSTI */
}
/*ARGSUSED*/
CCRETVAL
e_insovr(Char c)
{
USE(c);
inputmode = (inputmode == MODE_INSERT ? MODE_REPLACE : MODE_INSERT);
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_tty_dsusp(Char c)
{
USE(c);
/* do no editing */
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_tty_flusho(Char c)
{
USE(c);
/* do no editing */
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_tty_quit(Char c)
{
USE(c);
/* do no editing */
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_tty_tsusp(Char c)
{
USE(c);
/* do no editing */
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_tty_stopo(Char c)
{
USE(c);
/* do no editing */
return(CC_NORM);
}
/* returns the number of (attempted) expansions */
int
ExpandHistory(void)
{
*LastChar = '\0'; /* just in case */
return c_substitute();
}
/*ARGSUSED*/
CCRETVAL
e_expand_history(Char c)
{
USE(c);
(void)ExpandHistory();
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_magic_space(Char c)
{
USE(c);
*LastChar = '\0'; /* just in case */
(void)c_substitute();
return(e_insert(' '));
}
/*ARGSUSED*/
CCRETVAL
e_inc_fwd(Char c)
{
CCRETVAL ret;
USE(c);
patbuf.len = 0;
MarkIsSet = 0;
ret = e_inc_search(F_DOWN_SEARCH_HIST);
if (adrof(STRhighlight) && IncMatchLen) {
IncMatchLen = 0;
ClearLines();
ClearDisp();
Refresh();
}
IncMatchLen = 0;
return ret;
}
/*ARGSUSED*/
CCRETVAL
e_inc_back(Char c)
{
CCRETVAL ret;
USE(c);
patbuf.len = 0;
MarkIsSet = 0;
ret = e_inc_search(F_UP_SEARCH_HIST);
if (adrof(STRhighlight) && IncMatchLen) {
IncMatchLen = 0;
ClearLines();
ClearDisp();
Refresh();
}
IncMatchLen = 0;
return ret;
}
/*ARGSUSED*/
CCRETVAL
e_copyprev(Char c)
{
Char *cp, *oldc, *dp;
USE(c);
if (Cursor == InputBuf)
return(CC_ERROR);
/* else */
oldc = Cursor;
/* does a bounds check */
cp = c_prev_word(Cursor, InputBuf, Argument);
c_insert((int)(oldc - cp));
for (dp = oldc; cp < oldc && dp < LastChar; cp++)
*dp++ = *cp;
Cursor = dp; /* put cursor at end */
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
e_tty_starto(Char c)
{
USE(c);
/* do no editing */
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
e_load_average(Char c)
{
USE(c);
PastBottom();
#ifdef TIOCSTAT
/*
* Here we pass &c to the ioctl because some os's (NetBSD) expect it
* there even if they don't use it. (lukem@netbsd.org)
*/
if (ioctl(SHIN, TIOCSTAT, (ioctl_t) &c) < 0)
#endif
xprintf("%s", CGETS(5, 1, "Load average unavailable\n"));
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
v_chgmeta(Char c)
{
USE(c);
/*
* Delete with insert == change: first we delete and then we leave in
* insert mode.
*/
return(v_action(TCSHOP_DELETE|TCSHOP_INSERT));
}
/*ARGSUSED*/
CCRETVAL
v_delmeta(Char c)
{
USE(c);
return(v_action(TCSHOP_DELETE));
}
/*ARGSUSED*/
CCRETVAL
v_endword(Char c)
{
USE(c);
if (Cursor == LastChar)
return(CC_ERROR);
/* else */
Cursor = c_endword(Cursor, LastChar, Argument, STRshwspace);
if (ActionFlag & TCSHOP_DELETE)
{
Cursor++;
c_delfini();
return(CC_REFRESH);
}
RefCursor();
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
v_eword(Char c)
{
USE(c);
if (Cursor == LastChar)
return(CC_ERROR);
/* else */
Cursor = c_eword(Cursor, LastChar, Argument);
if (ActionFlag & TCSHOP_DELETE) {
Cursor++;
c_delfini();
return(CC_REFRESH);
}
RefCursor();
return(CC_NORM);
}
/*ARGSUSED*/
CCRETVAL
v_char_fwd(Char c)
{
Char ch;
USE(c);
if (GetNextChar(&ch) != 1)
return e_send_eof(0);
srch_dir = CHAR_FWD;
srch_char = ch;
return v_csearch_fwd(ch, Argument, 0);
}
/*ARGSUSED*/
CCRETVAL
v_char_back(Char c)
{
Char ch;
USE(c);
if (GetNextChar(&ch) != 1)
return e_send_eof(0);
srch_dir = CHAR_BACK;
srch_char = ch;
return v_csearch_back(ch, Argument, 0);
}
/*ARGSUSED*/
CCRETVAL
v_charto_fwd(Char c)
{
Char ch;
USE(c);
if (GetNextChar(&ch) != 1)
return e_send_eof(0);
return v_csearch_fwd(ch, Argument, 1);
}
/*ARGSUSED*/
CCRETVAL
v_charto_back(Char c)
{
Char ch;
USE(c);
if (GetNextChar(&ch) != 1)
return e_send_eof(0);
return v_csearch_back(ch, Argument, 1);
}
/*ARGSUSED*/
CCRETVAL
v_rchar_fwd(Char c)
{
USE(c);
if (srch_char == 0)
return CC_ERROR;
return srch_dir == CHAR_FWD ? v_csearch_fwd(srch_char, Argument, 0) :
v_csearch_back(srch_char, Argument, 0);
}
/*ARGSUSED*/
CCRETVAL
v_rchar_back(Char c)
{
USE(c);
if (srch_char == 0)
return CC_ERROR;
return srch_dir == CHAR_BACK ? v_csearch_fwd(srch_char, Argument, 0) :
v_csearch_back(srch_char, Argument, 0);
}
/*ARGSUSED*/
CCRETVAL
v_undo(Char c)
{
int loop;
Char *kp, *cp;
Char temp;
int size;
USE(c);
switch (UndoAction) {
case TCSHOP_DELETE|TCSHOP_INSERT:
case TCSHOP_DELETE:
if (UndoSize == 0) return(CC_NORM);
cp = UndoPtr;
kp = UndoBuf;
for (loop=0; loop < UndoSize; loop++) /* copy the chars */
*kp++ = *cp++; /* into UndoBuf */
for (cp = UndoPtr; cp <= LastChar; cp++)
*cp = cp[UndoSize];
LastChar -= UndoSize;
Cursor = UndoPtr;
UndoAction = TCSHOP_INSERT;
break;
case TCSHOP_INSERT:
if (UndoSize == 0) return(CC_NORM);
cp = UndoPtr;
Cursor = UndoPtr;
kp = UndoBuf;
c_insert(UndoSize); /* open the space, */
for (loop = 0; loop < UndoSize; loop++) /* copy the chars */
*cp++ = *kp++;
UndoAction = TCSHOP_DELETE;
break;
case TCSHOP_CHANGE:
if (UndoSize == 0) return(CC_NORM);
cp = UndoPtr;
Cursor = UndoPtr;
kp = UndoBuf;
size = (int)(Cursor-LastChar); /* NOT NSL independant */
if (size < UndoSize)
size = UndoSize;
for(loop = 0; loop < size; loop++) {
temp = *kp;
*kp++ = *cp;
*cp++ = temp;
}
break;
default:
return(CC_ERROR);
}
return(CC_REFRESH);
}
/*ARGSUSED*/
CCRETVAL
v_ush_meta(Char c)
{
USE(c);
return v_search(F_UP_SEARCH_HIST);
}
/*ARGSUSED*/
CCRETVAL
v_dsh_meta(Char c)
{
USE(c);
return v_search(F_DOWN_SEARCH_HIST);
}
/*ARGSUSED*/
CCRETVAL
v_rsrch_fwd(Char c)
{
USE(c);
if (patbuf.len == 0) return(CC_ERROR);
return(v_repeat_srch(searchdir));
}
/*ARGSUSED*/
CCRETVAL
v_rsrch_back(Char c)
{
USE(c);
if (patbuf.len == 0) return(CC_ERROR);
return(v_repeat_srch(searchdir == F_UP_SEARCH_HIST ?
F_DOWN_SEARCH_HIST : F_UP_SEARCH_HIST));
}
#ifndef WINNT_NATIVE
/* Since ed.defns.h is generated from ed.defns.c, these empty
functions will keep the F_NUM_FNS consistent
*/
CCRETVAL
e_copy_to_clipboard(Char c)
{
USE(c);
return CC_ERROR;
}
CCRETVAL
e_paste_from_clipboard(Char c)
{
USE(c);
return (CC_ERROR);
}
CCRETVAL
e_dosify_next(Char c)
{
USE(c);
return (CC_ERROR);
}
CCRETVAL
e_dosify_prev(Char c)
{
USE(c);
return (CC_ERROR);
}
CCRETVAL
e_page_up(Char c)
{
USE(c);
return (CC_ERROR);
}
CCRETVAL
e_page_down(Char c)
{
USE(c);
return (CC_ERROR);
}
#endif /* !WINNT_NATIVE */
#ifdef notdef
void
MoveCursor(int n) /* move cursor + right - left char */
{
Cursor = Cursor + n;
if (Cursor < InputBuf)
Cursor = InputBuf;
if (Cursor > LastChar)
Cursor = LastChar;
return;
}
Char *
GetCursor(void)
{
return(Cursor);
}
int
PutCursor(Char *p)
{
if (p < InputBuf || p > LastChar)
return 1; /* Error */
Cursor = p;
return 0;
}
#endif