home *** CD-ROM | disk | FTP | other *** search
/ Serving the Web / ServingTheWeb1995.disc1of1.iso / linux / slacksrce / d / libc / libc-4.6 / libc-4 / libc-linux / termcap / termcap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-19  |  11.5 KB  |  594 lines

  1. /*
  2.  * termcap.c    Replacement for the GNU Emacs termcap routines.
  3.  *        These do more error checking, like preventing a loop
  4.  *        with the tc= capability and buffer overflow.
  5.  *        Also, these routines can stuff a whole lot more in
  6.  *        one buffer because duplicate capabilities are eliminated.
  7.  *
  8.  * Version:    1.1 20-Oct-1994 MvS (miquels@ow.org)
  9.  *
  10.  *        Copyright (C) Miquel van Smoorenburg 1994.
  11.  *        This code falls under the LGPL.
  12.  */
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <unistd.h>
  16. #include <malloc.h>
  17. #include <string.h>
  18. #include <sys/ioctl.h>
  19. #include <termios.h>
  20. #include <termcap.h>
  21.  
  22. /* Escape sequences we know about. */
  23. static char *escapes = "E\033r\rn\nb\bt\tf\f\\\\";
  24.  
  25. /* Pointer for tgetstr() et al */
  26. static char *term_entry;
  27.  
  28. /* Table with speeds for padding. */
  29. static short speeds[] = {
  30.   0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
  31.   -20, -24, -36, -48, -72, -96, -192, -384, -768, -1536
  32. };
  33.  
  34. /* Some (undocumented?) global variables. */
  35. speed_t ospeed;
  36. int tputs_baud_rate;
  37. char PC;
  38. int tgetent_bufsize = 1024;
  39.  
  40. /* We store a terminal description in a linked list. */
  41. struct tc_ent {
  42.   struct tc_ent *next;
  43.   char cap[1];
  44. };
  45.  
  46. /* Safe malloc. */
  47. static void *xmalloc(int len)
  48. {
  49.   void *x;
  50.  
  51.   if ((x = malloc(len)) != NULL) return(x);
  52.   write(2, "Virtual memory exhausted.\n", 26);
  53.   exit(1);
  54. }
  55.  
  56. /* Safe strdup() */
  57. static char *strsave(char *s)
  58. {
  59.   char *x;
  60.  
  61.   x = xmalloc(strlen(s) + 1);
  62.   strcpy(x, s);
  63.   return(x);
  64. }
  65.  
  66. /* Build a linked list with capabilities. */
  67. static char *build_list(struct tc_ent **listp, char *buf)
  68. {
  69.   struct tc_ent *i, *last = NULL, *list = *listp;
  70.   char *s, *sp, *bp;
  71.   int len;
  72.   char *tc_next = NULL;
  73.  
  74.   /* Skip name field. */
  75.   for(sp = buf; *sp && *sp != ':'; sp++)
  76.     ;
  77.   if (*sp) *sp++ = 0;
  78.  
  79.   /* Extract capabilities one-by-one. */
  80.   while(*sp) {
  81.     /* Find end of field. */
  82.     bp = sp;
  83.     while(*sp && *sp != ':') sp++;
  84.     if (*sp) *sp++ = 0;
  85.  
  86.     /* Check for empty field. */
  87.     while(*bp == ' ' || *bp == '\t' || *bp == '\n') bp++;
  88.     if (*bp == 0 || *bp == ':') continue;
  89.  
  90.     /* Is this the "tc" capability? */
  91.     if (!tc_next && strncmp(bp, "tc=", 3) == 0) {
  92.         tc_next = strsave(bp + 3);
  93.         continue;
  94.     }
  95.  
  96.     /* Find name of capability. */
  97.     if ((s = strchr(bp, '=')) != NULL)
  98.         len = s - bp;
  99.     else if ((s = strchr(bp, '@')) != NULL)
  100.         len = s - bp;
  101.     else if ((s = strchr(bp, '#')) != NULL)
  102.         len = s - bp;
  103.     else len = strlen(bp);
  104.     if (len == 0) continue;
  105.  
  106.     /* See if the capability is already in the list. */
  107.     if (list) {
  108.         for(i = list; i; i = i->next) {
  109.             last = i;
  110.             if (strncmp(i->cap, bp, len) == 0) break;
  111.         }
  112.     }
  113.     if (list != NULL && i != NULL) continue;
  114.  
  115.     /* Add capability to the list. */
  116.     i = (struct tc_ent *)xmalloc(sizeof(struct tc_ent) + strlen(bp));
  117.     if (i == NULL) break;
  118.     strcpy(i->cap, bp);
  119.     i->next = NULL;
  120.     if (list == NULL)
  121.         list = i;
  122.     else
  123.         last->next = i;
  124.   }
  125.   /* Done. */
  126.   *listp = list;
  127.   return(tc_next);
  128. }
  129.  
  130. /* Add OR change a capability (hardcoded for li# and co#) */
  131. static void add_list(struct tc_ent **list, char *cap)
  132. {
  133.   struct tc_ent *prev, *new, *l;
  134.  
  135.   /* Walk through the list. */
  136.   prev = NULL;
  137.   for(l = *list; l; l = l->next) {
  138.     if (strncmp(l->cap, cap, 3) == 0) {
  139.  
  140.         /* Found: modify in-place. */
  141.         new = xmalloc(sizeof(struct tc_ent) + strlen(cap));
  142.         strcpy(new->cap, cap);
  143.         new->next = l->next;
  144.         if (prev)
  145.             prev->next = new;
  146.         else
  147.             *list = new;
  148.         free(l);
  149.         l = new;
  150.         break;
  151.     }
  152.     prev = l;
  153.   }
  154.   if (l != NULL) return;
  155.  
  156.   /* Not found, add to the end of the list. */
  157.   new = xmalloc(sizeof(struct tc_ent) + strlen(cap));
  158.   strcpy(new->cap, cap);
  159.   new->next = NULL;
  160.   if (prev)
  161.     prev->next = new;
  162.   else
  163.     *list = new;
  164. }
  165.  
  166. /* Convert a number to ASCII */
  167. static char *_itoa(int num, char *buf)
  168. {
  169.   char *sp = buf + 16;
  170.  
  171.   *--sp = 0;
  172.   do {
  173.     *--sp = (num % 10) + '0';
  174.     num /= 10;
  175.   } while(num);
  176.   return(sp);
  177. }
  178.  
  179. /* Adjust lines and columns by doing a TIOCGWINSZ */
  180. static void adjust_lines_cols(struct tc_ent **l)
  181. {
  182.   struct winsize ws;
  183.   char buf[16];
  184.   char num[16];
  185.  
  186.   /* Get and check window size. */
  187.   if (ioctl(0, TIOCGWINSZ, &ws) < 0 || !ws.ws_row || !ws.ws_col)
  188.     return;
  189.  
  190.   /* Fill in li# and co# */
  191.   strcpy(buf, "li#");
  192.   strcpy(buf + 3, _itoa(ws.ws_row, num));
  193.   add_list(l, buf);
  194.  
  195.   strcpy(buf, "co#");
  196.   strcpy(buf + 3, _itoa(ws.ws_col, num));
  197.   add_list(l, buf);
  198. }
  199.  
  200. /* See if strings contains terminal name. */
  201. static int tc_comp(char *line, char *term)
  202. {
  203.   char *sp, *bp;
  204.   int found = 0, x;
  205.   int len = strlen(term);
  206.  
  207.   bp = sp = line;
  208.   x = *bp;
  209.   while(x && x != ':' && x != '\n') {
  210.     /* Find the end of this description. */
  211.     while(*sp && *sp != ':' && *sp != '|' && *sp != '\n')
  212.         sp++;
  213.     if (len == (sp - bp) && strncmp(term, bp, len) == 0) {
  214.         /* Found it! */
  215.         found = 1;
  216.         break;
  217.     }
  218.     x = *sp++;
  219.     bp = sp;
  220.   }
  221.   return(found);
  222. }
  223.  
  224.  
  225. /* Load a specific terminal. */
  226. static char *get_one_entry(FILE *tfp, char *term)
  227. {
  228.   char line[256];
  229.   int status = 0;
  230.   char *sp;
  231.   char buf[4096];
  232.   char *bufp = buf;
  233.  
  234.   if (term == NULL) return(NULL);
  235.  
  236.   /* Start at beginning. */
  237.   rewind(tfp);
  238.  
  239.   /* Read line by line. */
  240.   while(fgets(line, 256, tfp) != NULL) {
  241.     if (line[0] == '#') continue;
  242.  
  243.     /* See if this is what we're looking for. */
  244.     if (status == 0 && tc_comp(line, term) == 0) continue;
  245.     status = 1;
  246.  
  247.     /* We are reading a description here. */
  248.     for(sp = line; *sp == ' ' || *sp == '\t'; sp++)
  249.         ;
  250.  
  251.     /* Add the rest until nl to the buffer. */
  252.     while(*sp && *sp != '\n') {
  253.         if (*sp == '\\' && (*(sp + 1) == '\n')) break;
  254.         *bufp++ = *sp++;
  255.         if (bufp - buf > 4092) {
  256.             /* Buffer full, quit. */
  257.             *sp = '\n';
  258.             break;
  259.         }
  260.     }
  261.     if (*sp == '\n') break;
  262.   }
  263.   /* Save buffer to malloced area. */
  264.   *bufp++ = 0;
  265.   if (status == 0) return(NULL);
  266.   if ((sp = xmalloc(bufp - buf)) == NULL) return(sp);
  267.   memcpy(sp, buf, bufp - buf);
  268.   return(sp);
  269. }
  270.  
  271.  
  272. /* Read terminal description. */
  273. static char *tc_read(struct tc_ent **tcp, char *term)
  274. {
  275.   FILE *fp;
  276.   char *sp, *tc;
  277.   char *desc = NULL;
  278.   char *tc_file = "/etc/termcap";
  279.   struct tc_ent *l = NULL;
  280.   int first = 1;
  281.   int loop = 0;
  282.  
  283.   *tcp = NULL;
  284.  
  285.   /* See if we have a TERMCAP environment variable. */
  286.   if ((tc = getenv("TERMCAP")) != NULL) {
  287.     if (*tc == '/')
  288.         tc_file = tc;
  289.     else {
  290.         /* check if TERMCAP is term */
  291.         if (tc_comp(tc, term)) {
  292. #if DEBUG
  293.             printf("Using TERMCAP\n");
  294. #endif
  295.             /* Just read the TERMCAP variable. */
  296.             sp = strsave(tc);
  297.             tc = build_list(&l, sp);
  298.             if (tc) free(tc);
  299.             *tcp = l;
  300.             return(sp);
  301.         }
  302.     }
  303.   }
  304.  
  305. #if DEBUG
  306.   printf("Using file %s\n", tc_file);
  307. #endif
  308.  
  309.   /* Now read the termcap file. */
  310.   if ((fp = fopen(tc_file, "r")) == NULL) return(NULL);
  311.  
  312.   while(term) {
  313.     if (++loop > 16) {
  314.         write(2, "tgetent: loop detected, check your termcap\n", 43);
  315.         break;
  316.     }
  317. #if DEBUG
  318.     printf("LOOKUP: term %s\n", term);
  319. #endif
  320.     sp = get_one_entry(fp, term);
  321.     if (sp == NULL) break;
  322.     term = build_list(&l, sp);
  323.     if (first)
  324.         desc = sp;
  325.     else
  326.         free(sp);
  327.     first = 0;
  328.   }
  329.   fclose(fp);
  330.  
  331.   /* Done. */
  332.   *tcp = l;
  333. #if DEBUG
  334.   printf(">> tc_read done, desc = %s\n", desc);
  335. #endif
  336.   return(desc ? desc : "");
  337. }
  338.  
  339.  
  340. /* The tgetent function. */
  341. int tgetent(void *buffer, const char *term)
  342. {
  343.   char *s;
  344.   struct tc_ent *l, *i, *next;
  345.   char *bp, *sp = (char *)buffer;
  346.   int len, count, maxlen;
  347.  
  348.   /* Find the termcap entry. */
  349.   s = tc_read(&l, (char *)term);
  350.  
  351.   /* Return -1 if we can't open the termcap file. */
  352.   if (s == NULL) return(-1);
  353.  
  354.   /* Return 0 if the entry is not present. */
  355.   if (l == NULL) return(0);
  356.  
  357.   /* Adjust lines and columns. */
  358.   adjust_lines_cols(&l);
  359.  
  360.   /* Do we already have a buffer? */
  361.   if (sp)
  362.     maxlen = tgetent_bufsize - 1;
  363.   else {
  364.     /* Count how many bytes we need. */
  365.     count = strlen(s) + 1;
  366.     for(i = l; i; i = i->next)
  367.         count += strlen(i->cap) + 1;
  368.     count++;
  369.     
  370.     /* Malloc this amount. */
  371.       sp = xmalloc(count);
  372.     maxlen = count + 32; /* Just a lot. */
  373.   }
  374.  
  375.   /* Save buffer into static variable (yuk!) */
  376.   term_entry = sp;
  377.  
  378.   /* First copy the description to the buffer. */
  379.   count = 0;
  380.   for(bp = s; *bp; bp++) {
  381.     *sp++ = *bp;
  382.     count++;
  383.   }
  384.   *sp++ = ':';
  385.   count++;
  386.  
  387.   /* And now the capabilities. */
  388.   for(i = l; i; i = next) {
  389.  
  390.     /* Is this a 'skip' capability? */
  391.     len = strlen(i->cap);
  392.     if (strchr(i->cap, '=') == NULL && i->cap[len-1] == '@') {
  393.         next = i->next;
  394.         free(i);
  395.         continue;
  396.     }
  397.  
  398.     /* Check for buffer overflow. */
  399.     count += len + 1;
  400.     if (count >= maxlen) {
  401.         write(2, "tgetent: warning: termcap entry too long\n", 41);
  402.         break;
  403.     }
  404.  
  405.     /* Add capability to buffer. */
  406.     for(bp = i->cap; *bp; bp++)
  407.         *sp++ = *bp;
  408.     *sp++ = ':';
  409.  
  410.     /* Free space. */
  411.     next = i->next;
  412.     free(i);
  413.   }
  414.   *sp = 0;
  415.  
  416.   return(1);
  417. }
  418.  
  419. /* Generic "find capability" routine. */
  420. static char *find_cap(char *bp, const char *cap, char sep)
  421. {
  422. #if 0
  423.   int len = strlen(cap);
  424.  
  425.   if (len == 2) {
  426.     /* Normal case, do fast lookup. */
  427. #endif
  428.     while(*bp) {
  429.         if (bp[0] == ':' &&
  430.             bp[1] == cap[0] &&
  431.             bp[2] == cap[1] &&
  432.             bp[3] == sep) return(bp + 4);
  433.         bp++;
  434.     }
  435.     return(NULL);
  436. #if 0
  437.   }
  438.   /* Longer string, use slow lookup. */
  439.   while(*bp) {
  440.     if (bp[0] == ':' && bp[len+1] == sep && strncmp(bp+1, cap, len) == 0)
  441.         return(bp + len + 2);
  442.     bp++;
  443.   }
  444.   return(NULL);
  445. #endif
  446. }
  447.  
  448. /* Find a number capability. */
  449. int tgetnum(const char *cap)
  450. {
  451.   char *s;
  452.  
  453.   s = find_cap(term_entry, cap, '#');
  454.   return(s ? atoi(s) : -1);
  455. }
  456.  
  457. /* Find a boolean capability. */
  458. int tgetflag(const char *cap)
  459. {
  460.   return(find_cap(term_entry, cap, ':') ? 1 : 0);
  461. }
  462.  
  463. /* Find a string capability. */
  464. char *tgetstr(const char *cap, char **bufp)
  465. {
  466.   char *s;
  467.   char *sp, *r, *ret;
  468.   int c;
  469.  
  470.   s = find_cap(term_entry, cap, '=');
  471.   if (s == NULL) return(s);
  472.  
  473.   /* Where to put the result. */
  474.   if (bufp == (char **)NULL) {
  475.     for(sp = s; *sp != ':' && *sp; sp++)
  476.         ;
  477.     ret = xmalloc(sp - s + 1);
  478.   } else
  479.     ret = *bufp;
  480.   r = ret;
  481.  
  482.   /* Translate escaped characters and hat-notation. */
  483.   while((c = *s++) && c != ':') {
  484.     if (c == '\\' && *s != ':') {
  485.  
  486.         /* Escaped character. */
  487.         c = *s++;
  488.  
  489.         if (c >= '0' && c <= '9') {
  490.             /* Octal number. */
  491.             c -= '0';
  492.             while(*s >= '0' && *s <= '9') {
  493.                 c = (c * 8) + (*s - '0');
  494.                 s++;
  495.             }
  496.         } else {
  497.             /* \r or \n or whatever. */
  498.             for(sp = escapes; *sp; sp += 2)
  499.                 if (c == *sp) {
  500.                     c = *++sp;
  501.                     break;
  502.                 }
  503.         }
  504.     } else if (c == '^' && *s != ':')
  505.         /* Hat notation. */
  506.         c = *s++ & 0x1f;
  507.     *r++ = c;
  508.   }
  509.   *r++ = 0;
  510.  
  511.   /* Do we need to update bufp? */
  512.   if (bufp) *bufp = r;
  513.  
  514.   return(ret);
  515. }
  516.  
  517. /* Output string with padding - stolen from GNU termcap.c :) */
  518. void tputs(const char *str, int nlines, int (*outfun)(int))
  519. {
  520.   int padcount = 0;
  521.   int speed;
  522.  
  523.   /* Safety check. */
  524.   if (!str) return;
  525.  
  526.   /* Try to get output speed. */
  527.   if (ospeed == 0)
  528.     speed = tputs_baud_rate;
  529.   else
  530.     speed = speeds[ospeed];
  531.  
  532.   /* Read padding information. */
  533.   while (*str >= '0' && *str <= '9') {
  534.       padcount += *str++ - '0';
  535.       padcount *= 10;
  536.   }
  537.   if (*str == '.') {
  538.       str++;
  539.       padcount += *str++ - '0';
  540.   }
  541.   if (*str == '*') {
  542.       str++;
  543.       padcount *= nlines;
  544.   }
  545.  
  546.   /* Now output the capability string. */
  547.   while (*str)
  548.     (*outfun) (*str++);
  549.  
  550.   /* Do we need padding? */
  551.   if (padcount == 0) return;
  552.  
  553.   /* padcount is now in units of tenths of msec.  */
  554.   padcount *= speeds[ospeed];
  555.   padcount += 500;
  556.   padcount /= 1000;
  557.   if (speeds[ospeed] < 0)
  558.     padcount = -padcount;
  559.   else {
  560.     padcount += 50;
  561.     padcount /= 100;
  562.   }
  563.  
  564.   /* And output the pad character. */
  565.   while (padcount-- > 0)
  566.     (*outfun) (PC);
  567. }
  568.  
  569.  
  570. #ifdef TEST
  571. /*ARGSUSED*/
  572. int main(int argc, char **argv)
  573. {
  574.   char buf[1024];
  575.   char *s;
  576.  
  577.   if (tgetent(buf, argv[1]) != 1) exit(1);
  578.  
  579.   printf("%s\n\n", term_entry);
  580.  
  581.   s = tgetstr("cm", NULL);
  582.   if (s && *s == '\033') *s = '?';
  583.  
  584.   printf("tgetflag(li) = %d\n", tgetflag("li"));
  585.   printf("tgetflag(mi) = %d\n", tgetflag("mi"));
  586.   printf("tgetstr(cm) = [%s]\n", s);
  587.   printf("tgetstr(ks) = [%s]\n", tgetstr("ks", NULL));
  588.   printf("tgetnum(li) = %d\n", tgetnum("li"));
  589.  
  590.   return(0);
  591. }
  592. #endif
  593.  
  594.