Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/* vi:set ts=8 sts=4 sw=4: */
/*
 * The following software is (C) 1984 Peter da Silva, the Mad Australian, in
 * the public domain. It may be re-distributed for any purpose with the
 * inclusion of this notice.
 */

/* Modified by Bram Moolenaar for use with VIM - Vi Improved. */
/* A few bugs removed by Olaf 'Rhialto' Seibert. */

/* TERMLIB: Terminal independent database. */

#include "vim.h"
#include "termlib.pro"

#if !defined(AMIGA) && !defined(VMS) && !defined(MACOS) && !defined(RISCOS)
# include <sgtty.h>
#endif

static int  getent __ARGS((char *, char *, FILE *, int));
static int  nextent __ARGS((char *, FILE *, int));
static int  _match __ARGS((char *, char *));
static char *_addfmt __ARGS((char *, char *, int));
static char *_find __ARGS((char *, char *));

/*
 * Global variables for termlib
 */

char    *tent;                /* Pointer to terminal entry, set by tgetent */
char    PC = 0;               /* Pad character, default NULL */
char    *UP = 0, *BC = 0;     /* Pointers to UP and BC strings from database */
short   ospeed;               /* Baud rate (1-16, 1=300, 16=19200), as in stty */

/*
 * Module: tgetent
 *
 * Purpose: Get termcap entry for <term> into buffer at <tbuf>.
 *
 * Calling conventions: char tbuf[TBUFSZ+], term=canonical name for terminal.
 *
 * Returned values: 1 = success, -1 = can't open file,
 *          0 = can't find terminal.
 *
 * Notes:
 * - Should probably supply static buffer.
 * - Uses environment variables "TERM" and "TERMCAP". If TERM = term (that is,
 *   if the argument matches the environment) then it looks at TERMCAP.
 * - If TERMCAP begins with a slash, then it assumes this is the file to
 *   search rather than /etc/termcap.
 * - If TERMCAP does not begin with a slash, and it matches TERM, then this is
 *   used as the entry.
 * - This could be simplified considerably for non-UNIX systems.
 */

#ifndef TERMCAPFILE
# ifdef AMIGA
#  define TERMCAPFILE "s:termcap"
# else
#  ifdef VMS
#   define TERMCAPFILE "VIMRUNTIME:termcap"
#  else
#   define TERMCAPFILE "/etc/termcap"
#  endif
# endif
#endif

    int
tgetent(tbuf, term)
    char    *tbuf;              /* Buffer to hold termcap entry, TBUFSZ bytes max */
    char    *term;              /* Name of terminal */
{
    char    tcbuf[32];          /* Temp buffer to handle */
    char    *tcptr = tcbuf;     /* extended entries */
    char    *tcap = TERMCAPFILE; /* Default termcap file */
    char    *tmp;
    FILE    *termcap;
    int     retval = 0;
    int     len;

    if ((tmp = (char *)mch_getenv((char_u *)"TERMCAP")) != NULL)
    {
        if (*tmp == '/')                /* TERMCAP = name of termcap file */
        {
            tcap = tmp ;
#if defined(AMIGA)
            /* Convert /usr/share/lib/termcap to usr:share/lib/termcap */
            tcap++;
            tmp = strchr(tcap, '/');
            if (tmp)
                *tmp = ':';
#endif
        }
        else                            /* TERMCAP = termcap entry itself */
        {
            int tlen = strlen(term);

            while (*tmp && *tmp != ':')         /* Check if TERM matches */
            {
                char *nexttmp;

                while (*tmp == '|')
                    tmp++;
                nexttmp  = _find(tmp, ":|");    /* Rhialto */
                if (tmp+tlen == nexttmp && _match(tmp, term) == tlen)
                {
                    strcpy(tbuf, tmp);
                    tent = tbuf;
                    return 1;
                }
                else
                    tmp = nexttmp;
            }
        }
    }
    if (!(termcap = mch_fopen(tcap, "r")))
    {
        strcpy(tbuf, tcap);
        return -1;
    }

    len = 0;
    while (getent(tbuf + len, term, termcap, TBUFSZ - len))
    {
        tcptr = tcbuf;                          /* Rhialto */
        if ((term = tgetstr("tc", &tcptr)))     /* extended entry */
        {
            rewind(termcap);
            len = strlen(tbuf);
        }
        else
        {
            retval = 1;
            tent = tbuf;        /* reset it back to the beginning */
            break;
        }
    }
    fclose(termcap);
    return retval;
}

    static int
getent(tbuf, term, termcap, buflen)
    char    *tbuf, *term;
    FILE    *termcap;
    int     buflen;
{
    char    *tptr;
    int     tlen = strlen(term);

    while (nextent(tbuf, termcap, buflen))      /* For each possible entry */
    {
        tptr = tbuf;
        while (*tptr && *tptr != ':')           /* : terminates name field */
        {
            char    *nexttptr;

            while (*tptr == '|')                /* | separates names */
                tptr++;
            nexttptr = _find(tptr, ":|");       /* Rhialto */
            if (tptr + tlen == nexttptr &&
                _match(tptr, term) == tlen)     /* FOUND! */
            {
                tent = tbuf;
                return 1;
            }
            else                                /* Look for next name */
                tptr = nexttptr;
        }
    }
    return 0;
}

    static int
nextent(tbuf, termcap, buflen)          /* Read 1 entry from TERMCAP file */
    char    *tbuf;
    FILE    *termcap;
    int     buflen;
{
    char *lbuf = tbuf;                          /* lbuf=line buffer */
                                /* read lines straight into buffer */

    while (lbuf < tbuf+buflen &&                /* There's room and */
          fgets(lbuf, (int)(tbuf+buflen-lbuf), termcap)) /* another line */
    {
        int llen = strlen(lbuf);

        if (*lbuf == '#')                       /* eat comments */
            continue;
        if (lbuf[-1] == ':' &&                  /* and whitespace */
            lbuf[0] == '\t' &&
            lbuf[1] == ':')
        {
            strcpy(lbuf, lbuf+2);
            llen -= 2;
        }
        if (lbuf[llen-2] == '\\')               /* and continuations */
            lbuf += llen-2;
        else
        {
            lbuf[llen-1]=0;                     /* no continuation, return */
            return 1;
        }
    }

    return 0;                                   /* ran into end of file */
}

/*
 * Module: tgetflag
 *
 * Purpose: returns flag true or false as to the existence of a given entry.
 * used with 'bs', 'am', etc...
 *
 * Calling conventions: id is the 2 character capability id.
 *
 * Returned values: 1 for success, 0 for failure.
 */

    int
tgetflag(id)
    char *id;
{
    char    buf[256], *ptr = buf;

    return tgetstr(id, &ptr) ? 1 : 0;
}

/*
 * Module: tgetnum
 *
 * Purpose: get numeric value such as 'li' or 'co' from termcap.
 *
 * Calling conventions: id = 2 character id.
 *
 * Returned values: -1 for failure, else numerical value.
 */

    int
tgetnum(id)
    char *id;
{
    char *ptr, buf[256];
    ptr = buf;

    if (tgetstr(id, &ptr))
        return atoi(buf);
    else
        return 0;
}

/*
 * Module: tgetstr
 *
 * Purpose: get terminal capability string from database.
 *
 * Calling conventions: id is the two character capability id.
 *          (*buf) points into a hold buffer for the
 *          id. the capability is copied into the buffer
 *          and (*buf) is advanced to point to the next
 *          free byte in the buffer.
 *
 * Returned values: 0 = no such entry, otherwise returns original
 *          (*buf) (now a pointer to the string).
 *
 * Notes
 *      It also decodes certain escape sequences in the buffer.
 *  they should be obvious from the code:
 *      \E = escape.
 *      \n, \r, \t, \f, \b match the 'c' escapes.
 *      ^x matches control-x (^@...^_).
 *      \nnn matches nnn octal.
 *      \x, where x is anything else, matches x. I differ
 *  from the standard library here, in that I allow ^: to match
 *  :.
 *
 */

    char *
tgetstr(id, buf)
    char        *id, **buf;
{
    int         len = strlen(id);
    char        *tmp=tent;
    char        *hold;
    int         i;

    do {
        tmp = _find(tmp, ":");                  /* For each field */
        while (*tmp == ':')                     /* skip empty fields */
            tmp++;
        if (!*tmp)
            break;

        if (_match(id, tmp) == len) {
            tmp += len;                         /* find '=' '@' or '#' */
            if (*tmp == '@')                    /* :xx@: entry for tc */
                return 0;                       /* deleted entry */
            hold= *buf;
            while (*++tmp && *tmp != ':') {     /* not at end of field */
                switch(*tmp) {
                case '\\':                      /* Expand escapes here */
                    switch(*++tmp) {
                    case 0:                      /* ignore backslashes */
                        tmp--;                  /* at end of entry */
                        break;                  /* shouldn't happen */
                    case 'e':
                    case 'E':                   /* ESC */
                        *(*buf)++ = ESC;
                        break;
                    case 'n':                   /* \n */
                        *(*buf)++ = '\n';
                        break;
                    case 'r':                   /* \r */
                        *(*buf)++ = '\r';
                        break;
                    case 't':                   /* \t */
                        *(*buf)++ = '\t';
                        break;
                    case 'b':                   /* \b */
                        *(*buf)++ = '\b';
                        break;
                    case 'f':                   /* \f */
                        *(*buf)++ = '\f';
                        break;
                    case '0':                   /* \nnn */
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        **buf = 0;
                            /* get up to three digits */
                        for (i = 0; i < 3 && VIM_ISDIGIT(*tmp); ++i)
                            **buf = **buf * 8 + *tmp++ - '0';
                        (*buf)++;
                        tmp--;
                        break;
                    default:                    /* \x, for all other x */
                        *(*buf)++= *tmp;
                    }
                    break;
                case '^':                       /* control characters */
                    ++tmp;
                    *(*buf)++ = Ctrl_chr(*tmp);
                    break;
                default:
                    *(*buf)++ = *tmp;
                }
            }
            *(*buf)++ = 0;
            return hold;
        }
    } while (*tmp);

    return 0;
}

/*
 * Module: tgoto
 *
 * Purpose: decode cm cursor motion string.
 *
 * Calling conventions: cm is cursor motion string.  line, col, are the
 * desired destination.
 *
 * Returned values: a string pointing to the decoded string, or "OOPS" if it
 * cannot be decoded.
 *
 * Notes
 *      The accepted escapes are:
 *      %d       as in printf, 0 origin.
 *      %2, %3   like %02d, %03d in printf.
 *      %.       like %c
 *      %+x      adds <x> to value, then %.
 *      %>xy     if value>x, adds y. No output.
 *      %i       increments line& col, no output.
 *      %r       reverses order of line&col. No output.
 *      %%       prints as a single %.
 *      %n       exclusive or row & col with 0140.
 *      %B       BCD, no output.
 *      %D       reverse coding (x-2*(x%16)), no output.
 */

    char *
tgoto(cm, col, line)
    char        *cm;                            /* cm string, from termcap */
    int col,                                    /* column, x position */
    line;                                       /* line, y position */
{
    char    gx, gy,                             /* x, y */
        *ptr,                                   /* pointer in 'cm' */
        reverse = 0,                            /* reverse flag */
        *bufp,                                  /* pointer in returned string */
        addup = 0,                              /* add upline */
        addbak = 0,                             /* add backup */
        c;
    static char buffer[32];

    if (!cm)
        return "OOPS";                          /* Kludge, but standard */

    bufp = buffer;
    ptr = cm;

    while (*ptr) {
        if ((c = *ptr++) != '%') {              /* normal char */
            *bufp++ = c;
        } else {                                /* % escape */
            switch(c = *ptr++) {
            case 'd':                           /* decimal */
                bufp = _addfmt(bufp, "%d", line);
                line = col;
                break;
            case '2':                           /* 2 digit decimal */
                bufp = _addfmt(bufp, "%02d", line);
                line = col;
                break;
            case '3':                           /* 3 digit decimal */
                bufp = _addfmt(bufp, "%03d", line);
                line = col;
                break;
            case '>':                           /* %>xy: if >x, add y */
                gx = *ptr++;
                gy = *ptr++;
                if (col>gx) col += gy;
                if (line>gx) line += gy;
                break;
            case '+':                           /* %+c: add c */
                line += *ptr++;
            case '.':                           /* print x/y */
                if (line == '\t' ||             /* these are */
                   line == '\n' ||              /* chars that */
                   line == '\004' ||            /* UNIX hates */
                   line == '\0') {
                    line++;                     /* so go to next pos */
                    if (reverse == (line == col))
                        addup=1;                /* and mark UP */
                    else
                        addbak=1;               /* or BC */
                }
                *bufp++=line;
                line = col;
                break;
            case 'r':                           /* r: reverse */
                gx = line;
                line = col;
                col = gx;
                reverse = 1;
                break;
            case 'i':                   /* increment (1-origin screen) */
                col++;
                line++;
                break;
            case '%':                           /* %%=% literally */
                *bufp++='%';
                break;
            case 'n':                           /* magic DM2500 code */
                line ^= 0140;
                col ^= 0140;
                break;
            case 'B':                           /* bcd encoding */
                line = line/10<<4+line%10;
                col = col/10<<4+col%10;
                break;
            case 'D':                           /* magic Delta Data code */
                line = line-2*(line&15);
                col = col-2*(col&15);
                break;
            default:                            /* Unknown escape */
                return "OOPS";
            }
        }
    }

    if (addup)                                  /* add upline */
        if (UP) {
            ptr=UP;
            while (VIM_ISDIGIT(*ptr) || *ptr == '.')
                ptr++;
            if (*ptr == '*')
                ptr++;
            while (*ptr)
                *bufp++ = *ptr++;
        }

    if (addbak)                                 /* add backspace */
        if (BC) {
            ptr=BC;
            while (VIM_ISDIGIT(*ptr) || *ptr == '.')
                ptr++;
            if (*ptr == '*')
                ptr++;
            while (*ptr)
                *bufp++ = *ptr++;
        }
        else
            *bufp++='\b';

    *bufp = 0;

    return(buffer);
}

/*
 * Module: tputs
 *
 * Purpose: decode padding information
 *
 * Calling conventions: cp = string to be padded, affcnt = # of items affected
 *      (lines, characters, whatever), outc = routine to output 1 character.
 *
 * Returned values: none
 *
 * Notes
 *      cp has padding information ahead of it, in the form
 *  nnnTEXT or nnn*TEXT. nnn is the number of milliseconds to delay,
 *  and may be a decimal (nnn.mmm). If the asterisk is given, then
 *  the delay is multiplied by afcnt. The delay is produced by outputting
 *  a number of nulls (or other padding char) after printing the
 *  TEXT.
 *
 */

long _bauds[16]={
    0,  50, 75, 110,
    134,    150,    200,    300,
    600,    1200,   1800,   2400,
    4800,   9600,   19200,  19200 };

    int
tputs(cp, affcnt, outc)
    char *cp;                           /* string to print */
    int affcnt;                         /* Number of lines affected */
    void (*outc) __ARGS((unsigned int));/* routine to output 1 character */
{
    long    frac,                       /* 10^(#digits after decimal point) */
        counter,                        /* digits */
        atol __ARGS((const char *));

    if (VIM_ISDIGIT(*cp)) {
        counter = 0;
        frac = 1000;
        while (VIM_ISDIGIT(*cp))
            counter = counter * 10L + (long)(*cp++ - '0');
        if (*cp == '.')
            while (VIM_ISDIGIT(*++cp)) {
                counter = counter * 10L + (long)(*cp++ - '0');
                frac = frac * 10;
            }
        if (*cp!='*') {                 /* multiply by affected lines */
            if (affcnt>1) affcnt = 1;
        }
        else
            cp++;

        /* Calculate number of characters for padding counter/frac ms delay */
        if (ospeed)
            counter = (counter * _bauds[ospeed] * (long)affcnt) / frac;

        while (*cp)                     /* output string */
            (*outc)(*cp++);
        if (ospeed)
            while (counter--)           /* followed by pad characters */
                (*outc)(PC);
    }
    else
        while (*cp)
            (*outc)(*cp++);
    return 0;
}

/*
 * Module: tutil.c
 *
 * Purpose: Utility routines for TERMLIB functions.
 *
 */
    static int
_match(s1, s2)          /* returns length of text common to s1 and s2 */
    char *s1, *s2;
{
    int i = 0;

    while (s1[i] && s1[i] == s2[i])
        i++;

    return i;
}

/*
 * finds next c in s that's a member of set, returns pointer
 */
    static char *
_find(s, set)
    char *s, *set;
{
    for(; *s; s++)
    {
        char    *ptr = set;

        while (*ptr && *s != *ptr)
            ptr++;

        if (*ptr)
            return s;
    }

    return s;
}

/*
 * add val to buf according to format fmt
 */
    static char *
_addfmt(buf, fmt, val)
    char *buf, *fmt;
    int val;
{
    sprintf(buf, fmt, val);
    while (*buf)
        buf++;
    return buf;
}