Logo Search packages:      
Sourcecode: tcsh version File versions  Download package

ed.chared.c

/* $Header: /src/pub/tcsh/ed.chared.c,v 3.83 2005/03/03 16:21:08 kim 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("$Id: ed.chared.c,v 3.83 2005/03/03 16:21:08 kim 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_ALNUM   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 Char patbuf[INBUFSIZE];                  /* Search target */
static int patlen = 0;
/*
 * 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   __P((int));
void   c_insert         __P((int));
void   c_delafter       __P((int));
void   c_delbefore            __P((int));
static      int    c_to_class       __P((Char));
static      Char  *c_prev_word            __P((Char *, Char *, int));
static      Char  *c_next_word            __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      void   c_delfini        __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      Char  *c_preword        __P((Char *, Char *, int, Char *));
static      Char  *c_nexword        __P((Char *, Char *, int));
static      Char  *c_endword        __P((Char *, Char *, int, Char *));
static      Char  *c_eword          __P((Char *, Char *, int));
static      void   c_push_kill            __P((Char *, Char *));
static  CCRETVAL c_get_histline           __P((void));
static  CCRETVAL c_search_line            __P((Char *, int));
static  CCRETVAL v_repeat_srch            __P((int));
static      CCRETVAL e_inc_search         __P((int));
static  CCRETVAL e_insert_str       __P((Char *));
static      CCRETVAL v_search       __P((int));
static      CCRETVAL v_csearch_fwd        __P((Char, int, int));
static      CCRETVAL v_action       __P((int));
static      CCRETVAL v_csearch_back       __P((Char, int, int));

static void
c_alternativ_key_map(state)
    int     state;
{
    switch (state) {
    case 0:
      CurrentKeyMap = CcKeyMap;
      break;
    case 1:
      CurrentKeyMap = CcAltMap;
      break;
    default:
      return;
    }

    AltKeyMap = (Char) state;
}

void
c_insert(num)
    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(num)   
    int num;
{
    Char *cp, *kp = NULL;

    if (num > LastChar - Cursor)
      num = (int) (LastChar - Cursor);    /* bounds check */

    if (num > 0) {                  /* if I can delete anything */
      num = NLSExtend(Cursor, LastChar - Cursor, num);
      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;
      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(num)        /* delete before dot, with bounds checking */
    int num;
{
    Char *cp, *kp = NULL;

    if (num > Cursor - InputBuf)
      num = (int) (Cursor - InputBuf);    /* bounds check */

    if (num > 0) {                  /* if I can delete anything */
      num = NLSExtend(Cursor, Cursor - InputBuf, -num);
      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;
      if (Mark && Mark > Cursor)
            Mark -= num;
    }
}

static Char *
c_preword(p, low, n, delim)
    Char *p, *low, *delim;
    int n;
{
  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() and c_next_word() 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(ch)
Char ch;
{
    if (Isspace(ch))
        return C_CLASS_WHITE;

    if (Isdigit(ch) || Isalpha(ch) || ch == '_')
        return C_CLASS_ALNUM;

    return C_CLASS_OTHER;
}

static Char *
c_prev_word(p, low, n)
    Char *p, *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(p, high, n)
    Char *p, *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(p, high, n)
    Char *p, *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(p, num, dval)
    Char *p;
    int *num;
    int dval;
{
    int i;
    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')
      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(p)
    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[INBUFSIZE];
    Char   *bend = buf;
    Char   *modbuf, *omodbuf;

    if (!h)
      goto excl_err;
excl_sw:
    switch (*(q = p + 1)) {

    case '^':
      bend = expand_lex(buf, INBUFSIZE, &h->Hlex, 1, 1);
      break;

    case '$':
      if ((l = (h->Hlex).prev) != 0)
          bend = expand_lex(buf, INBUFSIZE, l->prev->prev, 0, 0);
      break;

    case '*':
      bend = expand_lex(buf, INBUFSIZE, &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, INBUFSIZE, &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 = (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 = NCARGS;
          }
          else {
            to = from;
          }
          if (from < 0 || to < from)
            goto excl_err;
          bend = expand_lex(buf, INBUFSIZE, &h->Hlex, from, to);
      }
      else {                  /* get whole cmd */
          bend = expand_lex(buf, INBUFSIZE, &h->Hlex, 0, NCARGS);
      }
      break;
    }

    /*
     * Apply modifiers, if any.
     */
    if (q[1] == ':') {
      *bend = '\0';
      modbuf = omodbuf = 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(omodbuf, (int) q[2])) != NULL) {
                if (omodbuf != buf)
                  xfree((ptr_t) omodbuf);
                omodbuf = 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;
      }
      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;
    (void) memmove((ptr_t) (q + (bend - buf) - (q - op)), (ptr_t) q, 
               (size_t) ((LastChar - q) * sizeof(Char)));
    LastChar += (bend - buf) - (q - op);
    Cursor += (bend - buf) - (q - op);
    (void) memmove((ptr_t) op, (ptr_t) buf, 
               (size_t) ((bend - buf) * sizeof(Char)));
    *LastChar = '\0';
    return(op + (bend - buf));
excl_err:
    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 '>!'
 */

static void
c_excl(p)
    Char *p;
{
    int i;
    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)
          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.  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++)
          continue;
      if (i % 2 == 0)
          ++p;
      if (p >= Cursor)
          return;
      if (i % 2 == 1)
          p = c_expand(p);
    }
}


static void
c_substitute()
{
    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)
      continue;

    /*
     * If we found a history character, go expand it.
     */
    if (*p == HIST)
      c_excl(p);
    Refresh();
}

static void
c_delfini()       /* 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(p, high, n, delim)
    Char *p, *high, *delim;
    int n;
{
    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(p, high, n)
    Char *p, *high;
    int n;
{
    p++;

    while (n--) {
      while ((p < high) && Isspace(*p)) 
          p++;

      if (Isalnum(*p))
          while ((p < high) && Isalnum(*p)) 
            p++;
      else
          while ((p < high) && !(Isspace(*p) || Isalnum(*p)))
            p++;
    }

    p--;
    return(p);
}

/* Set the max length of the kill ring */
void
SetKillRing(max)
    int max;
{
    CStr *new;
    int count, i, j;

    if (max < 1)
      max = 1;          /* no ring, but always one buffer */
    if (max == KillRingMax)
      return;
    new = (CStr *)xcalloc((size_t) 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(start, end)
    Char *start, *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) {
      if (pos->buf == NULL)
          pos->buf = (Char *) xmalloc(len * sizeof(Char));
      else
          pos->buf = (Char *) xrealloc((ptr_t) pos->buf, len * sizeof(Char));
      pos->len = len;
    }
    cp = start;
    kp = pos->buf;
    while (cp < end)
      *kp++ = *cp++;
    *kp = '\0';
}

static CCRETVAL
c_get_histline()
{
    struct Hist *hp;
    int     h;

    if (Hist_num == 0) {      /* if really the current line */
      copyn(InputBuf, HistBuf, INBUFSIZE);
      LastChar = InputBuf + (LastHist - HistBuf);

#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);
      CurrentHistLit = 1;
    }
    else {
      (void) sprlex(InputBuf, sizeof(InputBuf) / sizeof(Char), &hp->Hlex);
      CurrentHistLit = 0;
    }
    LastChar = InputBuf + Strlen(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(pattern, dir)
Char *pattern;
int dir;
{
    Char *cp;
    int len;

    len = (int) Strlen(pattern);

    if (dir == F_UP_SEARCH_HIST) {
      for (cp = Cursor; cp >= InputBuf; cp--)
          if (Strncmp(cp, pattern, (size_t) 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, (size_t) len) == 0 ||
            Gmatch(cp, pattern)) {
            Cursor = cp;
            return(CC_NORM);
          }
      return(CC_ERROR);
    }
}

static CCRETVAL
e_inc_search(dir)
    int dir;
{
    static Char STRfwd[] = { 'f', 'w', 'd', '\0' },
            STRbck[] = { 'b', 'c', 'k', '\0' };
    static Char pchar = ':';  /* ':' = normal, '?' = failed */
    static Char endcmd[2];
    Char ch, *cp,
      *oldCursor = Cursor,
      oldpchar = pchar;
    CCRETVAL ret = CC_NORM;
    int oldHist_num = Hist_num,
      oldpatlen = patlen,
      newdir = dir,
        done, redo;

    if (LastChar + sizeof(STRfwd)/sizeof(Char) + 2 + patlen >= InputLim)
      return(CC_ERROR);

    for (;;) {

      if (patlen == 0) {      /* first round */
          pchar = ':';
          patbuf[patlen++] = '*';
      }
      done = redo = 0;
      *LastChar++ = '\n';
      for (cp = newdir == F_UP_SEARCH_HIST ? STRbck : STRfwd; 
           *cp; *LastChar++ = *cp++)
          continue;
      *LastChar++ = pchar;
      for (cp = &patbuf[1]; cp < &patbuf[patlen]; *LastChar++ = *cp++)
          continue;
      *LastChar = '\0';
      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 (patlen > INBUFSIZE - 3)
            SoundBeep();
          else {
            patbuf[patlen++] = 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 (patlen > 1)
            done++;
          else 
            SoundBeep();
          break;

      default:
          switch (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[1]; ; cp++)
                if (cp >= &patbuf[patlen]) {
                  Cursor += patlen - 1;
                  cp = c_next_word(Cursor, LastChar, 1);
                  while (Cursor < cp && *Cursor != '\n') {
                      if (patlen > INBUFSIZE - 3) {
                        SoundBeep();
                        break;
                      }
                      patbuf[patlen++] = *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[patlen - 1], ch = ']'; cp > patbuf; cp--)
            if (*cp == '[' || *cp == ']') {
                ch = *cp;
                break;
            }

          if (patlen > 1 && ch != '[') {
            if (redo && newdir == dir) {
                if (pchar == '?') { /* wrap around */
                  Hist_num = newdir == F_UP_SEARCH_HIST ? 0 : 0x7fffffff;
                  if (c_get_histline() == CC_ERROR)
                      /* Hist_num was fixed by first call */
                      (void) c_get_histline();
                  Cursor = newdir == F_UP_SEARCH_HIST ?
                      LastChar : InputBuf;
                } else
                  Cursor += newdir == F_UP_SEARCH_HIST ? -1 : 1;
            }
            patbuf[patlen++] = '*';
            patbuf[patlen] = '\0';
            if (Cursor < InputBuf || Cursor > LastChar ||
                (ret = c_search_line(&patbuf[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[1], newdir);
                }
            }
            patbuf[--patlen] = '\0';
            if (ret == CC_ERROR) {
                SoundBeep();
                if (Hist_num != oldHist_num) {
                  Hist_num = oldHist_num;
                  if (c_get_histline() == 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;
          patlen = oldpatlen;
          if (Hist_num != oldHist_num) {
            Hist_num = oldHist_num;
            if (c_get_histline() == CC_ERROR)
                return(CC_ERROR);
          }
          Cursor = oldCursor;
          if (ret == CC_ERROR)
            Refresh();
      }
      if (done || ret != CC_NORM)
          return(ret);
          
    }

}

static CCRETVAL
v_search(dir)
    int dir;
{
    Char ch;
    Char tmpbuf[INBUFSIZE];
    Char oldbuf[INBUFSIZE];
    Char *oldlc, *oldc;
    int tmplen;

    copyn(oldbuf, InputBuf, INBUFSIZE);
    oldlc = LastChar;
    oldc = Cursor;
    tmplen = 0;
    tmpbuf[tmplen++] = '*';

    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)
          return(e_send_eof(0));
      switch (ASC(ch)) {
      case 0010:  /* Delete and backspace */
      case 0177:
          if (tmplen > 1) {
            *Cursor-- = '\0';
            LastChar = Cursor;
            tmpbuf[tmplen--] = '\0';
          }
          else {
            copyn(InputBuf, oldbuf, INBUFSIZE);
            LastChar = oldlc;
            Cursor = oldc;
            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:
          if (tmplen >= INBUFSIZE)
            SoundBeep();
          else {
            tmpbuf[tmplen++] = ch;
            *Cursor++ = ch;
            LastChar = Cursor;
          }
          Refresh();
          ch = 0;
          break;
      }
    }

    if (tmplen == 1) {
      /*
       * Use the old pattern, but wild-card it.
       */
      if (patlen == 0) {
          InputBuf[0] = '\0';
          LastChar = InputBuf;
          Cursor = InputBuf;
          Refresh();
          return(CC_ERROR);
      }
      if (patbuf[0] != '*') {
          (void) Strcpy(tmpbuf, patbuf);
          patbuf[0] = '*';
          (void) Strcpy(&patbuf[1], tmpbuf);
          patlen++;
          patbuf[patlen++] = '*';
          patbuf[patlen] = '\0';
      }
    }
    else {
      tmpbuf[tmplen++] = '*';
      tmpbuf[tmplen] = '\0';
      (void) Strcpy(patbuf, tmpbuf);
      patlen = tmplen;
    }
    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 (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(c)
    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(c)
    Char c;
{                       /* bound to keys that arn't really assigned */
    USE(c);
    SoundBeep();
    flush();
    return(CC_NORM);
}

static CCRETVAL
e_insert_str(c)
    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 * NLSChars(c));
    }
    c_insert(Argument * n);
    while (Argument--) {
      for (i = 0; i < n; i++)
          *Cursor++ = c[i];
    }
    Refresh();
    return(CC_NORM);
}

CCRETVAL
e_insert(c)
    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 (!NLSFinished(Cursor, 0, c)) {
      Char buf[MB_LEN_MAX + 1];
      int f;
      size_t i = 1;
      buf[0] = c;
      do {
          if (GetNextChar(&c) != 1)
            break;
          f = NLSFinished(buf, i, (eChar)c);
          if (f == -1) {
            UngetNextChar(c);
            break;
          }
          buf[i++] = c;
      } while (!f && i < MB_CUR_MAX);
      if (i > 1) {
          buf[i] = 0;
          return e_insert_str(buf);
      }
      c = buf[0];
    }

    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(s)                  /* insert ASCIZ s at cursor (for complete) */
    Char   *s;
{
    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(n)                 /* delete the n characters before . */
    int     n;
{
    if (n <= 0)
      return;
    if (Cursor >= &InputBuf[n]) {
      c_delbefore(n);         /* delete before dot */
    }
}

CCRETVAL
e_digit(c)              /* gray magic here */
    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 */

      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(c)                 /* for ESC-n */
    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);
}

CCRETVAL
v_zero(c)               /* command mode 0 for vi */
    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;
      if (ActionFlag & TCSHOP_DELETE) {
         c_delfini();
         return(CC_REFRESH);
        }
      RefCursor();            /* move the cursor */
      return(CC_NORM);
    }
}

/*ARGSUSED*/
CCRETVAL
e_newline(c)
    Char c;
{                       /* always ignore argument */
    USE(c);
  /*  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_send_eof(c)
    Char c;
{                       /* for when ^D is ONLY send-eof */
    USE(c);
    PastBottom();
    *LastChar = '\0';         /* just in case */
    return(CC_EOF);
}

/*ARGSUSED*/
CCRETVAL
e_complete(c)
    Char c;
{
    USE(c);
    *LastChar = '\0';         /* just in case */
    return(CC_COMPLETE);
}

/*ARGSUSED*/
CCRETVAL
e_complete_back(c)
    Char c;
{
    USE(c);
    *LastChar = '\0';         /* just in case */
    return(CC_COMPLETE_BACK);
}

/*ARGSUSED*/
CCRETVAL
e_complete_fwd(c)
    Char c;
{
    USE(c);
    *LastChar = '\0';         /* just in case */
    return(CC_COMPLETE_FWD);
}

/*ARGSUSED*/
CCRETVAL
e_complete_all(c)
    Char c;
{
    USE(c);
    *LastChar = '\0';         /* just in case */
    return(CC_COMPLETE_ALL);
}

/*ARGSUSED*/
CCRETVAL
v_cm_complete(c)
    Char c;
{
    USE(c);
    if (Cursor < LastChar)
      Cursor++;
    *LastChar = '\0';         /* just in case */
    return(CC_COMPLETE);
}

/*ARGSUSED*/
CCRETVAL
e_toggle_hist(c)
    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);
          CurrentHistLit = 1;
      }
      else {
          return CC_ERROR;
      }
    }
    else {
      (void) sprlex(InputBuf, sizeof(InputBuf) / sizeof(Char), &hp->Hlex);
      CurrentHistLit = 0;
    }

    LastChar = InputBuf + Strlen(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(c)
    Char c;
{
    Char    beep = 0;

    USE(c);
    UndoAction = TCSHOP_NOP;
    *LastChar = '\0';         /* just in case */

    if (Hist_num == 0) {      /* save the current buffer away */
      copyn(HistBuf, InputBuf, INBUFSIZE);
      LastHist = HistBuf + (LastChar - InputBuf);
    }

    Hist_num += Argument;

    if (c_get_histline() == CC_ERROR) {
      beep = 1;
      (void) c_get_histline(); /* 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(c)
    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(c_get_histline());
}



/*
 * c_hmatch() return True if the pattern matches the prefix
 */
static int
c_hmatch(str)
Char *str;
{
    if (Strncmp(patbuf, str, (size_t) 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 = (int) (Cursor - InputBuf);
      if (patlen >= INBUFSIZE) patlen = INBUFSIZE -1;
      if (patlen >= 0)  {
          (void) Strncpy(patbuf, InputBuf, (size_t) patlen);
          patbuf[patlen] = '\0';
      }
      else
          patlen = (int) Strlen(patbuf);
    }
#ifdef SDEBUG
    xprintf("\nHist_num = %d\n", Hist_num);
    xprintf("patlen = %d\n", patlen);
    xprintf("patbuf = \"%S\"\n", patbuf);
    xprintf("Cursor %d LastChar %d\n", Cursor - InputBuf, LastChar - InputBuf);
#endif
}

/*ARGSUSED*/
CCRETVAL
e_up_search_hist(c)
    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)
    {
      copyn(HistBuf, InputBuf, INBUFSIZE);
      LastHist = HistBuf + (LastChar - InputBuf);
    }


    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 sbuf[INBUFSIZE], *hl;
      if (hp->histline == NULL) {
          hp->histline = Strsave(sprlex(sbuf, sizeof(sbuf) / sizeof(Char),
                           &hp->Hlex));
      }
      hl = HistLit ? hp->histline : sprlex(sbuf, sizeof(sbuf) / sizeof(Char),
                                   &hp->Hlex);
#ifdef SDEBUG
      xprintf("Comparing with \"%S\"\n", hl);
#endif
      if ((Strncmp(hl, InputBuf, (size_t) (LastChar - InputBuf)) || 
           hl[LastChar-InputBuf]) && c_hmatch(hl)) {
          found++;
          break;
      }
      h++;
      hp = hp->Hnext;
    }

    if (!found) {
#ifdef SDEBUG
      xprintf("not found\n"); 
#endif
      return(CC_ERROR);
    }

    Hist_num = h;

    return(c_get_histline());
}

/*ARGSUSED*/
CCRETVAL
e_down_search_hist(c)
    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 sbuf[INBUFSIZE], *hl;
      if (hp->histline == NULL) {
          hp->histline = Strsave(sprlex(sbuf, sizeof(sbuf) / sizeof(Char),
                           &hp->Hlex));
      }
      hl = HistLit ? hp->histline : sprlex(sbuf, sizeof(sbuf) / sizeof(Char),
                                   &hp->Hlex);
#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;
      hp = hp->Hnext;
    }

    if (!found) {       /* is it the current history number? */
      if (!c_hmatch(HistBuf)) {
#ifdef SDEBUG
          xprintf("not found\n"); 
#endif
          return(CC_ERROR);
      }
    }

    Hist_num = found;

    return(c_get_histline());
}

/*ARGSUSED*/
CCRETVAL
e_helpme(c)
    Char c;
{
    USE(c);
    PastBottom();
    *LastChar = '\0';         /* just in case */
    return(CC_HELPME);
}

/*ARGSUSED*/
CCRETVAL
e_correct(c)
    Char c;
{
    USE(c);
    *LastChar = '\0';         /* just in case */
    return(CC_CORRECT);
}

/*ARGSUSED*/
CCRETVAL
e_correctl(c)
    Char c;
{
    USE(c);
    *LastChar = '\0';         /* just in case */
    return(CC_CORRECT_L);
}

/*ARGSUSED*/
CCRETVAL
e_run_fg_editor(c)
    Char c;
{
    struct process *pp;

    USE(c);
    if ((pp = find_stop_ed()) != NULL) {
      /* save our editor state so we can restore it */
      tellwhat = 1;
      copyn(WhichBuf, InputBuf, INBUFSIZE);
      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);
}

/*ARGSUSED*/
CCRETVAL
e_list_choices(c)
    Char c;
{
    USE(c);
    PastBottom();
    *LastChar = '\0';         /* just in case */
    return(CC_LIST_CHOICES);
}

/*ARGSUSED*/
CCRETVAL
e_list_all(c)
    Char c;
{
    USE(c);
    PastBottom();
    *LastChar = '\0';         /* just in case */
    return(CC_LIST_ALL);
}

/*ARGSUSED*/
CCRETVAL
e_list_glob(c)
    Char c;
{
    USE(c);
    PastBottom();
    *LastChar = '\0';         /* just in case */
    return(CC_LIST_GLOB);
}

/*ARGSUSED*/
CCRETVAL
e_expand_glob(c)
    Char c;
{
    USE(c);
    *LastChar = '\0';         /* just in case */
    return(CC_EXPAND_GLOB);
}

/*ARGSUSED*/
CCRETVAL
e_normalize_path(c)
    Char c;
{
    USE(c);
    *LastChar = '\0';         /* just in case */
    return(CC_NORMALIZE_PATH);
}

/*ARGSUSED*/
CCRETVAL
e_normalize_command(c)
    Char c;
{
    USE(c);
    *LastChar = '\0';         /* just in case */
    return(CC_NORMALIZE_COMMAND);
}

/*ARGSUSED*/
CCRETVAL
e_expand_vars(c)
    Char c;
{
    USE(c);
    *LastChar = '\0';         /* just in case */
    return(CC_EXPAND_VARS);
}

/*ARGSUSED*/
CCRETVAL
e_which(c)
    Char c;
{                       /* do a fast command line which(1) */
    USE(c);
    PastBottom();
    *LastChar = '\0';         /* just in case */
    return(CC_WHICH);
}

/*ARGSUSED*/
CCRETVAL
e_last_item(c)
    Char c;
{                       /* insert the last element of the prev. cmd */
    Char *cp;
    struct Hist *hp;
    struct wordent *wp, *firstp;
    int i;
    Char buf[INBUFSIZE];

    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;
    }

    cp = expand_lex(buf, INBUFSIZE, wp->prev, 0, i - 1);
    *cp = '\0';
    if (InsertStr(buf))
      return(CC_ERROR);

    return(CC_REFRESH);
}

/*ARGSUSED*/
CCRETVAL
e_dabbrev_expand(c)
    Char c;
{                       /* expand to preceding word matching prefix */
    Char *cp, *ncp, *bp;
    struct Hist *hp;
    int arg = 0, len = 0, i; /* len = 0 to shut up gcc -Wall */
    int found = 0;
    Char hbuf[INBUFSIZE];
    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);

    hp = Histlist.Hnext;
    bp = InputBuf;
    if (Argument == 1 && eventno == oldevent && cp == start &&
      Cursor == oldcursor && patlen > 0 && Strncmp(patbuf, cp, patlen) == 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" */
            return(CC_ERROR);
          cp = expand_lex(hbuf, INBUFSIZE, &hp->Hlex, 0, NCARGS);
          *cp = '\0';
          bp = hbuf;
          hp = hp->Hnext;
      }
      cp = c_preword(cp, bp, word, STRshwordsep);
    } else {                  /* starting new search */
      oldevent = eventno;
      start = cp;
      patlen = (int) (Cursor - cp);
      (void) Strncpy(patbuf, cp, patlen);
      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)
            return(CC_ERROR);
          cp = expand_lex(hbuf, INBUFSIZE, &hp->Hlex, 0, NCARGS);
          *cp = '\0';
          bp = hbuf;
          hp = hp->Hnext;
          continue;
      } else {
          word++;
          len = (int) (c_endword(ncp-1, cp, 1, STRshwordsep) - ncp + 1);
          cp = ncp;
      }
      if (len > patlen && Strncmp(cp, patbuf, patlen) == 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 != Cursor - start || Strncmp(cp, start, len) != 0)
                found++;
          }
      }
    }

    if (LastChar + len - (Cursor - start) >= InputLim)
      return(CC_ERROR); /* no room */
    DeleteBack(Cursor - start);
    c_insert(len);
    while (len--)
      *Cursor++ = *cp++;
    oldcursor = Cursor;
    return(CC_REFRESH);
}

/*ARGSUSED*/
CCRETVAL
e_yank_kill(c)
    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 */
    }

    return(CC_REFRESH);
}

/*ARGSUSED*/
CCRETVAL
e_yank_pop(c)
    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 */
    }

    return(CC_REFRESH);
}

/*ARGSUSED*/
CCRETVAL
v_delprev(c)            /* Backspace key in insert mode */
    Char c;
{
    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(c)
    Char c;
{
    USE(c);
    if (Cursor > InputBuf) {
      c_delbefore(Argument);  /* delete before dot */
      return(CC_REFRESH);
    }
    else {
      return(CC_ERROR);
    }
}

/*ARGSUSED*/
CCRETVAL
e_delwordprev(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    Char c;
{
    USE(c);
    c_push_kill(Cursor, LastChar); /* copy it */
    Mark = LastChar = Cursor;       /* zap! -- delete to end */
    return(CC_REFRESH);
}


/*ARGSUSED*/
CCRETVAL
e_killbeg(c)
    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(c)
    Char c;
{
    USE(c);
    c_push_kill(InputBuf, LastChar); /* copy it */
    Cursor = Mark = LastChar = InputBuf;  /* zap! -- delete all of it */
    return(CC_REFRESH);
}

/*ARGSUSED*/
CCRETVAL
e_killregion(c)
    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));
    }
    return(CC_REFRESH);
}

/*ARGSUSED*/
CCRETVAL
e_copyregion(c)
    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(cc)
    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(cc)
    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(c)
    Char c;
{
    int     num;
    USE(c);
    if (Cursor > InputBuf) {
      num = NLSExtend(Cursor, Cursor - InputBuf, -Argument);
      if (num > Cursor - InputBuf)
          Cursor = InputBuf;
      else
          Cursor -= num;

      if (VImode)
          if (ActionFlag & TCSHOP_DELETE) {
            c_delfini();
            return(CC_REFRESH);
          }

      RefCursor();
      return(CC_NORM);
    }
    else {
      return(CC_ERROR);
    }
}

/*ARGSUSED*/
CCRETVAL
v_wordback(c)
    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(c)
    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(c)
    Char c;
{
    int     num;
    USE(c);
    if (Cursor < LastChar) {
      num = NLSExtend(Cursor, LastChar - Cursor, Argument);
      Cursor += num;
      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(c)
    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(c)
    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(c)
    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(c)
    int c;
{
    CCRETVAL rv = CC_ERROR;
#ifdef SDEBUG
    xprintf("dir %d patlen %d patbuf %S\n", 
          c, patlen, patbuf);
#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(ch, count, tflag)
    Char ch;
    int count, 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(ch, count, tflag)
    Char ch;
    int count, 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(c)
    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(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 */

/*ARGSUSED*/
CCRETVAL
e_uppercase(c)
    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_capitolcase(c)
    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(c)
    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(c)
    Char c;
{
    USE(c);
    Mark = Cursor;
    return(CC_NORM);
}

/*ARGSUSED*/
CCRETVAL
e_exchange_mark(c)
    Char c;
{
    Char *cp;

    USE(c);
    cp = Cursor;
    Cursor = Mark;
    Mark = cp;
    RefCursor();
    return(CC_NORM);
}

/*ARGSUSED*/
CCRETVAL
e_argfour(c)
    Char c;
{                       /* multiply current argument by 4 */
    USE(c);
    if (Argument > 1000000)
      return CC_ERROR;
    DoingArg = 1;
    Argument *= 4;
    return(CC_ARGHACK);
}

/*ARGSUSED*/
CCRETVAL
e_quote(c)
    Char c;
{
    Char    ch;
    int     num;

    USE(c);
    QuoteModeOn();
    num = GetNextChar(&ch);
    QuoteModeOff();
    if (num == 1)
      return e_insert(ch);
    else
      return e_send_eof(0);
}

/*ARGSUSED*/
CCRETVAL
e_metanext(c)
    Char c;
{
    USE(c);
    MetaNext = 1;
    return(CC_ARGHACK); /* preserve argument */
}

#ifdef notdef
/*ARGSUSED*/
CCRETVAL
e_extendnext(c)
    Char c;
{
    CurrentKeyMap = CcAltMap;
    return(CC_ARGHACK); /* preserve argument */
}

#endif

/*ARGSUSED*/
CCRETVAL
v_insbeg(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(cc)
    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(c)
    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(c)
    Char c;
{                       /* erase all of current line, start again */
    USE(c);
    ResetInLine(0);           /* reset the input pointers */
    return(CC_REFRESH);
}

/*ARGSUSED*/
CCRETVAL
e_redisp(c)
    Char c;
{
    USE(c);
    ClearLines();
    ClearDisp();
    return(CC_REFRESH);
}

/*ARGSUSED*/
CCRETVAL
e_cleardisp(c)
    Char c;
{
    USE(c);
    ClearScreen();            /* clear the whole real screen */
    ClearDisp();        /* reset everything */
    return(CC_REFRESH);
}

/*ARGSUSED*/
CCRETVAL
e_tty_int(c)
    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(c)
     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) write(SHIN, "\n", 1);
     len = one_wctomb(buf, c & CHAR);
     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(c)
    Char c;
{
    USE(c);
    inputmode = (inputmode == MODE_INSERT ? MODE_REPLACE : MODE_INSERT);
    return(CC_NORM);
}

/*ARGSUSED*/
CCRETVAL
e_tty_dsusp(c)
    Char c;
{
    USE(c);
    /* do no editing */
    return(CC_NORM);
}

/*ARGSUSED*/
CCRETVAL
e_tty_flusho(c)
    Char c;
{
    USE(c);
    /* do no editing */
    return(CC_NORM);
}

/*ARGSUSED*/
CCRETVAL
e_tty_quit(c)
    Char c;
{
    USE(c);
    /* do no editing */
    return(CC_NORM);
}

/*ARGSUSED*/
CCRETVAL
e_tty_tsusp(c)
    Char c;
{
    USE(c);
    /* do no editing */
    return(CC_NORM);
}

/*ARGSUSED*/
CCRETVAL
e_tty_stopo(c)
    Char c;
{
    USE(c);
    /* do no editing */
    return(CC_NORM);
}

/*ARGSUSED*/
CCRETVAL
e_expand_history(c)
    Char c;
{
    USE(c);
    *LastChar = '\0';         /* just in case */
    c_substitute();
    return(CC_NORM);
}

/*ARGSUSED*/
CCRETVAL
e_magic_space(c)
    Char c;
{
    USE(c);
    *LastChar = '\0';         /* just in case */
    c_substitute();
    return(e_insert(' '));
}

/*ARGSUSED*/
CCRETVAL
e_inc_fwd(c)
    Char c;
{
    USE(c);
    patlen = 0;
    return e_inc_search(F_DOWN_SEARCH_HIST);
}


/*ARGSUSED*/
CCRETVAL
e_inc_back(c)
    Char c;
{
    USE(c);
    patlen = 0;
    return e_inc_search(F_UP_SEARCH_HIST);
}

/*ARGSUSED*/
CCRETVAL
e_copyprev(c)
    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(c)
    Char c;
{
    USE(c);
    /* do no editing */
    return(CC_NORM);
}

/*ARGSUSED*/
CCRETVAL
e_load_average(c)
    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(CGETS(5, 1, "Load average unavailable\n"));
    return(CC_REFRESH);
}

/*ARGSUSED*/
CCRETVAL
v_chgmeta(c)
    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(c)
    Char c;
{
    USE(c);
    return(v_action(TCSHOP_DELETE));
}


/*ARGSUSED*/
CCRETVAL
v_endword(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    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(c)
    Char c;
{
    USE(c);
    return v_search(F_UP_SEARCH_HIST);
}

/*ARGSUSED*/
CCRETVAL
v_dsh_meta(c)
    Char c;
{
    USE(c);
    return v_search(F_DOWN_SEARCH_HIST);
}

/*ARGSUSED*/
CCRETVAL
v_rsrch_fwd(c)
    Char c;
{
    USE(c);
    if (patlen == 0) return(CC_ERROR);
    return(v_repeat_srch(searchdir));
}

/*ARGSUSED*/
CCRETVAL
v_rsrch_back(c)
    Char c;
{
    USE(c);
    if (patlen == 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(c)
    Char c;
{
    USE(c);
    return CC_ERROR;
}

CCRETVAL
e_paste_from_clipboard(c)
    Char c;
{
    USE(c);
    return (CC_ERROR);
}

CCRETVAL
e_dosify_next(c)
    Char c;
{
    USE(c);
    return (CC_ERROR);
}
CCRETVAL
e_dosify_prev(c)
    Char c;
{
    USE(c);
    return (CC_ERROR);
}
CCRETVAL
e_page_up(c)
    Char c;
{
    USE(c);
    return (CC_ERROR);
}
CCRETVAL
e_page_down(c)
    Char c;
{
    USE(c);
    return (CC_ERROR);
}
#endif /* !WINNT_NATIVE */

#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

Generated by  Doxygen 1.6.0   Back to index