Logo Search packages:      
Sourcecode: tcsh version File versions

ed.xmap.c

/* $Header: /src/pub/tcsh/ed.xmap.c,v 3.25 2002/03/08 17:36:45 christos Exp $ */
/*
 * ed.xmap.c: This module contains the procedures for maintaining
 *          the extended-key map.
 *
 *          An extended-key (Xkey) is a sequence of keystrokes
 *          introduced with an sequence introducer and consisting
 *          of an arbitrary number of characters.  This module maintains
 *          a map (the Xmap) to convert these extended-key sequences
 *          into input strings (XK_STR), editor functions (XK_CMD), or
 *          unix commands (XK_EXE). It contains the
 *          following externally visible functions.
 *
 *          int GetXkey(ch,val);
 *          CStr *ch;
 *          XmapVal *val;
 *
 *          Looks up *ch in map and then reads characters until a
 *          complete match is found or a mismatch occurs. Returns the
 *          type of the match found (XK_STR, XK_CMD, or XK_EXE).
 *          Returns NULL in val.str and XK_STR for no match.  
 *          The last character read is returned in *ch.
 *
 *          void AddXkey(Xkey, val, ntype);
 *          CStr *Xkey;
 *          XmapVal *val;
 *          int ntype;
 *
 *          Adds Xkey to the Xmap and associates the value in val with it.
 *          If Xkey is already is in Xmap, the new code is applied to the
 *          existing Xkey. Ntype specifies if code is a command, an
 *          out string or a unix command.
 *
 *            int DeleteXkey(Xkey);
 *            CStr *Xkey;
 *
 *          Delete the Xkey and all longer Xkeys staring with Xkey, if
 *          they exists.
 *
 *          Warning:
 *          If Xkey is a substring of some other Xkeys, then the longer
 *          Xkeys are lost!!  That is, if the Xkeys "abcd" and "abcef"
 *          are in Xmap, adding the key "abc" will cause the first two
 *          definitions to be lost.
 *
 *          void ResetXmap();
 *
 *          Removes all entries from Xmap and resets the defaults.
 *
 *          void PrintXkey(Xkey);
 *          CStr *Xkey;
 *
 *          Prints all extended keys prefixed by Xkey and their associated
 *          commands.
 *
 *          Restrictions:
 *          -------------
 *            1) It is not possible to have one Xkey that is a
 *             substring of another.
 */
/*-
 * 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.
 */
#include "sh.h"

RCSID("$Id: ed.xmap.c,v 3.25 2002/03/08 17:36:45 christos Exp $")

#include "ed.h"
#include "ed.defns.h"

#ifndef NULL
#define NULL 0
#endif

/* Internal Data types and declarations */

/* The Nodes of the Xmap.  The Xmap is a linked list of these node
 * elements
 */
typedef struct Xmapnode {
    Char    ch;               /* single character of Xkey */
    int     type;
    XmapVal val;        /* command code or pointer to string, if this
                         * is a leaf */
    struct Xmapnode *next;    /* ptr to next char of this Xkey */
    struct Xmapnode *sibling; /* ptr to another Xkey with same prefix */
} XmapNode;

static XmapNode *Xmap = NULL; /* the current Xmap */
#define MAXXKEY 100           /* max length of a Xkey for print putposes */
static Char printbuf[MAXXKEY];      /* buffer for printing */


/* Some declarations of procedures */
static      int       TraverseMap   __P((XmapNode *, CStr *, XmapVal *));
static      int       TryNode __P((XmapNode *, CStr *, XmapVal *, int));
static      XmapNode *GetFreeNode   __P((CStr *));
static      void    PutFreeNode     __P((XmapNode *));
static      int     TryDeleteNode   __P((XmapNode **, CStr *));
static      int     Lookup    __P((CStr *, XmapNode *, int));
static      int     Enumerate __P((XmapNode *, int));
static      int     unparsech __P((int, Char *));


XmapVal *
XmapCmd(cmd)
    int cmd;
{
    static XmapVal xm;
    xm.cmd = (KEYCMD) cmd;
    return &xm;
}

XmapVal *
XmapStr(str)
    CStr  *str;
{
    static XmapVal xm;
    xm.str.len = str->len;
    xm.str.buf = str->buf;
    return &xm;
}

/* ResetXmap():
 *    Takes all nodes on Xmap and puts them on free list.  Then
 *    initializes Xmap with arrow keys
 */
void
ResetXmap()
{
    PutFreeNode(Xmap);
    Xmap = NULL;

    DefaultArrowKeys();
    return;
}


/* GetXkey():
 *    Calls the recursive function with entry point Xmap
 */
int
GetXkey(ch, val)
    CStr     *ch;
    XmapVal  *val;
{
    return (TraverseMap(Xmap, ch, val));
}

/* TraverseMap():
 *    recursively traverses node in tree until match or mismatch is
 *    found.  May read in more characters.
 */
static int
TraverseMap(ptr, ch, val)
    XmapNode *ptr;
    CStr     *ch;
    XmapVal  *val;
{
    Char    tch;

    if (ptr->ch == *(ch->buf)) {
      /* match found */
      if (ptr->next) {
          /* Xkey not complete so get next char */
          if (GetNextChar(&tch) != 1) {   /* if EOF or error */
            val->cmd = F_SEND_EOF;
            return XK_CMD;/* PWP: Pretend we just read an end-of-file */
          }
          *(ch->buf) = tch;
          return (TraverseMap(ptr->next, ch, val));
      }
      else {
          *val = ptr->val;
          if (ptr->type != XK_CMD)
            *(ch->buf) = '\0';
          return ptr->type;
      }
    }
    else {
      /* no match found here */
      if (ptr->sibling) {
          /* try next sibling */
          return (TraverseMap(ptr->sibling, ch, val));
      }
      else {
          /* no next sibling -- mismatch */
          val->str.buf = NULL;
          val->str.len = 0;
          return XK_STR;
      }
    }
}

void
AddXkey(Xkey, val, ntype)
    CStr    *Xkey;
    XmapVal *val;
    int      ntype;
{
    CStr cs;
    cs.buf = Xkey->buf;
    cs.len = Xkey->len;
    if (Xkey->len == 0) {
      xprintf(CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
      return;
    }

    if (ntype == XK_CMD && val->cmd == F_XKEY) {
      xprintf(CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
      return;
    }

    if (Xmap == NULL)
      /* tree is initially empty.  Set up new node to match Xkey[0] */
      Xmap = GetFreeNode(&cs);      /* it is properly initialized */

    /* Now recurse through Xmap */
    (void) TryNode(Xmap, &cs, val, ntype);      
    return;
}

static int
TryNode(ptr, str, val, ntype)
    XmapNode *ptr;
    CStr     *str;
    XmapVal  *val;
    int       ntype;
{
    /*
     * Find a node that matches *string or allocate a new one
     */
    if (ptr->ch != *(str->buf)) {
      XmapNode *xm;

      for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
          if (xm->sibling->ch == *(str->buf))
            break;
      if (xm->sibling == NULL)
          xm->sibling = GetFreeNode(str); /* setup new node */
      ptr = xm->sibling;
    }

    str->buf++;
    str->len--;
    if (str->len == 0) {
      /* we're there */
      if (ptr->next != NULL) {
          PutFreeNode(ptr->next);   /* lose longer Xkeys with this prefix */
          ptr->next = NULL;
      }

      switch (ptr->type) {
      case XK_STR:
      case XK_EXE:
          if (ptr->val.str.buf != NULL)
            xfree((ptr_t) ptr->val.str.buf);
          ptr->val.str.len = 0;
          break;
      case XK_NOD:
      case XK_CMD:
          break;
      default:
          abort();
          break;
      }

      switch (ptr->type = ntype) {
      case XK_CMD:
          ptr->val = *val;
          break;
      case XK_STR:
      case XK_EXE:
          ptr->val.str.len = (val->str.len + 1) * sizeof(Char);
          ptr->val.str.buf = (Char *) xmalloc((size_t) ptr->val.str.len);
          (void) memmove((ptr_t) ptr->val.str.buf, (ptr_t) val->str.buf,
                     (size_t) ptr->val.str.len);
          ptr->val.str.len = val->str.len;
          break;
      default:
          abort();
          break;
      }
    }
    else {
      /* still more chars to go */
      if (ptr->next == NULL)
          ptr->next = GetFreeNode(str);   /* setup new node */
      (void) TryNode(ptr->next, str, val, ntype);
    }
    return (0);
}

void
ClearXkey(map, in)
    KEYCMD *map;
    CStr   *in;
{
    unsigned char c = (unsigned char) *(in->buf);
    if ((map[c] == F_XKEY) &&
      ((map == CcKeyMap && CcAltMap[c] != F_XKEY) ||
       (map == CcAltMap && CcKeyMap[c] != F_XKEY)))
      (void) DeleteXkey(in);
}

int
DeleteXkey(Xkey)
    CStr   *Xkey;
{
    if (Xkey->len == 0) {
      xprintf(CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
      return (-1);
    }

    if (Xmap == NULL)
      return (0);

    (void) TryDeleteNode(&Xmap, Xkey);
    return (0);
}

static int
TryDeleteNode(inptr, str)
    XmapNode **inptr;
    CStr   *str;
{
    XmapNode *ptr;
    XmapNode *prev_ptr = NULL;

    ptr = *inptr;
    /*
     * Find a node that matches *string or allocate a new one
     */
    if (ptr->ch != *(str->buf)) {
      XmapNode *xm;

      for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
          if (xm->sibling->ch == *(str->buf))
            break;
      if (xm->sibling == NULL)
          return (0);
      prev_ptr = xm;
      ptr = xm->sibling;
    }

    str->buf++;
    str->len--;

    if (str->len == 0) {
      /* we're there */
      if (prev_ptr == NULL)
          *inptr = ptr->sibling;
      else
          prev_ptr->sibling = ptr->sibling;
      ptr->sibling = NULL;
      PutFreeNode(ptr);
      return (1);
    }
    else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
      if (ptr->next != NULL)
          return (0);
      if (prev_ptr == NULL)
          *inptr = ptr->sibling;
      else
          prev_ptr->sibling = ptr->sibling;
      ptr->sibling = NULL;
      PutFreeNode(ptr);
      return (1);
    }
    else {
      return (0);
    }
}

/* PutFreeNode():
 *    Puts a tree of nodes onto free list using free(3).
 */
static void
PutFreeNode(ptr)
    XmapNode *ptr;
{
    if (ptr == NULL)
      return;

    if (ptr->next != NULL) {
      PutFreeNode(ptr->next);
      ptr->next = NULL;
    }

    PutFreeNode(ptr->sibling);

    switch (ptr->type) {
    case XK_CMD:
    case XK_NOD:
      break;
    case XK_EXE:
    case XK_STR:
      if (ptr->val.str.buf != NULL)
          xfree((ptr_t) ptr->val.str.buf);
      break;
    default:
      abort();
      break;
    }
    xfree((ptr_t) ptr);
}


/* GetFreeNode():
 *    Returns pointer to an XmapNode for ch.
 */
static XmapNode *
GetFreeNode(ch)
    CStr *ch;
{
    XmapNode *ptr;

    ptr = (XmapNode *) xmalloc((size_t) sizeof(XmapNode));
    ptr->ch = ch->buf[0];
    ptr->type = XK_NOD;
    ptr->val.str.buf = NULL;
    ptr->val.str.len = 0;
    ptr->next = NULL;
    ptr->sibling = NULL;
    return (ptr);
}
 

/* PrintXKey():
 *    Print the binding associated with Xkey key.
 *    Print entire Xmap if null
 */
void
PrintXkey(key)
    CStr   *key;
{
    CStr cs;

    if (key) {
      cs.buf = key->buf;
      cs.len = key->len;
    }
    else {
      cs.buf = STRNULL;
      cs.len = 0;
    }
    /* do nothing if Xmap is empty and null key specified */
    if (Xmap == NULL && cs.len == 0)
      return;

    printbuf[0] =  '"';
    if (Lookup(&cs, Xmap, 1) <= -1)
      /* key is not bound */
      xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf);
    return;
}

/* Lookup():
 *    look for the string starting at node ptr.
 *    Print if last node
 */
static int
Lookup(str, ptr, cnt)
    CStr   *str;
    XmapNode *ptr;
    int     cnt;
{
    int     ncnt;

    if (ptr == NULL)
      return (-1);            /* cannot have null ptr */

    if (str->len == 0) {
      /* no more chars in string.  Enumerate from here. */
      (void) Enumerate(ptr, cnt);
      return (0);
    }
    else {
      /* If match put this char into printbuf.  Recurse */
      if (ptr->ch == *(str->buf)) {
          /* match found */
          ncnt = unparsech(cnt, &ptr->ch);
          if (ptr->next != NULL) {
            /* not yet at leaf */
            CStr tstr;
            tstr.buf = str->buf + 1;
            tstr.len = str->len - 1;
            return (Lookup(&tstr, ptr->next, ncnt + 1));
          }
          else {
            /* next node is null so key should be complete */
            if (str->len == 1) {
                CStr pb;
                printbuf[ncnt + 1] = '"';
                printbuf[ncnt + 2] = '\0';
                pb.buf = printbuf;
                pb.len = ncnt + 2;
                (void) printOne(&pb, &ptr->val, ptr->type);
                return (0);
            }
            else
                return (-1);/* mismatch -- string still has chars */
          }
      }
      else {
          /* no match found try sibling */
          if (ptr->sibling)
            return (Lookup(str, ptr->sibling, cnt));
          else
            return (-1);
      }
    }
}

static int
Enumerate(ptr, cnt)
    XmapNode *ptr;
    int     cnt;
{
    int     ncnt;

    if (cnt >= MAXXKEY - 5) { /* buffer too small */
      printbuf[++cnt] = '"';
      printbuf[++cnt] = '\0';
      xprintf(CGETS(9, 5,
            "Some extended keys too long for internal print buffer"));
      xprintf(" \"%S...\"\n", printbuf);
      return (0);
    }

    if (ptr == NULL) {
#ifdef DEBUG_EDIT
      xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
#endif
      return (-1);
    }

    ncnt = unparsech(cnt, &ptr->ch); /* put this char at end of string */
    if (ptr->next == NULL) {
      CStr pb;
      /* print this Xkey and function */
      printbuf[++ncnt] = '"';
      printbuf[++ncnt] = '\0';
      pb.buf = printbuf;
      pb.len = ncnt;
      (void) printOne(&pb, &ptr->val, ptr->type);
    }
    else
      (void) Enumerate(ptr->next, ncnt + 1);

    /* go to sibling if there is one */
    if (ptr->sibling)
      (void) Enumerate(ptr->sibling, cnt);
    return (0);
}


/* PrintOne():
 *    Print the specified key and its associated
 *    function specified by val
 */
int
printOne(key, val, ntype)
    CStr    *key;
    XmapVal *val;
    int      ntype;
{
    struct KeyFuncs *fp;
    unsigned char unparsbuf[200];
    static char *fmt = "%s\n";

    xprintf("%-15S-> ", key->buf);
    if (val != NULL)
      switch (ntype) {
      case XK_STR:
      case XK_EXE:
          xprintf(fmt, unparsestring(&val->str, unparsbuf, 
                               ntype == XK_STR ? STRQQ : STRBB));
          break;
      case XK_CMD:
          for (fp = FuncNames; fp->name; fp++)
            if (val->cmd == fp->func)
                xprintf(fmt, fp->name);
            break;
      default:
          abort();
          break;
      }
    else
      xprintf(fmt, key, CGETS(9, 7, "no input"));
    return (0);
}

static int
unparsech(cnt, ch)
    int   cnt;
    Char  *ch;
{
    if (ch == 0) {
      printbuf[cnt++] = '^';
      printbuf[cnt] = '@';
      return cnt;
    }

    if (Iscntrl(*ch)) {
#ifdef IS_ASCII
      printbuf[cnt++] = '^';
      if (*ch == CTL_ESC('\177'))
          printbuf[cnt] = '?';
      else
          printbuf[cnt] = *ch | 0100;
#else
      if (*ch == CTL_ESC('\177'))
      {
            printbuf[cnt++] = '^';
            printbuf[cnt] = '?';
      }
      else if (Isupper(_toebcdic[_toascii[*ch]|0100])
            || strchr("@[\\]^_", _toebcdic[_toascii[*ch]|0100]) != NULL)
      {
            printbuf[cnt++] = '^';
            printbuf[cnt] = _toebcdic[_toascii[*ch]|0100];
      }
      else
      {
            printbuf[cnt++] = '\\';
            printbuf[cnt++] = ((*ch >> 6) & 7) + '0';
            printbuf[cnt++] = ((*ch >> 3) & 7) + '0';
            printbuf[cnt] = (*ch & 7) + '0';
      }
#endif
    }
    else if (*ch == '^') {
      printbuf[cnt++] = '\\';
      printbuf[cnt] = '^';
    }
    else if (*ch == '\\') {
      printbuf[cnt++] = '\\';
      printbuf[cnt] = '\\';
    }
    else if (*ch == ' ' || (Isprint(*ch) && !Isspace(*ch))) {
      printbuf[cnt] = *ch;
    }
    else {
      printbuf[cnt++] = '\\';
      printbuf[cnt++] = ((*ch >> 6) & 7) + '0';
      printbuf[cnt++] = ((*ch >> 3) & 7) + '0';
      printbuf[cnt] = (*ch & 7) + '0';
    }
    return cnt;
}

int
parseescape(ptr)
    const Char  **ptr;
{
    const Char *p;
    Char c;

    p = *ptr;

    if ((p[1] & CHAR) == 0) {
      xprintf(CGETS(9, 8, "Something must follow: %c\n"), *p);
      return -1;
    }
    if ((*p & CHAR) == '\\') {
      p++;
      switch (*p & CHAR) {
      case 'a':
          c = CTL_ESC('\007');         /* Bell */
          break;
      case 'b':
          c = CTL_ESC('\010');         /* Backspace */
          break;
      case 'e':
          c = CTL_ESC('\033');         /* Escape */
          break;
      case 'f':
          c = CTL_ESC('\014');         /* Form Feed */
          break;
      case 'n':
          c = CTL_ESC('\012');         /* New Line */
          break;
      case 'r':
          c = CTL_ESC('\015');         /* Carriage Return */
          break;
      case 't':
          c = CTL_ESC('\011');         /* Horizontal Tab */
          break;
      case 'v':
          c = CTL_ESC('\013');         /* Vertical Tab */
          break;
      case '\\':
          c = '\\';
          break;
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
          {
            register int cnt, val, ch;

            for (cnt = 0, val = 0; cnt < 3; cnt++) {
                ch = *p++ & CHAR;
                if (ch < '0' || ch > '7') {
                  p--;
                  break;
                }
                val = (val << 3) | (ch - '0');
            }
            if ((val & 0xffffff00) != 0) {
                xprintf(CGETS(9, 9,
                      "Octal constant does not fit in a char.\n"));
                return 0;
            }
#ifndef IS_ASCII
            if (CTL_ESC(val) != val && adrof(STRwarnebcdic))
                xprintf(/*CGETS(9, 9, no NLS-String yet!*/
                      "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/);
#endif
            c = (Char) val;
            --p;
          }
          break;
      default:
          c = *p;
          break;
      }
    }
    else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) || 
                            strchr("@^_?\\|[{]}", p[1] & CHAR))) {
      p++;
#ifdef IS_ASCII
      c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
#else
      c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
      if (adrof(STRwarnebcdic))
          xprintf(/*CGETS(9, 9, no NLS-String yet!*/
            "Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/);
#endif
    }
    else
      c = *p;
    *ptr = p;
    return (c);
}


unsigned char *
unparsestring(str, buf, sep)
    CStr   *str;
    unsigned char *buf;
    Char   *sep;
{
    unsigned char *b;
    Char   p;
    int l;

    b = buf;
    if (sep[0])
#ifndef WINNT_NATIVE
      *b++ = sep[0];
#else /* WINNT_NATIVE */
      *b++ = CHAR & sep[0];
#endif /* !WINNT_NATIVE */

    for (l = 0; l < str->len; l++) {
      p = str->buf[l];
      if (Iscntrl(p)) {
#ifdef IS_ASCII
          *b++ = '^';
          if (p == CTL_ESC('\177'))
            *b++ = '?';
          else
            *b++ = (unsigned char) (p | 0100);
#else
          if (_toascii[p] == '\177' || Isupper(_toebcdic[_toascii[p]|0100])
             || strchr("@[\\]^_", _toebcdic[_toascii[p]|0100]) != NULL)
          {
            *b++ = '^';
            *b++ = (_toascii[p] == '\177') ? '?' : _toebcdic[_toascii[p]|0100];
          }
          else
          {
            *b++ = '\\';
            *b++ = ((p >> 6) & 7) + '0';
            *b++ = ((p >> 3) & 7) + '0';
            *b++ = (p & 7) + '0';
          }
#endif
      }
      else if (p == '^' || p == '\\') {
          *b++ = '\\';
          *b++ = (unsigned char) p;
      }
      else if (p == ' ' || (Isprint(p) && !Isspace(p))) {
          *b++ = (unsigned char) p;
      }
      else {
          *b++ = '\\';
          *b++ = ((p >> 6) & 7) + '0';
          *b++ = ((p >> 3) & 7) + '0';
          *b++ = (p & 7) + '0';
      }
    }
    if (sep[0] && sep[1])
#ifndef WINNT_NATIVE
      *b++ = sep[1];
#else /* WINNT_NATIVE */
      *b++ = CHAR & sep[1];
#endif /* !WINNT_NATIVE */
    *b++ = 0;
    return buf;               /* should check for overflow */
}

Generated by  Doxygen 1.6.0   Back to index