home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / util / vim-2.0.lha / Vim-2.0 / src / termlib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-13  |  15.2 KB  |  647 lines

  1. /* vi:sw=4:ts=4:
  2.    The following software is (C) 1984 Peter da Silva,
  3.    the Mad Australian, in the public domain. It may
  4.    be re-distributed for any purpose with the inclusion
  5.    of this notice. */
  6. /* modified by Bram Moolenaar */
  7.  
  8. /* TERMLIB: Terminal independant database. */
  9.  
  10. #include "vim.h"
  11. #include "proto.h"
  12. #include "proto/termlib.pro"
  13.  
  14. #ifndef AMIGA
  15. # include <sgtty.h>
  16. #endif
  17.  
  18. static int    getent __PARMS((char *, char *, FILE *, int));
  19. static int    nextent __PARMS((char *, FILE *, int));
  20. static int    _match __PARMS((char *, char *));
  21. static char    *_addfmt __PARMS((char *, char *, int));
  22. static char    *_find __PARMS((char *, char *));
  23.  
  24. /*
  25.  * Global variables for termlib
  26.  */
  27.  
  28. char    *tent;                /* Pointer to terminal entry, set by tgetent */
  29. char    PC = 0;               /* Pad character, default NULL */
  30. char    *UP = 0, *BC = 0;     /* Pointers to UP and BC strings from database */
  31. short    ospeed;               /* Baud rate (1-16, 1=300, 16=19200), as in stty */
  32.  
  33. /*
  34.  * Module: tgetent
  35.  *
  36.  * Purpose: Get termcap entry for <term> into buffer at <tbuf>.
  37.  *
  38.  * Calling conventions: char tbuf[TBUFSZ+], term=canonical name for
  39.  *            terminal.
  40.  *
  41.  * Returned values: 1 = success, -1 = can't open file,
  42.  *            0 = can't find terminal.
  43.  *
  44.  * Notes
  45.  *        Should probably supply static buffer.
  46.  *
  47.  *        Uses environment variables "TERM" and
  48.  *    "TERMCAP". If TERM = term (that is, if the argument
  49.  *    matches the environment) then it looks at TERMCAP.
  50.  *        If TERMCAP begins with a slash, then it assumes
  51.  *    this is the file to search rather than /etc/termcap.
  52.  *        If TERMCAP does not begin with a slash, and it
  53.  *    matches TERM, then this is used as the entry.
  54.  *
  55.  *        This could be simplified considerably for non-UNIX
  56.  *    systems.
  57.  */
  58.  
  59. #ifdef AMIGA
  60. # define TERMCAPFILE "s:termcap"
  61. #else
  62. # define TERMCAPFILE "/etc/termcap"
  63. #endif
  64.  
  65. tgetent(tbuf, term)
  66.     char    *tbuf;               /* Buffer to hold termcap entry, TBUFSZ bytes max */
  67.     char    *term;               /* Name of terminal */
  68. {
  69.     char    tcbuf[32];           /* Temp buffer to handle */
  70.     char    *tcptr = tcbuf;      /* extended entries */
  71.     char    *tcap = TERMCAPFILE; /* Default termcap file */
  72.     char    *tmp;
  73.     FILE    *termcap;
  74.     int        retval = 0;
  75.     int        len;
  76.  
  77.     if ((tmp = (char *)vimgetenv("TERMCAP")) != NULL)
  78.     {
  79.         if (*tmp == '/')            /* TERMCAP = name of termcap file */
  80.             tcap = tmp ;
  81.         else                        /* TERMCAP = termcap entry itself */
  82.         {
  83.             int tlen = strlen(term);
  84.  
  85.             while (*tmp && *tmp != ':') /* Check if TERM matches */
  86.             {
  87.                 while (*tmp == '|')
  88.                     tmp++;
  89.                 if (_match(tmp, term) == tlen)
  90.                 {
  91.                     strcpy(tbuf, tmp);
  92.                     tent = tbuf;
  93.                     return 1;
  94.                 } 
  95.                 else
  96.                     tmp = _find(tmp, ":|");
  97.             }
  98.         }
  99.     }
  100.     if (!(termcap = fopen(tcap, "r")))
  101.     {
  102.         strcpy(tbuf, tcap);
  103.         return -1;
  104.     }
  105.  
  106.     len = 0;
  107.     while (getent(tbuf + len, term, termcap, TBUFSZ - len))
  108.     {
  109.         if ((term = tgetstr("tc", &tcptr)))         /* extended entry */
  110.         {
  111.             rewind(termcap);
  112.             len = strlen(tbuf);
  113.         }
  114.         else
  115.         {
  116.             retval = 1; 
  117.             tent = tbuf;
  118.             break;
  119.         }
  120.     }
  121.     fclose(termcap);
  122.     return retval;
  123. }
  124.  
  125.     static int
  126. getent(tbuf, term, termcap, buflen)
  127.     char    *tbuf, *term;
  128.     FILE    *termcap;
  129.     int        buflen;
  130. {
  131.     char    *tptr;
  132.     int        tlen = strlen(term);
  133.  
  134.     while (nextent(tbuf, termcap, buflen))   /* For each possible entry */
  135.     {
  136.         tptr = tbuf;
  137.         while (*tptr && *tptr != ':')    /* : terminates name field */
  138.         {
  139.             while (*tptr == '|')             /* | seperates names */
  140.                 tptr++;
  141.             if (_match(tptr, term) == tlen)             /* FOUND! */
  142.             {
  143.                 tent = tbuf;
  144.                 return 1;
  145.             } 
  146.             else                           /* Look for next name */
  147.                 tptr = _find(tptr, ":|");
  148.         }
  149.     }
  150.     return 0;
  151. }
  152.  
  153.     static int
  154. nextent(tbuf, termcap, buflen)         /* Read 1 entry from TERMCAP file */
  155.     char    *tbuf;
  156.     FILE    *termcap;
  157.     int        buflen;
  158. {
  159.     char *lbuf = tbuf;           /* lbuf=line buffer */
  160.                                  /* read lines straight into buffer */
  161.  
  162.     while (lbuf < tbuf+buflen &&                        /* There's room and */
  163.           fgets(lbuf, (int)(tbuf+buflen-lbuf), termcap))        /* another line */
  164.     {
  165.         int llen = strlen(lbuf);
  166.  
  167.         if (*lbuf == '#')                               /* eat comments */
  168.             continue;
  169.         if (lbuf[-1] == ':' &&                        /* and whitespace */
  170.             lbuf[0] == '\t' &&
  171.             lbuf[1] == ':')
  172.         {
  173.             strcpy(lbuf, lbuf+2);
  174.             llen -= 2;
  175.         }
  176.         if (lbuf[llen-2] == '\\')                  /* and continuations */
  177.             lbuf += llen-2;
  178.         else
  179.         {
  180.             lbuf[llen-1]=0;           /* no continuation, return */
  181.             return 1;
  182.         }
  183.     }
  184.  
  185.     return 0;                                    /* ran into end of file */
  186. }
  187.  
  188. /*
  189.  * Module: tgetflag
  190.  *
  191.  * Purpose: returns flag true or false as to the existence of a given
  192.  *        entry. used with 'bs', 'am', etc...
  193.  *
  194.  * Calling conventions: id is the 2 character capability id.
  195.  *
  196.  * Returned values: 1 for success, 0 for failure.
  197.  */
  198.  
  199. tgetflag(id)
  200.     char *id;
  201. {
  202.     char    buf[256], *ptr = buf;
  203.  
  204.     return tgetstr(id, &ptr) ? 1 : 0;
  205. }
  206.  
  207. /*
  208.  * Module: tgetnum
  209.  *
  210.  * Purpose: get numeric value such as 'li' or 'co' from termcap.
  211.  *
  212.  * Calling conventions: id = 2 character id.
  213.  *
  214.  * Returned values: -1 for failure, else numerical value.
  215.  */
  216.  
  217. tgetnum(id)
  218. char *id;
  219. {
  220.     char *ptr, buf[256];
  221.     ptr = buf;
  222.  
  223.     if (tgetstr(id, &ptr))
  224.         return atoi(buf);
  225.     else
  226.         return 0;
  227. }
  228.  
  229. /*
  230.  * Module: tgetstr
  231.  *
  232.  * Purpose: get terminal capability string from database.
  233.  *
  234.  * Calling conventions: id is the two character capability id.
  235.  *            (*buf) points into a hold buffer for the
  236.  *            id. the capability is copied into the buffer
  237.  *            and (*buf) is advanced to point to the next
  238.  *            free byte in the buffer.
  239.  *
  240.  * Returned values: 0 = no such entry, otherwise returns original
  241.  *            (*buf) (now a pointer to the string).
  242.  *
  243.  * Notes
  244.  *        It also decodes certain escape sequences in the buffer.
  245.  *    they should be obvious from the code:
  246.  *        \E = escape.
  247.  *        \n, \r, \t, \f, \b match the 'c' escapes.
  248.  *        ^x matches control-x (^@...^_).
  249.  *        \nnn matches nnn octal.
  250.  *        \x, where x is anything else, matches x. I differ
  251.  *    from the standard library here, in that I allow ^: to match
  252.  *    :.
  253.  *
  254.  */
  255.  
  256. char *
  257. tgetstr(id, buf)
  258. char    *id, **buf;
  259. {
  260.     int    len = strlen(id);
  261.     char *tmp=tent;
  262.     char *hold;
  263.     int        i;
  264.  
  265.     do {
  266.         tmp = _find(tmp, ":");                     /* For each field */
  267.         while (*tmp == ':')                        /* skip empty fields */
  268.             tmp++;
  269.         if (!*tmp)
  270.             break;
  271.  
  272.         if (_match(id, tmp) == len) {
  273.             tmp += len;                   /* find '=' '@' or '#' */
  274.             if (*tmp == '@')                  /* :xx@: entry for tc */
  275.                 return 0;                   /* deleted entry */
  276.             hold= *buf;
  277.             while (*++tmp && *tmp != ':') {/* not at end of field */
  278.                 switch(*tmp) {
  279.                 case '\\':            /* Expand escapes here */
  280.                     switch(*++tmp) {
  281.                     case 0:        /* ignore backslashes */
  282.                         tmp--;    /* at end of entry */
  283.                         break;   /* shouldn't happen */
  284.                     case 'e':
  285.                     case 'E':                     /* ESC */
  286.                         *(*buf)++ = '\033'; 
  287.                         break;
  288.                     case 'n':                      /* \n */
  289.                         *(*buf)++ = '\n'; 
  290.                         break;
  291.                     case 'r':                      /* \r */
  292.                         *(*buf)++ = '\r'; 
  293.                         break;
  294.                     case 't':                      /* \t */
  295.                         *(*buf)++ = '\t'; 
  296.                         break;
  297.                     case 'b':                      /* \b */
  298.                         *(*buf)++ = '\b'; 
  299.                         break;
  300.                     case 'f':                      /* \f */
  301.                         *(*buf)++ = '\f'; 
  302.                         break;
  303.                     case '0':                    /* \nnn */
  304.                     case '1': 
  305.                     case '2': 
  306.                     case '3': 
  307.                     case '4':
  308.                     case '5': 
  309.                     case '6': 
  310.                     case '7': 
  311.                     case '8': 
  312.                     case '9':
  313.                         **buf = 0;
  314.                             /* get up to three digits */
  315.                         for (i = 0; i < 3 && isdigit(*tmp); ++i)
  316.                             **buf = **buf * 8 + *tmp++ - '0';
  317.                         (*buf)++;
  318.                         tmp--;
  319.                         break;
  320.                     default:      /* \x, for all other x */
  321.                         *(*buf)++= *tmp;
  322.                     }
  323.                     break;
  324.                 case '^':              /* control characters */
  325.                     *(*buf)++ = *++tmp - '@'; 
  326.                     break;
  327.                 default: 
  328.                     *(*buf)++ = *tmp;
  329.                 }
  330.             }
  331.             *(*buf)++ = 0;
  332.             return hold;
  333.         }
  334.     } while (*tmp);
  335.  
  336.     return 0;
  337. }
  338.  
  339. /*
  340.  * Module: tgoto
  341.  *
  342.  * Purpose: decode cm cursor motion string.
  343.  *
  344.  * Calling conventions: cm is cursor motion string.
  345.  *            line, col, are the desired destination.
  346.  *
  347.  * Returned values: a string pointing to the decoded string, or
  348.  *            "OOPS" if it cannot be decoded.
  349.  *
  350.  * Notes
  351.  *        The accepted escapes are:
  352.  *            %d     as in printf, 0 origin.
  353.  *            %2, %3     like %02d, %03d in printf.
  354.  *            %.     like %c
  355.  *            %+x     adds <x> to value, then %.
  356.  *            %>xy     if value>x, adds y. No output.
  357.  *            %i     increments line& col, no output.
  358.  *            %r     reverses order of line&col. No output.
  359.  *            %%     prints as a single %.
  360.  *            %n     exclusive or row & col with 0140.
  361.  *            %B     BCD, no output.
  362.  *            %D     reverse coding (x-2*(x%16)), no output.
  363.  */
  364.  
  365. char *
  366. tgoto(cm, col, line)
  367. char    *cm;                                      /* cm string, from termcap */
  368. int    col,                                           /* column, x position */
  369.     line;                                            /* line, y position */
  370. {
  371.     char    gx, gy,                                           /*    x, y */
  372.         *ptr,                                     /* pointer in 'cm' */
  373.         reverse = 0,                                 /* reverse flag */
  374.         *bufp,                         /* pointer in returned string */
  375.         addup = 0,                                     /* add upline */
  376.         addbak = 0,                                    /* add backup */
  377.         c;
  378.     static char buffer[32];
  379.  
  380.     if (!cm)
  381.         return "OOPS";                       /* Kludge, but standard */
  382.  
  383.     bufp = buffer;
  384.     ptr = cm;
  385.  
  386.     while (*ptr) {
  387.         if ((c = *ptr++) != '%') {                     /* normal char */
  388.             *bufp++ = c;
  389.         } else {                                         /* % escape */
  390.             switch(c = *ptr++) {
  391.             case 'd':                                 /* decimal */
  392.                 bufp = _addfmt(bufp, "%d", line);
  393.                 line = col;
  394.                 break;
  395.             case '2':                         /* 2 digit decimal */
  396.                 bufp = _addfmt(bufp, "%02d", line);
  397.                 line = col;
  398.                 break;
  399.             case '3':                         /* 3 digit decimal */
  400.                 bufp = _addfmt(bufp, "%03d", line);
  401.                 line = col;
  402.                 break;
  403.             case '>':                      /* %>xy: if >x, add y */
  404.                 gx = *ptr++;
  405.                 gy = *ptr++;
  406.                 if (col>gx) col += gy;
  407.                 if (line>gx) line += gy;
  408.                 break;
  409.             case '+':                              /* %+c: add c */
  410.                 line += *ptr++;
  411.             case '.':                               /* print x/y */
  412.                 if (line == '\t' ||                /* these are */
  413.                    line == '\n' ||             /* chars that */
  414.                    line == '\004' ||             /* UNIX hates */
  415.                    line == '\0') {
  416.                     line++;         /* so go to next pos */
  417.                     if (reverse == (line == col))
  418.                         addup=1;      /* and mark UP */
  419.                     else
  420.                         addbak=1;           /* or BC */
  421.                 }
  422.                 *bufp++=line;
  423.                 line = col;
  424.                 break;
  425.             case 'r':                              /* r: reverse */
  426.                 gx = line; 
  427.                 line = col; 
  428.                 col = gx;
  429.                 reverse = 1;
  430.                 break;
  431.             case 'i':             /* increment (1-origin screen) */
  432.                 col++;
  433.                 line++;
  434.                 break;
  435.             case '%':                          /* %%=% literally */
  436.                 *bufp++='%';
  437.                 break;
  438.             case 'n':                       /* magic DM2500 code */
  439.                 line ^= 0140;
  440.                 col ^= 0140;
  441.                 break;
  442.             case 'B':                            /* bcd encoding */
  443.                 line = line/10<<4+line%10;
  444.                 col = col/10<<4+col%10;
  445.                 break;
  446.             case 'D':                   /* magic Delta Data code */
  447.                 line = line-2*(line&15);
  448.                 col = col-2*(col&15);
  449.                 break;
  450.             default:                           /* Unknown escape */
  451.                 return "OOPS";
  452.             }
  453.         }
  454.     }
  455.  
  456.     if (addup)                                              /* add upline */
  457.         if (UP) {
  458.             ptr=UP;
  459.             while (isdigit(*ptr) || *ptr == '.')
  460.                 ptr++;
  461.             if (*ptr == '*')
  462.                 ptr++;
  463.             while (*ptr)
  464.                 *bufp++ = *ptr++;
  465.         }
  466.  
  467.     if (addbak)                                          /* add backspace */
  468.         if (BC) {
  469.             ptr=BC;
  470.             while (isdigit(*ptr) || *ptr == '.')
  471.                 ptr++;
  472.             if (*ptr == '*')
  473.                 ptr++;
  474.             while (*ptr)
  475.                 *bufp++ = *ptr++;
  476.         } 
  477.         else
  478.             *bufp++='\b';
  479.  
  480.     *bufp = 0;
  481.  
  482.     return(buffer);
  483. }
  484.  
  485. /*
  486.  * Module: tinit
  487.  *
  488.  * Purpose: simplified terminal initialisation.
  489.  *
  490.  * Calling conventions: name is name of terminal.
  491.  *
  492.  * Returned values: none.
  493.  *
  494.  * Notes
  495.  *        tinit calls tgetent, then sets up the global
  496.  *    variables PC, UP, BC, ospeed appropriately.
  497.  *
  498.  */
  499.  
  500. #if 0        /* already included in term.c */
  501.  
  502. char tbuf[TBUFSZ];                                /* Buffer for termcap entry */
  503. char junkbuf[TBUFSZ];                                  /* Big buffer for junk */
  504. char *junkptr;
  505.  
  506. tinit(name)
  507. char *name;
  508. {
  509. #ifndef AMIGA
  510.     struct sgttyb sgbuf;
  511. #endif
  512.     char *ps;
  513.  
  514.     junkptr = junkbuf;
  515.  
  516.     tgetent(tbuf, name);
  517.  
  518.     ps = tgetstr("pc", &junkptr);
  519.     if (ps) PC = *ps;
  520.     UP = tgetstr("up", &junkptr);
  521.     BC = tgetstr("bc", &junkptr);
  522.  
  523. #ifdef AMIGA
  524.     ospeed=0;
  525. #else
  526.     gtty(1, &sgbuf);
  527.     ospeed=sgbuf.sg_ospeed;
  528. #endif
  529.     return 0;
  530. }
  531. #endif
  532.  
  533. /*
  534.  * Module: tputs
  535.  *
  536.  * Purpose: decode padding information
  537.  *
  538.  * Calling conventions: cp = string to be padded, affcnt = # of items
  539.  *            affected (lines, characters, whatever),
  540.  *            outc = routine to output 1 character.
  541.  *
  542.  * Returned values: none
  543.  *
  544.  * Notes
  545.  *        cp has padding information ahead of it, in the form
  546.  *    nnnTEXT or nnn*TEXT. nnn is the number of milliseconds to delay,
  547.  *    and may be a decimal (nnn.mmm). If the asterisk is given, then
  548.  *    the delay is multiplied by afcnt. The delay is produced by outputting
  549.  *    a number of nulls (or other padding char) after printing the
  550.  *    TEXT.
  551.  *
  552.  */
  553.  
  554. long _bauds[16]={
  555.     0,    50,    75,    110,
  556.     134,    150,    200,    300,
  557.     600,    1200,    1800,    2400,
  558.     4800,    9600,    19200,    19200 };
  559.  
  560. tputs(cp, affcnt, outc)
  561. char *cp;                                                 /* string to print */
  562. int affcnt;                                      /* Number of lines affected */
  563. void (*outc) __ARGS((unsigned int));                              /* routine to output 1 character */
  564. {
  565.     long    frac,                    /* 10^(#digits after decimal point) */
  566.         counter,                                           /* digits */
  567.         atol();
  568.  
  569.     if (isdigit(*cp)) {
  570.         counter = 0;
  571.         frac = 1000;
  572.         while (isdigit(*cp))
  573.             counter = counter * 10L + (long)(*cp++ - '0');
  574.         if (*cp == '.')
  575.             while (isdigit(*++cp)) {
  576.                 counter = counter * 10L + (long)(*cp++ - '0');
  577.                 frac = frac * 10;
  578.             }
  579.         if (*cp!='*') {                 /* multiply by affected lines */
  580.             if (affcnt>1) affcnt = 1;
  581.         } 
  582.         else
  583.             cp++;
  584.  
  585.         /* Calculate number of characters for padding counter/frac ms delay */
  586.         if (ospeed)
  587.             counter = (counter * _bauds[ospeed] * (long)affcnt) / frac;
  588.  
  589.         while (*cp)                                  /* output string */
  590.             (*outc)(*cp++);
  591.         if (ospeed)
  592.             while (counter--)            /* followed by pad characters */
  593.                 (*outc)(PC);
  594.     } 
  595.     else
  596.         while (*cp)
  597.             (*outc)(*cp++);
  598.     return 0;
  599. }
  600.  
  601. /*
  602.  * Module: tutil.c
  603.  *
  604.  * Purpose: Utility routines for TERMLIB functions.
  605.  *
  606.  */
  607.  
  608.     static int
  609. _match(s1, s2)                 /* returns length of text common to s1 and s2 */
  610. char *s1, *s2;
  611. {
  612.     int i = 0;
  613.  
  614.     while (s1[i] && s1[i] == s2[i])
  615.         i++;
  616.  
  617.     return i;
  618. }
  619.  
  620.     static char *
  621. _find(s, set)   /* finds next c in s that's a member of set, returns pointer */
  622. char *s, *set;
  623. {
  624.     for(; *s; s++) {
  625.         char    *ptr = set;
  626.  
  627.         while (*ptr && *s != *ptr)
  628.             ptr++;
  629.  
  630.         if (*ptr)
  631.             return s;
  632.     }
  633.  
  634.     return s;
  635. }
  636.  
  637.     static char *
  638. _addfmt(buf, fmt, val)             /* add val to buf according to format fmt */
  639. char *buf, *fmt;
  640. int val;
  641. {
  642.     sprintf(buf, fmt, val);
  643.     while (*buf)
  644.         buf++;
  645.     return buf;
  646. }
  647.