home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume26 / pcomm-2.0.2 / part04 / vcs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-13  |  10.8 KB  |  502 lines

  1. /*
  2.  * Routines for VCS detection.
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include "config.h"
  7. #include "status.h"
  8. #include "vcs.h"
  9.  
  10. #ifndef OLDCURSES
  11. #include <curses.h>
  12. #include <term.h>
  13. #endif /* OLDCURSES */
  14.  
  15. static int vcs_codes[NUM_VCS][VCS_SIZE];/* the VCS codes */
  16. static int vcs_leadin[NUM_VCS];        /* unique list of lead-in characters */
  17. static int num_leadin;            /* length of lead-in list */
  18. static int putc_cnt;
  19. static char putc_buf[VCS_SIZE];
  20. static int substr(), fake_putc(), match_codes();
  21. static void fake_it();
  22.  
  23. int vcs_param[NUM_VCS][5];        /* positional parameters */
  24. int vcs_opt[NUM_VCS][10];        /* options unique to each VCS */
  25.  
  26. /*
  27.  * Test for possible VCS (video command sequence).  A character return
  28.  * code means no match.  An return code greater than 255 means a VCS
  29.  * was found.
  30.  */
  31.  
  32. int
  33. vcs_filter(c)
  34. char c;
  35. {
  36.     static int vcs_buf[VCS_SIZE];
  37.     static int ptr = 0;
  38.     register int i;
  39.     int maybe, possible;
  40.  
  41.                     /* see if possible */
  42.     possible = 0;
  43.     if (ptr == 0) {
  44.         /*
  45.          * This is kinda crude... I'm checking to see if the
  46.          * lead-in character is greater than the space character.
  47.          * If so, it most probably is NOT a VCS.
  48.          */
  49.         if (c >= ' ')
  50.             return(c & 0xff);
  51.                     /* check the list */
  52.         for (i=0; i<num_leadin; i++) {
  53.             if (c == vcs_leadin[i]) {
  54.                 possible++;
  55.                 break;
  56.             }
  57.         }
  58.         if (!possible)
  59.             return(c & 0xff);
  60.     }
  61.  
  62.                     /* build the string */
  63.     vcs_buf[ptr++] = (unsigned char) c;
  64.     vcs_buf[ptr] = -1;
  65.                     /* test for match */
  66.     maybe = 0;
  67.     for (i=0; i<NUM_VCS; i++) {
  68.         switch (match_codes(vcs_buf, vcs_codes[i], i)) {
  69.             case YES:
  70.                 ptr = 0;
  71.                 return(i+256);
  72.             case NO:
  73.                 break;
  74.             case MAYBE:
  75.                 maybe++;
  76.                 break;
  77.         }
  78.     }
  79.                     /* abandon what you've got */
  80.     if (maybe && ptr == VCS_SIZE-1) {
  81.         ptr = 0;
  82.         return(c & 0xff);
  83.     }
  84.                     /* hang on, wait and see */
  85.     if (maybe)
  86.         return(MAYBE);
  87.                     /* a clean miss */
  88.     ptr = 0;
  89.     return(c & 0xff);
  90. }
  91.  
  92. /*
  93.  * See if the two integer arrays "match".  Character parameters are
  94.  * designated by codes > 1000 and ASCII digit parameters are designated
  95.  * by codes > 2000.  Uses a simple linear search, so if NUM_VCS grows
  96.  * this routine will have to mature a bit.
  97.  */
  98.  
  99. static int
  100. match_codes(test, code, k)
  101. int test[], code[], k;
  102. {
  103.     extern int vcs_param[NUM_VCS][5];
  104.     register int i, j;
  105.     int pos, done;
  106.                     /* doesn't exist */
  107.     if (code[0] == -1)
  108.         return(NO);
  109.  
  110.     i = 0;
  111.     j = 0;
  112.     while (i<VCS_SIZE && j<VCS_SIZE) {
  113.                     /* at the end (a match) */
  114.         if (test[i] == -1 && code[j] == -1)
  115.             return(YES);
  116.                     /* ran out of input */
  117.         if (test[i] == -1)
  118.             break;
  119.         /*
  120.          * The char parameter (code 1000) always matches the
  121.          * next character.
  122.          */
  123.         if (code[j] >= 1000 && code[j] < 2000) {
  124.             pos = code[j] -1000;
  125.             vcs_param[k][pos] = test[i];
  126.             i++;
  127.             j++;
  128.             continue;
  129.         }
  130.         /*
  131.          * The digit parameter (code 2000) tries to match as many
  132.          * ASCII digits as it can.
  133.          */
  134.         if (code[j] >= 2000) {
  135.             pos = code[j] -2000;
  136.                     /* done with this number? */
  137.             if (vcs_param[k][pos])
  138.                 done = 1;
  139.             else
  140.                 done = 0;
  141.                     /* only digits */
  142.             while (test[i] >= 48 && test[i] <= 57) {
  143.                 if (!done)
  144.                     vcs_param[k][pos] = (vcs_param[k][pos] * 10) + test[i] -48;
  145.                 i++;
  146.             }
  147.                     /* ended in a digit */
  148.             if (test[i] == -1 && code[j+1] != -1) {
  149.                 vcs_param[k][pos] = 0;
  150.                 break;
  151.             }
  152.             j++;
  153.             continue;
  154.         }
  155.                     /* a clean miss */
  156.         if (test[i] != code[j]) {
  157.             for (j=0; j<5; j++)
  158.                 vcs_param[k][j] = 0;
  159.             return(NO);
  160.         }
  161.         i++;
  162.         j++;
  163.     }
  164.                     /* a maybe */
  165.     return(MAYBE);
  166. }
  167.  
  168. /*
  169.  * Build the table of VCS codes.  Actually we cheat... We tell curses(3)
  170.  * to build the strings to perform the function, and then we decipher
  171.  * what it did.
  172.  *
  173.  * For example: On a vt100 the cursor motion string in terminfo is:
  174.  *    cup=\E[%i%p1%d;%p2%dH$<5>
  175.  *
  176.  * This gets translated to the integer array vcs_code[] as:
  177.  *    \E   [   %p1%d  ;   %p2%d  H
  178.  *    27,  91, 2000,  59, 2001,  72
  179.  * 
  180.  * Notice that the "%p1" and "%p2" parameters get translated to 2000 and
  181.  * 2001.  This is to signify that the parameters are multiple digit ASCII
  182.  * encoded numbers.  The "%i" and "%d" codes are embedded into the vcs_opt[]
  183.  * array in somewhat of a loose manner.  In other words, there is no set
  184.  * format for the vcs_opt[] array.  The padding info "$<5>" is ignored.
  185.  */
  186.  
  187. void
  188. vcs_table()
  189. {
  190.     int i, j, k, match, temp[VCS_SIZE];
  191.     char *p, *strcpy(), buf[VCS_SIZE];
  192.     char *tparm();            /* <- comment out, if required */
  193.  
  194. #ifdef OLDCURSES
  195.     extern char tcbuf[1024];
  196.     char *t, *cursor_home, *clr_eol, *clr_eos;
  197.     char *clear_screen, *cursor_up, *cursor_down, *cursor_right;
  198.     char *cursor_left, *cursor_address, *tgetstr(), *tgoto();
  199.  
  200.     t = tcbuf;
  201.     cursor_home = tgetstr("ho", &t);
  202.     clr_eol = tgetstr("ce", &t);
  203.     clr_eos = tgetstr("cd", &t);
  204.     clear_screen = tgetstr("cl", &t);
  205.     cursor_up = tgetstr("up", &t);
  206.     cursor_down = tgetstr("do", &t);
  207.     cursor_right = tgetstr("nd", &t);
  208.     cursor_left = tgetstr("le", &t);
  209.     cursor_address = tgetstr("cm", &t);
  210. #endif /* OLDCURSES */
  211.  
  212.     /*
  213.      * Do the easy ones first.  These don't take positional parameters,
  214.      * so all we have to do is strip the padding info.
  215.      */
  216.     for (i=0; i<NUM_VCS; i++) {
  217.         switch (i) {
  218.             case HOME:
  219.                 p = cursor_home;
  220.                 break;
  221.             case CLR_EOL:
  222.                 p = clr_eol;
  223.                 break;
  224.             case CLR_EOS:
  225.                 p = clr_eos;
  226.                 break;
  227.             case CLEAR:
  228.                 p = clear_screen;
  229.                 break;
  230.             case MV_UP:
  231.                 p = cursor_up;
  232.                 break;
  233.             case MV_DOWN:
  234.                 p = cursor_down;
  235.                 break;
  236.             case MV_RIGHT:
  237.                 p = cursor_right;
  238.                 break;
  239.             case MV_LEFT:
  240.                 p = cursor_left;
  241.                 break;
  242.             default:
  243.                 p = "";
  244.                 break;
  245.         }
  246.         /*
  247.          * Either the capability doesn't exist, or we're gonna
  248.          * do this one by hand (i.e.: ones with positional parameters)
  249.          */
  250.         if (!p) {
  251.             vcs_codes[i][0] = -1;
  252.             continue;
  253.         }
  254.                     /* fake an "output" */
  255.         fake_it(p);
  256.                     /* copy what it did */
  257.         j = 0;
  258.         while (putc_buf[j]) {
  259.             vcs_codes[i][j] = (unsigned char) putc_buf[j];
  260.             j++;
  261.             if (j == VCS_SIZE-1)
  262.                 break;
  263.         }
  264.         vcs_codes[i][j] = -1;
  265.     }
  266.  
  267.     /*
  268.      * And now for the difficult ones.  The way it's done is: load the
  269.      * string with a few known parameters and then find where the
  270.      * parameters end up.  The vcs_opt[][] array is "free-flowing"
  271.      * and means something only to the routine being used.
  272.      */
  273.                     /* add one to the param */
  274.     if (substr(cursor_address, "%i") > 0) {
  275.         vcs_opt[MV_DIRECT][0] = 1;
  276.         vcs_opt[MV_DIRECT][1] = 1;
  277.     }
  278.                     /* decimal codes used */
  279.     if (substr(cursor_address, "%d") > 0)
  280.         vcs_opt[MV_DIRECT][1] = 1;
  281.                     /* character codes used */
  282.     if (substr(cursor_address, "%c") > 0)
  283.         vcs_opt[MV_DIRECT][2] = 1;
  284.                     /* add an offset */
  285.     if (substr(cursor_address, "%+") > 0)
  286.         vcs_opt[MV_DIRECT][3] = 1;
  287.                     /* subtract an offset */
  288.     if (substr(cursor_address, "%-") > 0)
  289.         vcs_opt[MV_DIRECT][4] = 1;
  290.                     /* load with parameters 12 & 34 */
  291. #ifdef OLDCURSES
  292.     fake_it(tgoto(cursor_address, 12, 34));
  293. #else /* OLDCURSES */
  294.     fake_it((char *)tparm(cursor_address, 12, 34));
  295. #endif /* OLDCURSES */
  296.  
  297.     j = 0;
  298.     while (putc_buf[j]) {
  299.         temp[j] = (unsigned char) putc_buf[j];
  300.         j++;
  301.         if (j == VCS_SIZE-1)
  302.             break;
  303.     }
  304.     temp[j] = -1;
  305.                     /* if decimal parameters */
  306.     if (vcs_opt[MV_DIRECT][1]) {
  307.                     /* if add one */
  308.         if (vcs_opt[MV_DIRECT][0])
  309.             strcpy(buf, "13");
  310.         else
  311.             strcpy(buf, "12");
  312.                     /* where is the 12 (or 13)? */
  313.         if ((i = substr(putc_buf, buf)) > 0) {
  314.             temp[i] = 2000;
  315.             temp[i+1] = -2;
  316.         }
  317.         else
  318.             temp[0] = -1;
  319.                     /* if add one */
  320.         if (vcs_opt[MV_DIRECT][0])
  321.             strcpy(buf, "35");
  322.         else
  323.             strcpy(buf, "34");
  324.                     /* where is the 34 (or 35)? */
  325.         if ((i = substr(putc_buf, buf)) > 0) {
  326.             temp[i] = 2001;
  327.             temp[i+1] = -2;
  328.         }
  329.         else
  330.             temp[0] = -1;
  331.     }
  332.                     /* if character parameters */
  333.     if (vcs_opt[MV_DIRECT][2]) {
  334.                     /* original with 12 and 34 */
  335.         strcpy(buf, putc_buf);
  336.                     /* change 12 to 13 */
  337. #ifdef OLDCURSES
  338.         fake_it(tgoto(cursor_address, 13, 34));
  339. #else /* OLDCURSES */
  340.         fake_it((char *)tparm(cursor_address, 13, 34));
  341. #endif /* OLDCURSES */
  342.                     /* where are they different */
  343.         i = 0;
  344.         while (buf[i] != '\0') {
  345.             if (buf[i] != putc_buf[i])
  346.                 break;
  347.             i++;
  348.         }
  349.                     /* sanity checking */
  350.         if (buf[i] == '\0')
  351.             temp[0] = -1;
  352.                     /* if add, what is offset? */
  353.         if (vcs_opt[MV_DIRECT][3])
  354.             vcs_opt[MV_DIRECT][5] = temp[i] - 13;
  355.  
  356.                     /* if subtract, what is offset? */
  357.         if (vcs_opt[MV_DIRECT][4])
  358.             vcs_opt[MV_DIRECT][5] = 13 - temp[i];
  359.  
  360.         temp[i] = 1000;
  361.                     /* change 34 to 35 */
  362. #ifdef OLDCURSES
  363.         fake_it(tgoto(cursor_address, 12, 35));
  364. #else /* OLDCURSES */
  365.         fake_it((char *)tparm(cursor_address, 12, 35));
  366. #endif /* OLDCURSES */
  367.                     /* where are they different */
  368.         i = 0;
  369.         while (buf[i] != '\0') {
  370.             if (buf[i] != putc_buf[i])
  371.                 break;
  372.             i++;
  373.         }
  374.         temp[i] = 1001;
  375.         if (buf[i] == '\0')
  376.             temp[0] = -1;
  377.     }
  378.                     /* strip the -2's out, if any */
  379.     i = 0;
  380.     j = 0;
  381.     while (temp[i] != -1) {
  382.         if (temp[i] != -2)
  383.             vcs_codes[MV_DIRECT][j++] = temp[i];
  384.         i++;
  385.     }
  386.     vcs_codes[MV_DIRECT][j] = -1;
  387.  
  388.     /*
  389.      * Simplify the list.  Some codes are already handled by the
  390.      * virtual screen routines... no need to duplicate them.
  391.      */
  392.     if (vcs_codes[MV_DOWN][0] == '\n')
  393.         vcs_codes[MV_DOWN][0] = -1;
  394.  
  395.     if (vcs_codes[MV_LEFT][0] == 8)
  396.         vcs_codes[MV_LEFT][0] = -1;
  397.  
  398.     /*
  399.      * Often the "clear screen" sequence will contain the "home"
  400.      * sequence... if so, don't duplicate the "home" portion.
  401.      */
  402.     fake_it(cursor_home);
  403.     strcpy(buf, putc_buf);
  404.  
  405.     fake_it(clear_screen);
  406.                     /* if "home" inside "clear screen" */
  407.     if ((k = substr(putc_buf, buf)) >= 0) {
  408.                     /* if at the beginning */
  409.         if (k == 0) {
  410.             i = 0;
  411.             for (j=strlen(buf); j<VCS_SIZE; j++)
  412.                 vcs_codes[CLEAR][i++] = (unsigned char) putc_buf[j];
  413.             vcs_codes[CLEAR][i] = -1;
  414.         }
  415.                     /* if at the end */
  416.         else if (strlen(buf)+k == strlen(putc_buf))
  417.             vcs_codes[CLEAR][k] = -1;
  418.     }
  419.                     /* is "clear screen" still unique */
  420.     k = 0;
  421.     for (i=0; i<NUM_VCS; i++) {
  422.         if (vcs_codes[CLEAR][i] == -1 || vcs_codes[CLR_EOS][i] == -1)
  423.             break;
  424.         if (vcs_codes[CLEAR][i] != vcs_codes[CLR_EOS][i]) {
  425.             k++;
  426.             break;
  427.         }
  428.     }
  429.     if (k == 0)
  430.         vcs_codes[CLEAR][0] = -1;
  431.  
  432.     /*
  433.      * Make a list of unique lead-in characters to be used as a
  434.      * simple hash table.
  435.      */
  436.     num_leadin = 0;
  437.     for (i=0; i<NUM_VCS; i++) {
  438.         if (vcs_codes[i][0] == -1)
  439.             continue;
  440.                     /* add any new lead-in character */
  441.         match = 0;
  442.         for (j=0; j<num_leadin; j++) {
  443.             if (vcs_leadin[j] == vcs_codes[i][0])
  444.                 match++;
  445.         }
  446.         if (!match)
  447.             vcs_leadin[num_leadin++] = vcs_codes[i][0];
  448.     }
  449.     return;
  450. }
  451.  
  452. /*
  453.  * The routine that fakes curses(3) into outputting the string info with
  454.  * the padding removed.
  455.  */
  456.  
  457. static void
  458. fake_it(s)
  459. char *s;
  460. {
  461.     putc_cnt = 0;
  462.     putc_buf[0] = '\0';
  463.     tputs(s, 1, fake_putc);
  464.     putc_buf[putc_cnt] = '\0';
  465.     return;
  466. }
  467.  
  468. static int
  469. fake_putc(c)
  470. char c;
  471. {
  472.     if (c != '\0')
  473.         putc_buf[putc_cnt++] = (unsigned char) c;
  474.     return((unsigned char) c);
  475. }
  476.  
  477. /*
  478.  * Is string2 contained in string1?  If so, return the offset, otherwise
  479.  * return a -1.
  480.  */
  481.  
  482. static int
  483. substr(s1, s2)
  484. char *s1, *s2;
  485. {
  486.     int i, len;
  487.  
  488.     len = strlen(s2);
  489.                     /* not possible */
  490.     if (len > strlen(s1))
  491.         return(-1);
  492.  
  493.     i = 0;
  494.     while (*s1) {
  495.         if (!strncmp(s1, s2, len))
  496.             return(i);
  497.         s1++;
  498.         i++;
  499.     }
  500.     return(-1);
  501. }
  502.