2.11BSD/src/bin/tcsh/ed.chared.c
/* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/ed.chared.c,v 3.0 1991/07/04 21:49:28 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. 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.
*/
#include "config.h"
#if !defined(lint) && !defined(pdp11)
static char *rcsid()
{ return "$Id: ed.chared.c,v 3.0 1991/07/04 21:49:28 christos Exp $"; }
#endif
#include "sh.h"
#include "ed.h"
#include "tw.h"
#include "ed.defns.h"
/* all routines that start with c_ are private to this set of routines */
static void c_alternativ_key_map __P((int));
static void c_insert __P((int));
static void c_delafter __P((int));
static void c_delbefore __P((int));
static Char *c_prev_word __P((Char *, Char *, int));
static Char *c_next_word __P((Char *, Char *, int));
static Char *c_beg_next_word __P((Char *, Char *, int));
static void c_copy __P((Char *, Char *, int));
static Char *c_number __P((Char *, int *, int));
static Char *c_expand __P((Char *));
static void c_excl __P((Char *));
static void c_substitute __P((void));
static int c_hmatch __P((Char *));
static void c_hsetpat __P((void));
#ifdef COMMENT
static void c_get_word __P((Char **, Char **));
#endif
static void
c_alternativ_key_map(state)
int state;
{
AltKeyMap = state;
if (state)
Cur_KeyMap = CcAltMap;
else
Cur_KeyMap = CcKeyMap;
}
static void
c_insert(num)
register int num;
{
register 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;
}
LastChar += num;
}
static void
c_delafter(num) /* delete after dot, with bounds checking */
register int num;
{
register Char *cp;
if (Cursor + num > LastChar)
num = LastChar - Cursor;/* bounds check */
if (num > 0) { /* if I can delete anything */
for (cp = Cursor; cp <= LastChar; cp++)
*cp = cp[num];
LastChar -= num;
}
else
replacemode = 0;
}
static void
c_delbefore(num) /* delete before dot, with bounds checking */
register int num;
{
register Char *cp;
if (Cursor - num < InputBuf)
num = Cursor - InputBuf;/* bounds check */
if (num > 0) { /* if I can delete anything */
for (cp = Cursor - num; cp <= LastChar; cp++)
*cp = cp[num];
LastChar -= num;
}
}
static Char *
c_prev_word(p, low, n)
register Char *p, *low;
register int n;
{
/* to the beginning of the PREVIOUS word, not this one */
p--;
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);
}
static Char *
c_next_word(p, high, n)
register Char *p, *high;
register int n;
{
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);
}
static Char *
c_beg_next_word(p, high, n)
register Char *p, *high;
register int n;
{
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);
}
/*
* Expand-History (originally "Magic-Space") code added by
* Ray Moody <ray@gibbs.physics.purdue.edu>
* this is a neat, but odd, addition.
*/
/*
* c_copy is sorta like bcopy() except that we handle overlap between
* source and destination memory
*/
static void
c_copy(src, dst, length)
register Char *src, *dst;
register int length;
{
if (src > dst) {
while (length--) {
*dst++ = *src++;
}
}
else {
src += length;
dst += length;
while (length--) {
*--dst = *--src;
}
}
}
/*
* 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(p, num, dval)
register Char *p;
register int *num;
register int dval;
{
register int i;
register int sign = 1;
if (*++p == '^') {
*num = 1;
return (p);
}
if (*p == '$') {
if (*++p != '-') {
*num = NCARGS; /* Handle $ */
return (--p);
}
sign = -1; /* Handle $- */
++p;
}
for (i = 0; *p >= '0' && *p <= '9'; i = 10 * i + *p++ - '0');
*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(p)
register Char *p;
{
register Char *q;
register struct Hist *h = Histlist.Hnext;
register struct wordent *l;
int i, from, to, dval;
bool all_dig;
bool been_once = 0;
Char *op = p;
Char buf[INBUFSIZ];
Char *bend = buf;
Char *modbuf, *omodbuf;
if (!h)
goto excl_err;
excl_sw:
switch (*(q = p + 1)) {
case '^':
bend = expand_lex(buf, INBUFSIZ, &h->Hlex, 1, 1);
break;
case '$':
if ((l = (h->Hlex).prev))
bend = expand_lex(buf, INBUFSIZ, l->prev->prev, 0, 0);
break;
case '*':
bend = expand_lex(buf, INBUFSIZ, &h->Hlex, 1, NCARGS);
break;
default:
if (been_once) { /* unknown argument */
/* assume it's a modifier, e.g. !foo:h, and get whole cmd */
bend = expand_lex(buf, INBUFSIZ, &h->Hlex, 0, NCARGS);
q -= 2;
break;
}
been_once = 1;
if (*q == ':') /* short form: !:arg */
--q;
if (*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 = q - p; h; h = h->Hnext) {
if ((l = &h->Hlex)) {
if (!Strncmp(p + 1, l->next->word, 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)) {
for (l = l->prev; l != h->Hlex.next; l = l->prev, dval++);
}
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 = NCARGS;
}
else {
to = from;
}
if (from < 0 || to < from)
goto excl_err;
bend = expand_lex(buf, INBUFSIZ, &h->Hlex, from, to);
}
else { /* get whole cmd */
bend = expand_lex(buf, INBUFSIZ, &h->Hlex, 0, NCARGS);
}
break;
}
/*
* Apply modifiers, if any.
*/
if (q[1] == ':') {
*bend = '\0';
omodbuf = buf;
while (q[1] == ':' && (modbuf = domod(omodbuf, (int) q[2])) != NOSTR) {
if (omodbuf != buf)
xfree((ptr_t) omodbuf);
omodbuf = modbuf;
q += 2;
}
if (omodbuf != buf) {
(void) Strcpy(buf, omodbuf);
xfree((ptr_t) omodbuf);
bend = Strend(buf);
}
}
/*
* Now replace the text from op to q inclusive with the text from buf to
* bend.
*/
q++;
/*
* Now replace text non-inclusively like a real CS major!
*/
if (LastChar + (bend - buf) - (q - op) >= InputLim)
goto excl_err;
c_copy(q, q + (bend - buf) - (q - op), LastChar - q);
LastChar += (bend - buf) - (q - op);
Cursor += (bend - buf) - (q - op);
c_copy(buf, op, (bend - buf));
return (op + (bend - buf));
excl_err:
Beep();
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 '>!'
*/
static void
c_excl(p)
register Char *p;
{
register int i;
register Char *q;
/*
* 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);
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. Of odd, skip history char. Return if all done.
* Expand if even number of backslashes.
*/
for (;;) {
while (*p != HIST && p < Cursor)
++p;
for (i = 1; (p - i) >= InputBuf && p[-i] == '\\'; i++);
if (i % 2 == 0)
++p;
if (p >= Cursor)
return;
if (i % 2 == 1)
p = c_expand(p);
}
}
static void
c_substitute()
{
register Char *p;
/*
* 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 != HIST; --p);
/*
* If we found a history character, go expand it.
*/
if (*p == HIST)
c_excl(p);
Refresh();
}
/*
* demi-PUBLIC routines. Any routine that is of type CCRETVAL is an
* entry point, called from the CcKeyMap indirected into the
* CcFuncTbl array.
*/
/*VARARGS*/
CCRETVAL
v_cmd_mode()
{
replacemode = 0;
c_alternativ_key_map(1);
if (Cursor > InputBuf)
Cursor--;
RefCursor();
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_unassigned()
{ /* bound to keys that arn't really assigned */
Beep();
flush();
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_insert(c)
register 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) { /* optimize */
if (replacemode == 1)
c_delafter(1);
else if (replacemode == 2)
c_delafter(1);
c_insert(1);
*Cursor++ = c;
DoingArg = 0; /* just in case */
RefPlusOne(); /* fast refresh for one char. */
if (replacemode == 2)
(void) v_cmd_mode();
}
else {
if (replacemode == 1)
c_delafter(Argument);
else if (replacemode == 2)
c_delafter(Argument);
c_insert(Argument);
while (Argument--)
*Cursor++ = c;
Refresh();
if (replacemode == 2)
(void) v_cmd_mode();
}
return (CC_NORM);
}
int
InsertStr(s) /* insert ASCIZ s at cursor (for complete) */
Char *s;
{
register int len;
if ((len = 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(n) /* delete the n characters before . */
int n;
{
if (n <= 0)
return;
if (Cursor >= &InputBuf[n]) {
c_delbefore(n); /* delete before dot */
Cursor -= n;
if (Cursor < InputBuf)
Cursor = InputBuf; /* bounds check */
}
}
/*VARARGS*/
CCRETVAL
e_digit(c) /* gray magic here */
register Char c;
{
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 */
c_insert(1);
*Cursor++ = c;
DoingArg = 0; /* just in case */
RefPlusOne(); /* fast refresh for one char. */
}
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_argdigit(c) /* for ESC-n */
register Char c;
{
c &= ASCII;
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);
}
/*VARARGS*/
CCRETVAL
v_zero(c) /* command mode 0 for vi */
register Char c;
{
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;
RefCursor(); /* move the cursor */
return (CC_NORM);
}
}
/*VARARGS*/
CCRETVAL
e_newline()
{ /* always ignore argument */
PastBottom();
*LastChar++ = '\n'; /* for the benifit of CSH */
*LastChar = '\0'; /* just in case */
return (CC_NEWLINE); /* we must do a ResetInLine later */
}
/*VARARGS*/
CCRETVAL
e_send_eof()
{ /* for when ^D is ONLY send-eof */
PastBottom();
*LastChar = '\0'; /* just in case */
#ifdef notdef
ResetInLine(); /* reset the input pointers */
#endif
return (CC_EOF);
}
/*VARARGS*/
CCRETVAL
e_complete()
{
*LastChar = '\0'; /* just in case */
return (CC_COMPLETE);
}
/*VARARGS*/
CCRETVAL
v_cm_complete()
{
if (Cursor < LastChar)
Cursor++;
*LastChar = '\0'; /* just in case */
return (CC_COMPLETE);
}
/*VARARGS*/
CCRETVAL
e_toggle_hist()
{
struct Hist *hp;
int h;
*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 (!Cur_HistLit) {
if (hp->histline) {
copyn(InputBuf, hp->histline, INBUFSIZ);
Cur_HistLit = 1;
}
else {
return CC_ERROR;
}
}
else {
(void) sprlex(InputBuf, &hp->Hlex);
Cur_HistLit = 0;
}
LastChar = InputBuf + Strlen(InputBuf);
if (LastChar > InputBuf) {
if (LastChar[-1] == '\n')
LastChar--;
if (LastChar[-1] == ' ')
LastChar--;
if (LastChar < InputBuf)
LastChar = InputBuf;
}
Cursor = LastChar;
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_up_hist()
{
struct Hist *hp;
int hnumcntr;
Char beep = 0;
*LastChar = '\0'; /* just in case */
if (Hist_num == 0) { /* save the current buffer away */
copyn(HistBuf, InputBuf, INBUFSIZ);
LastHist = HistBuf + (LastChar - InputBuf);
}
hp = Histlist.Hnext;
if (hp == NULL) { /* this is only if no history */
return (CC_ERROR);
}
Hist_num += Argument;
for (hnumcntr = 1; hnumcntr < Hist_num; hnumcntr++) {
if ((hp->Hnext) == NULL) {
Hist_num = hnumcntr;
beep = 1;
break;
}
hp = hp->Hnext;
}
if (HistLit && hp->histline) {
copyn(InputBuf, hp->histline, INBUFSIZ);
Cur_HistLit = 1;
}
else {
(void) sprlex(InputBuf, &hp->Hlex);
Cur_HistLit = 0;
}
LastChar = InputBuf + Strlen(InputBuf);
if (LastChar > InputBuf) {
if (LastChar[-1] == '\n')
LastChar--;
if (LastChar[-1] == ' ')
LastChar--;
if (LastChar < InputBuf)
LastChar = InputBuf;
}
Cursor = LastChar;
Refresh();
if (beep)
return (CC_ERROR);
else
return (CC_NORM); /* was CC_UP_HIST */
}
/*VARARGS*/
CCRETVAL
e_d_hist()
{
struct Hist *hp;
int hnumcntr;
*LastChar = '\0'; /* just in case */
Hist_num -= Argument;
if (Hist_num < 0) {
Hist_num = 0;
return (CC_ERROR); /* make it beep */
}
if (Hist_num == 0) { /* if really the current line */
copyn(InputBuf, HistBuf, INBUFSIZ);
LastChar = InputBuf + (LastHist - HistBuf);
Cursor = LastChar;
return (CC_REFRESH);
}
hp = Histlist.Hnext;
if (hp == NULL)
return (CC_ERROR);
for (hnumcntr = 1; hnumcntr < Hist_num; hnumcntr++) {
if ((hp->Hnext) == NULL) {
Hist_num = hnumcntr;
return (CC_ERROR);
}
hp = hp->Hnext;
}
if (HistLit && hp->histline) {
copyn(InputBuf, hp->histline, INBUFSIZ);
Cur_HistLit = 1;
}
else {
(void) sprlex(InputBuf, &hp->Hlex);
Cur_HistLit = 0;
}
LastChar = InputBuf + Strlen(InputBuf);
if (LastChar > InputBuf) {
if (LastChar[-1] == '\n')
LastChar--;
if (LastChar[-1] == ' ')
LastChar--;
if (LastChar < InputBuf)
LastChar = InputBuf;
}
Cursor = LastChar;
return (CC_REFRESH);
}
static Char patbuf[INBUFSIZ];
static int patlen = 0;
/*
* c_hmatch() return True if the pattern matches the prefix
*/
static int
c_hmatch(str)
Char *str;
{
if (Strncmp(patbuf, str, patlen) == 0)
return 1;
return Gmatch(str, patbuf);
}
/*
* c_hsetpat(): Set the history seatch pattern
*/
static void
c_hsetpat()
{
if (LastCmd != F_UP_SEARCH_HIST && LastCmd != F_DOWN_SEARCH_HIST) {
patlen = Cursor - InputBuf;
if (patlen >= INBUFSIZ) patlen = INBUFSIZ -1;
(void) Strncpy(patbuf, InputBuf, patlen);
patbuf[patlen] = '\0';
}
#ifdef SDEBUG
xprintf("\nHist_num = %d\n", Hist_num);
xprintf("patlen = %d\n", patlen);
xprintf("patbuf = \"%s\"\n", short2str(patbuf));
#endif
}
/*VARARGS*/
CCRETVAL
e_up_search_hist()
{
struct Hist *hp;
int h;
bool found = 0;
*LastChar = '\0'; /* just in case */
if (Hist_num < 0) {
xprintf("tcsh: e_up_search_hist(): Hist_num < 0; resetting.\n");
Hist_num = 0;
return (CC_ERROR);
}
if (Hist_num == 0) {
copyn(HistBuf, InputBuf, INBUFSIZ);
LastHist = HistBuf + (LastChar - InputBuf);
}
hp = Histlist.Hnext;
if (hp == NULL)
return (CC_ERROR);
c_hsetpat();
for (h = 1; h <= Hist_num; h++)
hp = hp->Hnext;
while (hp != NULL) {
if (hp->histline == NULL) {
Char sbuf[BUFSIZ];
hp->histline = Strsave(sprlex(sbuf, &hp->Hlex));
}
#ifdef SDEBUG
xprintf("Comparing with \"%s\"\n", short2str(hp->histline));
#endif
if (c_hmatch(hp->histline)) {
found++;
break;
}
h++;
hp = hp->Hnext;
}
if (!found)
return (CC_ERROR);
Hist_num = h;
if (HistLit && hp->histline) {
copyn(InputBuf, hp->histline, INBUFSIZ);
Cur_HistLit = 1;
}
else {
(void) sprlex(InputBuf, &hp->Hlex);
Cur_HistLit = 0;
}
LastChar = InputBuf + Strlen(InputBuf);
if (LastChar > InputBuf) {
if (LastChar[-1] == '\n')
LastChar--;
if (LastChar[-1] == ' ')
LastChar--;
if (LastChar < InputBuf)
LastChar = InputBuf;
}
Cursor = LastChar;
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_d_search_hist()
{
struct Hist *hp, *hpt = NULL;
int h;
bool found = 0;
*LastChar = '\0'; /* just in case */
if (Hist_num == 0)
return (CC_ERROR);
hp = Histlist.Hnext;
if (hp == 0)
return (CC_ERROR);
c_hsetpat();
for (h = 1; h < Hist_num && hp; h++) {
if (hp->histline == NULL) {
Char sbuf[BUFSIZ];
hp->histline = Strsave(sprlex(sbuf, &hp->Hlex));
}
#ifdef SDEBUG
xprintf("Comparing with \"%s\"\n", short2str(hp->histline));
#endif
if (c_hmatch(hp->histline)) {
found = h;
hpt = hp;
}
hp = hp->Hnext;
}
if (!found) { /* is it the current history number? */
if (c_hmatch(HistBuf)) {
copyn(InputBuf, HistBuf, INBUFSIZ);
LastChar = InputBuf + (LastHist - HistBuf);
Hist_num = 0;
Cursor = LastChar;
return (CC_REFRESH);
}
else {
return (CC_ERROR);
}
}
Hist_num = found;
hp = hpt;
if (HistLit && hp->histline) {
copyn(InputBuf, hp->histline, INBUFSIZ);
Cur_HistLit = 1;
}
else {
(void) sprlex(InputBuf, &hp->Hlex);
Cur_HistLit = 0;
}
LastChar = InputBuf + Strlen(InputBuf);
if (LastChar > InputBuf) {
if (LastChar[-1] == '\n')
LastChar--;
if (LastChar[-1] == ' ')
LastChar--;
if (LastChar < InputBuf)
LastChar = InputBuf;
}
Cursor = LastChar;
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_helpme()
{
PastBottom();
*LastChar = '\0'; /* just in case */
return (CC_HELPME);
}
/*VARARGS*/
CCRETVAL
e_corr()
{
*LastChar = '\0'; /* just in case */
return (CC_CORRECT);
}
/*VARARGS*/
CCRETVAL
e_corrl()
{
*LastChar = '\0'; /* just in case */
return (CC_CORRECT_L);
}
/*VARARGS*/
CCRETVAL
e_run_fg_editor()
{
register struct process *pp;
extern bool tellwhat;
if ((pp = find_stop_ed()) != PNULL) {
/* save our editor state so we can restore it */
tellwhat = 1;
copyn(WhichBuf, InputBuf, INBUFSIZ);
LastWhich = WhichBuf + (LastChar - InputBuf);
CursWhich = WhichBuf + (Cursor - InputBuf);
HistWhich = Hist_num;
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();
tellwhat = 0;
}
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_l_choices()
{
PastBottom();
*LastChar = '\0'; /* just in case */
return (CC_LIST_CHOICES);
}
/*VARARGS*/
CCRETVAL
e_l_glob()
{
PastBottom();
*LastChar = '\0'; /* just in case */
return (CC_LIST_GLOB);
}
/*VARARGS*/
CCRETVAL
e_ex_glob()
{
*LastChar = '\0'; /* just in case */
return (CC_EXPAND_GLOB);
}
/*VARARGS*/
CCRETVAL
e_ex_vars()
{
*LastChar = '\0'; /* just in case */
return (CC_EXPAND_VARS);
}
/*VARARGS*/
CCRETVAL
e_which()
{ /* do a fast command line which(1) */
PastBottom();
*LastChar = '\0'; /* just in case */
return (CC_WHICH);
}
/*VARARGS*/
CCRETVAL
e_last_item()
{ /* insert the last element of the prev. cmd */
register Char *cp;
register struct Hist *hp;
register struct wordent *wp, *firstp;
register int i;
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;
for (i = 0; i < Argument; i++) { /* back up arg words in lex */
wp = wp->prev;
if (wp == firstp)
break;
}
while (i > 0) {
cp = wp->word;
if (!cp)
return (CC_ERROR);
if (InsertStr(cp))
return (CC_ERROR);
wp = wp->next;
i--;
}
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_yank_kill()
{ /* almost like GnuEmacs */
register Char *kp, *cp;
if (LastKill == KillBuf) /* if zero content */
return (CC_ERROR);
if (LastChar + (LastKill - KillBuf) >= InputLim)
return (CC_ERROR); /* end of buffer space */
/* else */
Mark = Cursor; /* set the mark */
cp = Cursor; /* for speed */
c_insert(LastKill - KillBuf); /* open the space, */
for (kp = KillBuf; kp < LastKill; kp++) /* copy the chars */
*cp++ = *kp;
if (Argument == 1) /* if an arg, cursor at beginning */
Cursor = cp; /* else cursor at end */
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_delprev()
{
if (Cursor > InputBuf) {
c_delbefore(Argument); /* delete before dot */
Cursor -= Argument;
if (Cursor < InputBuf)
Cursor = InputBuf; /* bounds check */
return (CC_REFRESH);
}
else {
return (CC_ERROR);
}
}
/*VARARGS*/
CCRETVAL
e_dwrdprev()
{
register Char *cp, *p, *kp;
if (Cursor == InputBuf)
return (CC_ERROR);
/* else */
cp = c_prev_word(Cursor, InputBuf, Argument);
for (p = cp, kp = KillBuf; p < Cursor; p++) /* save the text */
*kp++ = *p;
LastKill = kp;
c_delbefore(Cursor - cp); /* delete before dot */
Cursor = cp;
if (Cursor < InputBuf)
Cursor = InputBuf; /* bounds check */
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_delnext()
{
if (Cursor == LastChar) { /* if I'm at the end */
if (Cursor == InputBuf && !VImode) {
/* if I'm also at the beginning */
so_write(STReof, 4);/* then do a EOF */
flush();
return (CC_EOF);
}
else {
return (CC_ERROR);
}
}
else {
c_delafter(Argument); /* delete after dot */
if (Cursor > LastChar)
Cursor = LastChar; /* bounds check */
return (CC_REFRESH);
}
}
/*VARARGS*/
CCRETVAL
e_l_delnext()
{
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);
}
}
CCRETVAL
e_l_eof()
{
if (Cursor == LastChar && Cursor == InputBuf) {
so_write(STReof, 4); /* then do a EOF */
flush();
return (CC_EOF);
}
else {
PastBottom();
*LastChar = '\0'; /* just in case */
return (CC_LIST_CHOICES);
}
}
/*VARARGS*/
CCRETVAL
e_dwrdnext()
{
register Char *cp, *p, *kp;
if (Cursor == LastChar)
return (CC_ERROR);
/* else */
cp = c_next_word(Cursor, LastChar, Argument);
for (p = Cursor, kp = KillBuf; p < cp; p++) /* save the text */
*kp++ = *p;
LastKill = kp;
c_delafter(cp - Cursor); /* delete after dot */
/* Cursor = Cursor; */
if (Cursor > LastChar)
Cursor = LastChar; /* bounds check */
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_toend()
{
Cursor = LastChar;
RefCursor(); /* move the cursor */
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_tobeg()
{
Cursor = InputBuf;
RefCursor(); /* move the cursor */
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_killend()
{
register Char *kp, *cp;
cp = Cursor;
kp = KillBuf;
while (cp < LastChar)
*kp++ = *cp++; /* copy it */
LastKill = kp;
LastChar = Cursor; /* zap! -- delete to end */
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_killbeg()
{
register Char *kp, *cp;
cp = InputBuf;
kp = KillBuf;
while (cp < Cursor)
*kp++ = *cp++; /* copy it */
LastKill = kp;
c_delbefore(Cursor - InputBuf);
Cursor = InputBuf; /* zap! */
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_killall()
{
register Char *kp, *cp;
cp = InputBuf;
kp = KillBuf;
while (cp < LastChar)
*kp++ = *cp++; /* copy it */
LastKill = kp;
LastChar = InputBuf; /* zap! -- delete all of it */
Cursor = InputBuf;
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_killregion()
{
register Char *kp, *cp;
if (!Mark)
return (CC_ERROR);
if (Mark > Cursor) {
cp = Cursor;
kp = KillBuf;
while (cp < Mark)
*kp++ = *cp++; /* copy it */
LastKill = kp;
c_delafter(cp - Cursor);/* delete it */
}
else { /* mark is before cursor */
cp = Mark;
kp = KillBuf;
while (cp < Cursor)
*kp++ = *cp++; /* copy it */
LastKill = kp;
c_delbefore(cp - Mark);
Cursor = Mark;
}
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_copyregion()
{
register Char *kp, *cp;
if (!Mark)
return (CC_ERROR);
if (Mark > Cursor) {
cp = Cursor;
kp = KillBuf;
while (cp < Mark)
*kp++ = *cp++; /* copy it */
LastKill = kp;
}
else { /* mark is before cursor */
cp = Mark;
kp = KillBuf;
while (cp < Cursor)
*kp++ = *cp++; /* copy it */
LastKill = kp;
}
return (CC_NORM); /* don't even need to Refresh() */
}
/*VARARGS*/
CCRETVAL
e_charswitch()
{
register Char c;
if (Cursor < LastChar) {
if (LastChar <= &InputBuf[1]) {
return (CC_ERROR);
}
else {
Cursor++;
}
}
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);
}
}
/*VARARGS*/
CCRETVAL
e_gcharswitch()
{ /* gosmacs style ^T */
register Char c;
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);
}
}
/*VARARGS*/
CCRETVAL
e_charback()
{
if (Cursor > InputBuf) {
Cursor -= Argument;
if (Cursor < InputBuf)
Cursor = InputBuf;
RefCursor();
return (CC_NORM);
}
else {
return (CC_ERROR);
}
}
/*VARARGS*/
CCRETVAL
e_wordback()
{
if (Cursor == InputBuf)
return (CC_ERROR);
/* else */
Cursor = c_prev_word(Cursor, InputBuf, Argument); /* does a bounds check */
RefCursor();
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_charfwd()
{
if (Cursor < LastChar) {
Cursor += Argument;
if (Cursor > LastChar)
Cursor = LastChar;
RefCursor();
return (CC_NORM);
}
else {
return (CC_ERROR);
}
}
/*VARARGS*/
CCRETVAL
e_wordfwd()
{
if (Cursor == LastChar)
return (CC_ERROR);
/* else */
Cursor = c_next_word(Cursor, LastChar, Argument);
RefCursor();
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
v_wordbegnext()
{
if (Cursor == LastChar)
return (CC_ERROR);
/* else */
Cursor = c_beg_next_word(Cursor, LastChar, Argument);
RefCursor();
return (CC_NORM);
}
#ifdef COMMENT
/* by: Brian Allison <uiucdcs!convex!allison@RUTGERS.EDU> */
static void
c_get_word(begin, end)
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 */
/*VARARGS*/
CCRETVAL
e_uppercase()
{
Char *cp, *end;
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);
}
/*VARARGS*/
CCRETVAL
e_capitolcase()
{
Char *cp, *end;
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);
}
/*VARARGS*/
CCRETVAL
e_lowercase()
{
Char *cp, *end;
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);
}
/*VARARGS*/
CCRETVAL
e_set_mark()
{
Mark = Cursor;
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_exchange_mark()
{
register Char *cp;
cp = Cursor;
Cursor = Mark;
Mark = cp;
RefCursor();
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_argfour()
{ /* multiply current argument by 4 */
if (Argument > 1000000)
return CC_ERROR;
DoingArg = 1;
Argument *= 4;
return (CC_ARGHACK);
}
/*VARARGS*/
CCRETVAL
e_quote()
{
Char ch;
int num;
QModeOn();
num = G_N_Char(&ch);
QModeOff();
if (num == 1)
return e_insert(ch);
else
return e_send_eof();
}
/*VARARGS*/
CCRETVAL
e_metanext()
{
MetaNext = 1;
return (CC_ARGHACK); /* preserve argument */
}
#ifdef notdef
/*VARARGS*/
CCRETVAL
e_extendnext()
{
Cur_KeyMap = CcAltMap;
return (CC_ARGHACK); /* preserve argument */
}
#endif
/*VARARGS*/
CCRETVAL
v_insbeg()
{ /* move to beginning of line and start vi
* insert mode */
Cursor = InputBuf;
RefCursor(); /* move the cursor */
c_alternativ_key_map(0);
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
v_replone()
{ /* vi mode overwrite one character */
c_alternativ_key_map(0);
replacemode = 2;
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
v_replmode()
{ /* vi mode start overwriting */
c_alternativ_key_map(0);
replacemode = 1;
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
v_s_char()
{ /* vi mode substitute for one char */
c_delafter(Argument);
c_alternativ_key_map(0);
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
v_s_line()
{ /* vi mode replace whole line */
(void) e_killall();
c_alternativ_key_map(0);
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
v_chgtoend()
{ /* vi mode change to end of line */
(void) e_killend();
c_alternativ_key_map(0);
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
v_insert()
{ /* vi mode start inserting */
c_alternativ_key_map(0);
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
v_add()
{ /* vi mode start adding */
c_alternativ_key_map(0);
if (Cursor < LastChar) {
Cursor++;
if (Cursor > LastChar)
Cursor = LastChar;
RefCursor();
}
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
v_addend()
{ /* vi mode to add at end of line */
c_alternativ_key_map(0);
Cursor = LastChar;
RefCursor();
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
v_change_case()
{
char c;
if (Cursor < LastChar) {
c = *Cursor;
if (Isupper(c))
*Cursor++ = Tolower(c);
else if (Islower(c))
*Cursor++ = Toupper(c);
else
Cursor++;
RefPlusOne(); /* fast refresh for one char */
return (CC_NORM);
}
return (CC_ERROR);
}
/*VARARGS*/
CCRETVAL
e_expand()
{
register Char *p;
extern bool justpr;
for (p = InputBuf; Isspace(*p); p++);
if (p == LastChar)
return (CC_ERROR);
justpr++;
Expand++;
return (e_newline());
}
/*VARARGS*/
CCRETVAL
e_startover()
{ /* erase all of current line, start again */
ResetInLine(); /* reset the input pointers */
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_redisp()
{
ClearLines();
ClearDisp();
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_cleardisp()
{
ClearScreen(); /* clear the whole real screen */
ClearDisp(); /* reset everything */
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_t_int()
{
/* do no editing */
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_insovr()
{
replacemode = !replacemode;
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_t_dsusp()
{
/* do no editing */
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_t_flusho()
{
/* do no editing */
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_t_quit()
{
/* do no editing */
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_t_tsusp()
{
/* do no editing */
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_t_stopo()
{
/* do no editing */
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_ex_history()
{
*LastChar = '\0'; /* just in case */
c_substitute();
return (CC_NORM);
}
/*VARARGS*/
CCRETVAL
e_magic_space()
{
*LastChar = '\0'; /* just in case */
c_substitute();
return (e_insert(' '));
}
/*VARARGS*/
CCRETVAL
e_copyprev()
{
register Char *cp, *oldc, *dp;
if (Cursor == InputBuf)
return (CC_ERROR);
/* else */
oldc = Cursor;
/* does a bounds check */
cp = c_prev_word(Cursor, InputBuf, Argument);
c_insert(oldc - cp);
for (dp = oldc; cp < oldc && dp < LastChar; cp++)
*dp++ = *cp;
Cursor = dp; /* put cursor at end */
return (CC_REFRESH);
}
/*VARARGS*/
CCRETVAL
e_t_starto()
{
/* do no editing */
return (CC_NORM);
}
#ifdef notdef
void
MoveCursor(n) /* move cursor + right - left char */
int n;
{
Cursor = Cursor + n;
if (Cursor < InputBuf)
Cursor = InputBuf;
if (Cursor > LastChar)
Cursor = LastChar;
return;
}
Char *
GetCursor()
{
return (Cursor);
}
int
PutCursor(p)
Char *p;
{
if (p < InputBuf || p > LastChar)
return 1; /* Error */
Cursor = p;
return 0;
}
#endif