home *** CD-ROM | disk | FTP | other *** search
/ Shareware 1 2 the Maxx / sw_1.zip / sw_1 / OS2 / BEAV132X.ZIP / TERMCAP.C < prev    next >
C/C++ Source or Header  |  1992-01-06  |  29KB  |  1,298 lines

  1. /************************************************************************
  2.  *                                    *
  3.  *            Copyright (c) 1982, Fred Fish            *
  4.  *                All Rights Reserved                *
  5.  *                                    *
  6.  *    This software and/or documentation is released for public    *
  7.  *    distribution for personal, non-commercial use only.        *
  8.  *    Limited rights to use, modify, and redistribute are hereby    *
  9.  *    granted for non-commercial purposes, provided that all        *
  10.  *    copyright notices remain intact and all changes are clearly    *
  11.  *    documented.  The author makes no warranty of any kind with    *
  12.  *    respect to this product and explicitly disclaims any implied    *
  13.  *    warranties of merchantability or fitness for any particular    *
  14.  *    purpose.                            *
  15.  *                                    *
  16.  ************************************************************************
  17.  */
  18.  
  19. #include <stdlib.h>
  20. #include <ctype.h>
  21. #include <stdio.h>
  22. #include <string.h>
  23.  
  24. #include "termcap.h"
  25.  
  26. static char *fgetlr(char *bp, int bpsize, FILE *fp);
  27. static FILE *find_file(char *bp);
  28. static gotcha(char *bp, char *name);
  29. static char *decode(char *bp, char **area);
  30. static char *do_esc(char *out, char *in);
  31. static void process(void);
  32.  
  33. #define TRUE 1
  34. #define FALSE 0
  35. #define BUFSIZE 1024            /* Assumed size of external buffer */
  36.  
  37. #define NO_FILE     -1            /* Returned if can't open file */
  38. #define NO_ENTRY  0            /* Returned if can't find entry */
  39. #define SUCCESS   1            /* Returned if entry found ok */
  40. #define TRUNCATED 2            /* Returned if entry found but trunc */
  41.  
  42. # ifdef DGK
  43. # define DEFAULT_ROOT "termcap.cnf"        /* name without path component */
  44.   FILE *fopenp();
  45. # endif
  46.  
  47. # ifdef OS2
  48. # define DEFAULT_FILE "termcap.dat"
  49. # else
  50. # define DEFAULT_FILE "/etc/termcap"    /* default termcap filename */
  51. # endif
  52.  
  53. char *_tcpbuf;                          /* Place to remember buffer pointer */
  54.  
  55. # ifdef OS2
  56. # define index strchr
  57. # endif
  58.  
  59.  
  60. /*
  61.  *  LIBRARY FUNCTION
  62.  *
  63.  *    fgetlr    get logical record from a file
  64.  *
  65.  *  KEY WORDS
  66.  *
  67.  *    fgetlr
  68.  *    string functions
  69.  *
  70.  *  SYNOPSIS
  71.  *
  72.  *    char *fgetlr(bp,bpsize,fp)
  73.  *    char *bp;
  74.  *    int bpsize;
  75.  *    FILE *fp;
  76.  *
  77.  *  DESCRIPTION
  78.  *
  79.  *    Reads the next logical record from stream "fp" into buffer "bp"
  80.  *    until next unescaped newline, "bpsize" minus one characters
  81.  *    have been read, end of file, or read error.
  82.  *    The last character read is followed by a NULL.
  83.  *
  84.  *    A logical record may span several physical records by having
  85.  *    each newline escaped with the standard C escape character
  86.  *    (backslash).
  87.  *
  88.  *    This is particularly useful for things like the termcap
  89.  *    file, where a single entry is too long for one physical
  90.  *    line, yet needs to be treated as a single record.
  91.  *
  92.  *    Returns its first argument unless an end of file or read
  93.  *    error occurs prior to any characters being read.
  94.  *
  95.  *  BUGS
  96.  *
  97.  *    The only way to know if read was terminated due to buffer size
  98.  *    limitation is to test for a newline before the terminating
  99.  *    null.
  100.  *
  101.  */
  102.  
  103. /*
  104.  *  PSEUDO CODE
  105.  *
  106.  *    Begin fgetlr
  107.  *        If read fails then
  108.  *        Return NULL.
  109.  *        Else
  110.  *        Find out how many characters were read.
  111.  *        Initialize pointer to terminating null.
  112.  *        If last char read was newline then
  113.  *            If newline was escaped then
  114.  *            Replace backslash with the newline.
  115.  *            Replace newline with null.
  116.  *            Read and append more.
  117.  *            End if
  118.  *        End if
  119.  *        Return buffer pointer.
  120.  *        End if
  121.  *    End fgetlr
  122.  *
  123.  */
  124.  
  125. static char *fgetlr(bp,bpsize,fp)
  126. char *bp;
  127. int bpsize;
  128. FILE *fp;
  129. {
  130.     int numch;
  131.     char *cp;
  132.  
  133.     if (fgets(bp,bpsize,fp) == NULL) {
  134.     return(NULL);
  135.     } else {
  136.     numch = strlen(bp);
  137.     cp = &bp[numch];
  138.     if (*--cp == '\n') {
  139.         if (numch > 1 && *--cp == '\\') {
  140.         *cp++ = '\n';
  141.         *cp = 0;
  142.         fgetlr(cp,bpsize-numch+1,fp);
  143.         }
  144.     }
  145.     return(bp);
  146.     }
  147. }
  148.  
  149.  
  150. #ifndef isdigit
  151. /*
  152.  *  LIBRARY FUNCTION
  153.  *
  154.  *    isdigit    test character for numeric property
  155.  *
  156.  *  SYNOPSIS
  157.  *
  158.  *    int isdigit(ch)
  159.  *    char ch;
  160.  *
  161.  *  DESCRIPTION
  162.  *
  163.  *    Returns TRUE or FALSE depending upon whether the specified
  164.  *    character is a numeric character or not.
  165.  *
  166.  *  BUGS
  167.  *
  168.  *    May fail on machines in which native character set is not ASCII.
  169.  *
  170.  */
  171.  
  172. int isdigit(ch)
  173. char ch;
  174. {
  175.     if (ch > '9' || ch < '0') {
  176.     return(FALSE);
  177.     } else {
  178.     return(TRUE);
  179.     }
  180. }
  181. #endif
  182.  
  183.  
  184. /*
  185.  *  LIBRARY FUNCTION
  186.  *
  187.  *    tgetent   load buffer with entry for specified terminal
  188.  *
  189.  *  KEY WORDS
  190.  *
  191.  *    termcap functions
  192.  *    utility routines
  193.  *
  194.  *  SYNOPSIS
  195.  *
  196.  *    int tgetent(bp,name)
  197.  *    char *bp;
  198.  *    char *name;
  199.  *
  200.  *  DESCRIPTION
  201.  *
  202.  *    Extracts the entry for terminal <name> from the termcap file
  203.  *    and places it in the character buffer <bp>.   It is currently
  204.  *    assumed that bp is at least 1024 characters.  If the entry in
  205.  *    the termcap file is larger than 1023 characters the excess
  206.  *    characters will be discarded and appropriate status will
  207.  *    be returned.
  208.  *
  209.  *    Also note that since bp is used by other termcap
  210.  *    routines, the storage associated with the termcap entry
  211.  *    cannot be freed until all termcap calls are completed.
  212.  *
  213.  *    Tgetent can be directed to look in a file other than
  214.  *    the default (/etc/termcap) by defining an environment
  215.  *    variable called TERMCAP to be the pathname of the desired
  216.  *    termcap file.  This is useful for debugging new entries.
  217.  *    NOTE: the pathname MUST begin with a '/' character.
  218.  *
  219.  *    Also, if the string assigned to TERMCAP does not begin with
  220.  *    a '/' and if the environment variable TERM matches <name> then
  221.  *    the string assigned to TERMCAP is copied to buffer <bp>
  222.  *    instead of reading a termcap file.
  223.  *
  224.  *  RETURNS
  225.  *
  226.  *    -1  if the termcap file cannot be opened
  227.  *     0  if no entry in termcap file matches <name>
  228.  *     1  if extraction is successful with no errors
  229.  *     2  if extraction is successful but entry truncated
  230.  *
  231.  *  SEE ALSO
  232.  *
  233.  *    tgetnum   extract numeric type capability
  234.  *    tgetflag  test boolean type capability
  235.  *    tgetstr   get string value of capability
  236.  *
  237.  *  AUTHOR
  238.  *
  239.  *    Fred Fish
  240.  *
  241.  */
  242.  
  243. /*
  244.  *  PSEUDO CODE
  245.  *
  246.  *    Begin tgetent
  247.  *        Erase any previous buffer contents.
  248.  *        Remember the buffer pointer.
  249.  *        If termcap file is not found then
  250.  *        If buffer was filled anyway then
  251.  *            Return SUCCESS.
  252.  *        Else
  253.  *            Return NO_FILE.
  254.  *        End if
  255.  *        Else
  256.  *        While records left to process
  257.  *            If this is entry is what we want then
  258.  *            Close the termcap file.
  259.  *            If entry was truncated then
  260.  *                Return TRUNCATED status
  261.  *            Else
  262.  *                Return SUCCESS status.
  263.  *            End if
  264.  *            End if
  265.  *        End while
  266.  *        Return NO_ENTRY status.
  267.  *        End if
  268.  *    End tgetent
  269.  *
  270.  */
  271.  
  272. int tgetent(bp,name)
  273. char *bp;                /* Pointer to buffer (1024 char min) */
  274. char *name;                /* Pointer to terminal entry to find */
  275. {
  276.     FILE *fp;
  277.  
  278.     *bp = 0;
  279.     _tcpbuf = bp;
  280.     if ((fp = find_file(bp)) == NULL) {
  281.     if (*bp != 0) {
  282.         return(SUCCESS);
  283.     } else {
  284.         return(NO_FILE);
  285.     }
  286.     } else {
  287.     while (fgetlr(bp,BUFSIZE,fp)) {
  288.         if (gotcha(bp,name)) {
  289.         fclose(fp);
  290.         if (bp[strlen(bp)-1] != '\n') {
  291.             return(TRUNCATED);
  292.         } else {
  293.             return(SUCCESS);
  294.         }
  295.         }
  296.     }
  297.     return(NO_ENTRY);
  298.     }
  299. }
  300.  
  301. /*
  302.  *  INTERNAL FUNCTION
  303.  *
  304.  *    find_file    find the termcap file and open it if possible
  305.  *
  306.  *  KEY WORDS
  307.  *
  308.  *    internal functions
  309.  *    find_file
  310.  *
  311.  *  SYNOPSIS
  312.  *
  313.  *    static FILE *find_file(bp)
  314.  *    char *bp;
  315.  *
  316.  *  DESCRIPTION
  317.  *
  318.  *    Attempts to locate and open the termcap file.  Also handles
  319.  *    using the environment TERMCAP string as the actual buffer
  320.  *    (that's why bp has to be an input parameter).
  321.  *
  322.  *    If TERMCAP is defined an begins with a '/' character then
  323.  *    it is taken to be the pathname of the termcap file and
  324.  *    an attempt is made to open it.  If this fails then
  325.  *    the default termcap file is used instead.
  326.  *
  327.  *    If TERMCAP is defined but does not begin with a '/' then
  328.  *    it is assumed to be the actual buffer contents provided
  329.  *    that <name> matches the environment variable TERM.
  330.  *
  331.  *  BUGS
  332.  *
  333.  *    There is currently no way to be sure which termcap
  334.  *    file was opened since the default will always be
  335.  *    tried.
  336.  *
  337.  */
  338.  
  339. /*
  340.  *  PSEUDO CODE
  341.  *
  342.  *    Begin find_file
  343.  *        If there is a TERMCAP environment string then
  344.  *        If the string is not null then
  345.  *            If the string is a pathname then
  346.  *            If that file is opened successfully then
  347.  *                Return its pointer.
  348.  *            End if
  349.  *            Else
  350.  *            If there is a TERM environment string then
  351.  *                If TERM matches <name> then
  352.  *                Copy TERMCAP string to buffer.
  353.  *                Return NULL for no file.
  354.  *                End if
  355.  *            End if
  356.  *            End if
  357.  *        End if
  358.  *        End if
  359.  *        Open default termcap file and return results.
  360.  *    End find_file
  361.  *
  362.  */
  363.  
  364. static FILE *find_file(bp)
  365. char *bp;
  366. {
  367.     FILE *fp;
  368.     char *cp, *ncp;
  369.  
  370.     if ((cp = getenv("TERMCAP")) != NULL) {
  371.     if (*cp != 0) {
  372.         if (*cp == '/' || *cp == '\\') {
  373.         if ((fp = fopen(cp,"r")) != NULL) {
  374.             return(fp);
  375.         }
  376.         } else {
  377.         if ((ncp = getenv("TERM")) != NULL) {
  378.             if (strcmp(cp,ncp) == 0) {
  379.             strcpy(bp,cp);
  380.             return((FILE *)NULL);
  381.             }
  382.         }
  383.         }
  384.     }
  385.     }
  386. # ifdef DGK
  387.     /* Try current directory, then /etc/termcap, then along the path
  388.      */
  389.     if (fp = fopen(DEFAULT_ROOT, "r"))
  390.         return fp;
  391.     else if (fp = fopen(DEFAULT_FILE, "r"))
  392.         return fp;
  393.     else
  394.         return fopenp(DEFAULT_ROOT, "r", NULL);
  395. # else
  396. # ifdef OS2
  397.   {
  398.     char path[128];
  399.  
  400.     _searchenv(DEFAULT_FILE, "INIT", path);
  401.     if ( path[0] == 0 )
  402.       _searchenv(DEFAULT_FILE, "PATH", path);
  403.  
  404.     return(fopen(path,"r"));
  405.   }
  406. # else
  407.     return(fopen(DEFAULT_FILE,"r"));
  408. # endif
  409. # endif
  410. }
  411.  
  412. /*
  413.  *  INTERNAL FUNCTION
  414.  *
  415.  *    gotcha   test to see if entry is for specified terminal
  416.  *
  417.  *  SYNOPSIS
  418.  *
  419.  *    gotcha(bp,name)
  420.  *    char *bp;
  421.  *    char *name;
  422.  *
  423.  *  DESCRIPTION
  424.  *
  425.  *    Tests to see if the entry in buffer bp matches the terminal
  426.  *    specified by name.  Returns TRUE if match is detected, FALSE
  427.  *    otherwise.
  428.  *
  429.  */
  430.  
  431. /*
  432.  *  PSEUDO CODE
  433.  *
  434.  *    Begin gotcha
  435.  *        If buffer character is comment character then
  436.  *        Return FALSE since remainder is comment
  437.  *        Else
  438.  *        Initialize name scan pointer.
  439.  *        Compare name and buffer until end or mismatch.
  440.  *        If valid terminators for both name and buffer strings
  441.  *            Return TRUE since a match was found.
  442.  *        Else
  443.  *            Find next non-name character in buffer.
  444.  *            If not an alternate name separater character
  445.  *            Return FALSE since no more names to check.
  446.  *            Else
  447.  *            Test next name and return results.
  448.  *            End if
  449.  *        End if
  450.  *        End if
  451.  *    End gotcha
  452.  *
  453.  */
  454.  
  455. static int gotcha(bp,name)
  456. char *bp;
  457. char *name;
  458. {
  459.     char *np;
  460.  
  461.     if (*bp == '#') {
  462.     return(FALSE);
  463.     } else {
  464.     np = name;
  465.     while (*np == *bp && *np != 0) {np++; bp++;}
  466.     if (*np == 0 && (*bp == 0 || *bp == '|' || *bp == ':')) {
  467.         return(TRUE);
  468.     } else {
  469.         while (*bp != 0 && *bp != ':' && *bp != '|') {bp++;}
  470.         if (*bp != '|') {
  471.         return(FALSE);
  472.         } else {
  473.         return(gotcha(++bp,name));
  474.         }
  475.     }
  476.     }
  477. }
  478.  
  479.  
  480. /*
  481.  *  LIBRARY FUNCTION
  482.  *
  483.  *    tgetflag   extract boolean termcap capability
  484.  *
  485.  *  KEY WORDS
  486.  *
  487.  *    termcap
  488.  *
  489.  *  SYNOPSIS
  490.  *
  491.  *    tgetflag(id)
  492.  *    char *id;
  493.  *
  494.  *  DESCRIPTION
  495.  *
  496.  *    Returns TRUE if specified id is present in terminal
  497.  *    entry, FALSE otherwise.
  498.  *
  499.  */
  500.  
  501. /*
  502.  *  PSEUDO CODE
  503.  *
  504.  *    Begin tgetflag
  505.  *        Initialize pointer to the termcap entry buffer.
  506.  *        While there is a field to process
  507.  *        Skip over the field separator character.
  508.  *        If this is the entry we want then
  509.  *            If entry is identifier only then
  510.  *            Return TRUE
  511.  *            Else
  512.  *            Return FALSE
  513.  *            End if
  514.  *        End if
  515.  *        End while
  516.  *        Return FALSE as default.
  517.  *    End tgetflag
  518.  *
  519.  */
  520.  
  521. tgetflag(id)
  522. char *id;
  523. {
  524.     char *bp;
  525.  
  526.     bp = _tcpbuf;
  527.     while ((bp = index(bp,':')) != NULL) {
  528.     bp++;
  529.     if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
  530.         if (*bp == 0 || *bp++ == ':') {
  531.         return(TRUE);
  532.         } else {
  533.         return(FALSE);
  534.         }
  535.     }
  536.     }
  537.     return(FALSE);
  538. }
  539.  
  540.  
  541. /*
  542.  *  LIBRARY FUNCTION
  543.  *
  544.  *    tgetnum   extract numeric option from termcap entry
  545.  *
  546.  *  KEY WORDS
  547.  *
  548.  *    termcap
  549.  *    ce functions
  550.  *
  551.  *  SYNOPSIS
  552.  *
  553.  *    tgetnum(id)
  554.  *    char *id;
  555.  *
  556.  *  DESCRIPTION
  557.  *
  558.  *    Returns numeric value of capability <id>, or -1 if <id>
  559.  *    is not found.   Knows about octal numbers, which
  560.  *    begin with 0.
  561.  *
  562.  */
  563.  
  564. /*
  565.  *  PSEUDO CODE
  566.  *
  567.  *    Begin tgetnum
  568.  *        Initialize pointer to the termcap entry buffer.
  569.  *        While there is a field to process
  570.  *        Skip over the field separator character.
  571.  *        If this is the entry we want then
  572.  *            If the entry is not a numeric then
  573.  *            Return failure value.
  574.  *            Else
  575.  *            Initialize value to zero.
  576.  *            If number begins with zero then
  577.  *                Set accumulation base to 8.
  578.  *            Else
  579.  *                Set accumulation base to 10.
  580.  *            End if
  581.  *            While there is a numeric character
  582.  *                Accumulate the value.
  583.  *            End while
  584.  *            Return value.
  585.  *            End if
  586.  *        End if
  587.  *        End while
  588.  *        Return failure value.
  589.  *    End tgetnum
  590.  *
  591.  */
  592.  
  593. tgetnum(id)
  594. char *id;
  595. {
  596.     int value, base;
  597.     char *bp;
  598.  
  599.     bp = _tcpbuf;
  600.     while ((bp = index(bp,':')) != NULL) {
  601.     bp++;
  602.     if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
  603.         if (*bp != 0 && *bp++ != '#') {
  604.         return(-1);
  605.         } else {
  606.         value = 0;
  607.         if (*bp == '0') {
  608.             base = 8;
  609.         } else {
  610.             base = 10;
  611.         }
  612.         while (isdigit(*bp)) {
  613.             value *= base;
  614.             value += (*bp++ - '0');
  615.         }
  616.         return(value);
  617.         }
  618.     }
  619.     }
  620.     return(-1);
  621. }
  622.  
  623.  
  624. /*
  625.  *  LIBRARY FUNCTION
  626.  *
  627.  *    tgetstr   extract string capability from termcap entry
  628.  *
  629.  *  KEY WORDS
  630.  *
  631.  *    termcap
  632.  *
  633.  *  SYNOPSIS
  634.  *
  635.  *    char *tgetstr(id,area)
  636.  *    char *id;
  637.  *    char **area;
  638.  *
  639.  *  DESCRIPTION
  640.  *
  641.  *    Gets the string capability for <id>, placing it in
  642.  *    the buffer at *area, and advancing *area to point
  643.  *    to next available storage.
  644.  *
  645.  *    For example, if the following capabilities are
  646.  *    in the termcap file:
  647.  *
  648.  *        ZZ=zzzz
  649.  *        YY=yyyyyy
  650.  *        WW=www
  651.  *
  652.  *    then successive calls using YY, ZZ, and WW will
  653.  *    build the following buffer:
  654.  *
  655.  *        yyyyyy0zzzz0www0
  656.  *
  657.  *    The first call will return a pointer to yyyyyy, the
  658.  *    second will return a pointer to zzzz and the third
  659.  *    will return a pointer to www.  Note that each
  660.  *    string is null terminated, as are all C strings.
  661.  *
  662.  *    Characters preceded by the carot character (\136)
  663.  *    are mapped into the corresponding control character.
  664.  *    For example, the two character sequence ^A becomes
  665.  *    a single control-A (\001) character.
  666.  *
  667.  *    The escape character is the normal C backslash and
  668.  *    the normal C escape sequences are recognized, along
  669.  *    with a special sequence for the ASCII escape character
  670.  *    (\033).  The recognized sequences are:
  671.  *
  672.  *        \E   =>  '\033'  (ASCII escape character)
  673.  *        \b   =>  '\010'  (ASCII backspace character)
  674.  *        \f   =>  '\014'  (ASCII form feed character)
  675.  *        \n   =>  '\012'  (ASCII newline/linefeed char)
  676.  *        \r   =>  '\015'  (ASCII carriage return char)
  677.  *        \t   =>  '\011'  (ASCII tab character)
  678.  *        \ddd =>  '\ddd'  (arbitrary ASCII digit)
  679.  *        \x   =>  'x'     (ordinary ASCII character)
  680.  *
  681.  */
  682.  
  683. /*
  684.  *  PSEUDO CODE
  685.  *
  686.  *    Begin tgetstr
  687.  *        Initialize pointer to the termcap entry buffer.
  688.  *        While there is a field to process
  689.  *        Skip over the field separator character.
  690.  *        If this is the entry we want then
  691.  *            If the entry is not a string then
  692.  *            Return NULL.
  693.  *            Else
  694.  *            Transfer string and rtn pointer.
  695.  *            End if
  696.  *        End if
  697.  *        End while
  698.  *        Return NULL
  699.  *    End tgetstr
  700.  *
  701.  */
  702.  
  703. char *tgetstr(id,area)
  704. char *id;
  705. char **area;
  706. {
  707.     char *bp;
  708.     char *decode();
  709.  
  710.     bp = _tcpbuf;
  711.     while ((bp = index(bp,':')) != NULL) {
  712.     bp++;
  713.     if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
  714.         if (*bp != 0 && *bp++ != '=') {
  715.         return(NULL);
  716.         } else {
  717.         return(decode(bp,area));
  718.         }
  719.     }
  720.     }
  721.     return(NULL);
  722. }
  723.  
  724. /*
  725.  *  INTERNAL FUNCTION
  726.  *
  727.  *    decode   transfer string capability, decoding escapes
  728.  *
  729.  *  SYNOPSIS
  730.  *
  731.  *    static char *decode(bp,area)
  732.  *    char *bp;
  733.  *    char **area;
  734.  *
  735.  *  DESCRIPTION
  736.  *
  737.  *    Transfers the string capability, up to the next ':'
  738.  *    character, or null, to the buffer pointed to by
  739.  *    the pointer in *area.  Note that the initial
  740.  *    value of *area and *area is updated to point
  741.  *    to the next available location after the null
  742.  *    terminating the transfered string.
  743.  *
  744.  *  BUGS
  745.  *
  746.  *    There is no overflow checking done on the destination
  747.  *    buffer, so it better be large enough to hold
  748.  *    all expected strings.
  749.  *
  750.  */
  751.  
  752. /*
  753.  *  PSEUDO CODE
  754.  *
  755.  *    Begin decode
  756.  *        Initialize the transfer pointer.
  757.  *        While there is an input character left to process
  758.  *        Switch on input character
  759.  *        Case ESCAPE:
  760.  *            Decode and xfer the escaped sequence.
  761.  *            Break
  762.  *        Case CONTROLIFY:
  763.  *            Controlify and xfer the next character.
  764.  *            Advance the buffer pointer.
  765.  *            Break
  766.  *        Default:
  767.  *            Xfer a normal character.
  768.  *        End switch
  769.  *        End while
  770.  *        Null terminate the output string.
  771.  *        Remember where the output string starts.
  772.  *        Update the output buffer pointer.
  773.  *        Return pointer to the output string.
  774.  *    End decode
  775.  *
  776.  */
  777.  
  778. static char *decode(bp,area)
  779. char *bp;
  780. char **area;
  781. {
  782.     char *cp, *bgn;
  783.     char *do_esc();
  784.  
  785.     cp = *area;
  786.     while (*bp != 0 && *bp != ':') {
  787.     switch(*bp) {
  788.     case '\\':
  789.         bp = do_esc(cp++,++bp);
  790.         break;
  791.     case '^':
  792.         *cp++ = (char) (*++bp & 037);
  793.         bp++;
  794.         break;
  795.     default:
  796.         *cp++ = *bp++;
  797.         break;
  798.     }
  799.     }
  800.     *cp++ = 0;
  801.     bgn = *area;
  802.     *area = cp;
  803.     return(bgn);
  804. }
  805.  
  806. /*
  807.  *  INTERNAL FUNCTION
  808.  *
  809.  *    do_esc    process an escaped sequence
  810.  *
  811.  *  SYNOPSIS
  812.  *
  813.  *    char *do_esc(out,in);
  814.  *    char *out;
  815.  *    char *in;
  816.  *
  817.  *  DESCRIPTION
  818.  *
  819.  *    Processes an escape sequence pointed to by
  820.  *    in, transfering it to location pointed to
  821.  *    by out, and updating the pointer to in.
  822.  *
  823.  */
  824.  
  825. /*
  826.  *  PSEUDO CODE
  827.  *
  828.  *    Begin do_esc
  829.  *        If the first character is not a NULL then
  830.  *        If is a digit then
  831.  *            Set value to zero.
  832.  *            For up to 3 digits
  833.  *                Accumulate the sum.
  834.  *            End for
  835.  *            Transfer the sum.
  836.  *            Else if character is in remap list then
  837.  *            Transfer the remapped character.
  838.  *            Advance the input pointer once.
  839.  *            Else
  840.  *            Simply transfer the character.
  841.  *            End if
  842.  *        End if
  843.  *        Return updated input pointer.
  844.  *    End do_esc
  845.  *
  846.  */
  847.  
  848. static char *maplist = {
  849.     "E\033b\bf\fn\nr\rt\t"
  850. };
  851.  
  852. char *do_esc(out,in)
  853. char *out;
  854. char *in;
  855. {
  856.     int count;
  857.     char ch;
  858.     char *cp;
  859.  
  860.     if (*in != 0) {
  861.     if (isdigit(*in)) {
  862.         ch = 0;
  863.         for (count = 0; count < 3 && isdigit(*in); in++) {
  864.          ch <<= 3;
  865.          ch |= (*in - '0');
  866.         }
  867.         *out++ = ch;
  868.     } else if ((cp = index(maplist,*in)) != NULL) {
  869.         *out++ = *++cp;
  870.         in++;
  871.     } else {
  872.         *out++ = *in++;
  873.     }
  874.     }
  875.     return(in);
  876. }
  877.  
  878.  
  879. /*
  880.  *  LIBRARY FUNCTION
  881.  *
  882.  *    tgoto   expand cursor addressing string from cm capability
  883.  *
  884.  *  KEY WORDS
  885.  *
  886.  *    termcap
  887.  *
  888.  *  SYNOPSIS
  889.  *
  890.  *    char *tgoto(cm,destcol,destline)
  891.  *    char *cm;
  892.  *    int destcol;
  893.  *    int destline;
  894.  *
  895.  *  DESCRIPTION
  896.  *
  897.  *    Returns cursor addressing string, decoded from the cm
  898.  *    capability string, to move cursor to column destcol on
  899.  *    line destline.
  900.  *
  901.  *    The following sequences uses one input argument, either
  902.  *    line or column, and place the appropriate substitution
  903.  *    in the output string:
  904.  *
  905.  *        %d    substitute decimal value (in ASCII)
  906.  *        %2    like %d but forces field width to 2
  907.  *        %3    like %d but forces field width to 3
  908.  *        %.    like %c
  909.  *        %+x    like %c but adds ASCII value of x
  910.  *
  911.  *    The following sequences cause processing modifications
  912.  *    but do not "use up" one of the arguments.  If they
  913.  *    act on an argument they act on the next one to
  914.  *    be converted.
  915.  *
  916.  *        %>xy    if next value to be converted is
  917.  *            greater than value of ASCII char x
  918.  *            then add value of ASCII char y.
  919.  *        %r    reverse substitution of line
  920.  *            and column (line is substituted
  921.  *            first by default).
  922.  *        %i    causes input values destcol and
  923.  *            destline to be incremented.
  924.  *        %%    gives single % character in output.
  925.  *
  926.  *  BUGS
  927.  *
  928.  *    Does not implement some of the more arcane sequences for
  929.  *    radically weird terminals (specifically %n, %B, & %D).
  930.  *    If you have one of these you deserve whatever happens.
  931.  *
  932.  */
  933.  
  934. #define MAXARGS 2
  935.  
  936. static char *in;        /* Internal copy of input string pointer */
  937. static char *out;        /* Pointer to output array */
  938. static int args[MAXARGS];    /* Maximum number of args to convert */
  939. static int pcount;        /* Count of args processed */
  940. static char output[64];        /* Converted string */
  941.  
  942. /*
  943.  *  PSEUDO CODE
  944.  *
  945.  *    Begin tgoto
  946.  *        If no string to process then
  947.  *        Return pointer to error string.
  948.  *        Else
  949.  *        Initialize pointer to input string.
  950.  *        Initialize pointer to result string.
  951.  *        First arg is line number by default.
  952.  *        Second arg is col number by default.
  953.  *        No arguments processed yet.
  954.  *        While there is another character to process
  955.  *            If character is a not a % character then
  956.  *            Simply copy to output.
  957.  *            Else
  958.  *            Process the control sequence.
  959.  *            End if
  960.  *        End while
  961.  *        Return pointer to static output string.
  962.  *        End if
  963.  *    End tgoto
  964.  *
  965.  */
  966.  
  967. char *tgoto(cm,destcol,destline)
  968. char *cm;
  969. int destcol;
  970. int destline;
  971. {
  972.     if (cm == NULL) {
  973.     return("OOPS");
  974.     } else {
  975.     in = cm;
  976.     out = output;
  977.     args[0] = destline;
  978.     args[1] = destcol;
  979.     pcount = 0;
  980.     while (*in != 0) {
  981.         if (*in != '%') {
  982.         *out++ = *in++;
  983.         } else {
  984.         process();
  985.         }
  986.     }
  987.         *out++ = 0;
  988.     return(output);
  989.     }
  990. }
  991.  
  992. /*
  993.  *  INTERNAL FUNCTION
  994.  *
  995.  *    process   process the conversion/command sequence
  996.  *
  997.  *  SYNOPSIS
  998.  *
  999.  *    static process()
  1000.  *
  1001.  *  DESCRIPTION
  1002.  *
  1003.  *    Processes the sequence beginning with the % character.
  1004.  *    Directly manipulates the input string pointer, the
  1005.  *    output string pointer, and the arguments.  Leaves
  1006.  *    the input string pointer pointing to the next character
  1007.  *    to be processed, and the output string pointer pointing
  1008.  *    to the next output location.  If conversion of
  1009.  *    one of the numeric arguments occurs, then the pcount
  1010.  *    is incremented.
  1011.  *
  1012.  */
  1013.  
  1014. /*
  1015.  *  PSEUDO CODE
  1016.  *
  1017.  *    Begin process
  1018.  *        Skip over the % character.
  1019.  *        Switch on next character after %
  1020.  *        Case 'd':
  1021.  *        Process %d type conversion (variable width).
  1022.  *        Reinitialize output pointer.
  1023.  *        Break;
  1024.  *        Case '2':
  1025.  *        Process %d type conversion (width 2).
  1026.  *        Reinitialize output pointer.
  1027.  *        Break;
  1028.  *        Case '3':
  1029.  *        Process %d type conversion (width 3).
  1030.  *        Reinitialize output pointer.
  1031.  *        Break;
  1032.  *        Case '.'
  1033.  *        Process %c type conversion.
  1034.  *        Break;
  1035.  *        Case '+':
  1036.  *        Process %c type conversion with offset.
  1037.  *        Break;
  1038.  *        Case '>':
  1039.  *        Process argument modification.
  1040.  *        Break;
  1041.  *        Case 'r':
  1042.  *        Process argument reversal.
  1043.  *        Break;
  1044.  *        Case 'i':
  1045.  *        Increment argument values.
  1046.  *        Break;
  1047.  *        Case '%':
  1048.  *        Copy to output, incrementing pointers.
  1049.  *        Break;
  1050.  *        End switch
  1051.  *    End process
  1052.  *
  1053.  */
  1054.  
  1055. static void process()
  1056. {
  1057.     int temp;
  1058.  
  1059.     in++;
  1060.     switch(*in++) {
  1061.     case 'd':
  1062.     sprintf(out,"%d",args[pcount++]);
  1063.     out = &output[strlen(output)];
  1064.     break;
  1065.     case '2':
  1066.     sprintf(out,"%02d",args[pcount++]);
  1067.     out = &output[strlen(output)];
  1068.     break;
  1069.     case '3':
  1070.     sprintf(out,"%03d",args[pcount++]);
  1071.     out = &output[strlen(output)];
  1072.     break;
  1073.     case '.':
  1074.     *out++ = (char) args[pcount++];
  1075.     break;
  1076.     case '+':
  1077.     *out++ = (char) args[pcount++] + *in++;
  1078.     break;
  1079.     case '>':
  1080.     if (args[pcount] > (int) *in++) {
  1081.         args[pcount] += *in++;
  1082.     } else {
  1083.         in++;
  1084.     }
  1085.     break;
  1086.     case 'r':
  1087.     temp = args[pcount];
  1088.     args[pcount] = args[pcount+1];
  1089.     args[pcount+1] = temp;
  1090.     break;
  1091.     case 'i':
  1092.     args[pcount]++;
  1093.     args[pcount+1]++;
  1094.     break;
  1095.     case '%':
  1096.     *out++ = '%';
  1097.     break;
  1098.     }
  1099. }
  1100.  
  1101.  
  1102. /*
  1103.  *  LIBRARY FUNCTION
  1104.  *
  1105.  *    tputs     output string with appropriate padding
  1106.  *
  1107.  *  KEY WORDS
  1108.  *
  1109.  *    termcap
  1110.  *
  1111.  *  SYNOPSIS
  1112.  *
  1113.  *    tputs(cp,affcnt,outc)
  1114.  *    char *cp;
  1115.  *    int affcnt;
  1116.  *    int (*outc)();
  1117.  *
  1118.  *  DESCRIPTION
  1119.  *
  1120.  *    Outputs string pointed to by cp, using function outc, and
  1121.  *    following it with the appropriate number of padding characters.
  1122.  *    Affcnt contains the number of lines affected, which is used
  1123.  *    as a multiplier for the specified per line pad time.  If
  1124.  *    per line pad count is not applicable, affcnt should be 1,
  1125.  *    NOT zero.
  1126.  *
  1127.  *    The format of the string pointed to by cp is:
  1128.  *
  1129.  *        [pad time][*]<string to send>
  1130.  *
  1131.  *        where:    pad time => time to delay in milliseconds
  1132.  *            * => specifies that time is per line
  1133.  *
  1134.  *    The pad character is assumed to reside in the external
  1135.  *    variable "PC".  Also, the external variable "ospeed"
  1136.  *    should contain the output speed of the terminal as
  1137.  *    encoded in /usr/include/sgtty.h  (B0-B9600).
  1138.  *
  1139.  *  BUGS
  1140.  *
  1141.  *    Digit conversion is based on native character set
  1142.  *    being ASCII.
  1143.  *
  1144.  */
  1145.  
  1146. /*
  1147.  *    Miscellaneous stuff
  1148.  */
  1149.  
  1150.  
  1151. # ifndef OS2
  1152. extern char PC;            /* Pad character to use */
  1153. extern char ospeed;        /* Encoding of output speed */
  1154.  
  1155. static int times[] = {
  1156.     0,                /* Tenths of ms per char 0 baud */
  1157.     2000,            /* Tenths of ms per char 50 baud */
  1158.     1333,            /* Tenths of ms per char 75 baud */
  1159.     909,            /* Tenths of ms per char 110 baud */
  1160.     743,            /* Tenths of ms per char 134 baud */
  1161.     666,            /* Tenths of ms per char 150 baud */
  1162.     500,            /* Tenths of ms per char 200 baud */
  1163.     333,            /* Tenths of ms per char 300 baud */
  1164.     166,            /* Tenths of ms per char 600 baud */
  1165.     83,                /* Tenths of ms per char 1200 baud */
  1166.     55,                /* Tenths of ms per char 1800 baud */
  1167.     41,                /* Tenths of ms per char 2400 baud */
  1168.     20,                /* Tenths of ms per char 4800 baud */
  1169.     10                /* Tenths of ms per char 9600 baud */
  1170. };
  1171. # endif
  1172.  
  1173. /*
  1174.  *  PSEUDO CODE
  1175.  *
  1176.  *    Begin tgoto
  1177.  *        If string pointer is invalid then
  1178.  *        Return without doing anything.
  1179.  *        Else
  1180.  *        For each pad digit (if any)
  1181.  *            Do decimal left shift.
  1182.  *            Accumulate the lower digit.
  1183.  *        End for
  1184.  *        Adjust scale to tenths of milliseconds
  1185.  *        If there is a fractional field
  1186.  *            Skip the decimal point.
  1187.  *            If there is a valid tenths digit
  1188.  *            Accumulate the tenths.
  1189.  *            End if
  1190.  *            Discard remaining digits.
  1191.  *        End if
  1192.  *        If per line is specified then
  1193.  *            Adjust the pad time.
  1194.  *            Discard the per line flag char.
  1195.  *        End if
  1196.  *        While there are any characters left
  1197.  *            Send them out via output function.
  1198.  *        End while
  1199.  *        Transmit any padding required.
  1200.  *        End if
  1201.  *    End tgoto
  1202.  *
  1203.  */
  1204.  
  1205. void tputs(cp,affcnt,outc)
  1206. char *cp;
  1207. int affcnt;
  1208. int (*outc)(int);
  1209. {
  1210.     int ptime;            /* Pad time in tenths of milliseconds */
  1211.  
  1212.     if (cp == NULL || *cp == 0) {
  1213.     return;
  1214.     } else {
  1215.     for (ptime = 0; isdigit(*cp); cp++) {
  1216.         ptime *= 10;
  1217.         ptime += (*cp - '0');
  1218.     }
  1219.     ptime *= 10;
  1220.     if (*cp == '.') {
  1221.         cp++;
  1222.         if (isdigit(*cp)) {
  1223.         ptime += (*cp++ - '0');
  1224.         }
  1225.         while (isdigit(*cp)) {cp++;}
  1226.     }
  1227.     if (*cp == '*') {
  1228.         ptime *= affcnt;
  1229.         cp++;
  1230.     }
  1231.     while (*cp != 0) {
  1232.         (*outc)(*cp++);
  1233.     }
  1234. # ifndef OS2
  1235.     do_padding(ptime,outc);
  1236. # endif
  1237.     }
  1238. }
  1239.  
  1240. # ifndef OS2
  1241. /*
  1242.  *  FUNCTION
  1243.  *
  1244.  *    do_padding    transmit any pad characters required
  1245.  *
  1246.  *  SYNOPSIS
  1247.  *
  1248.  *    static do_padding(ptime,outc)
  1249.  *    int ptime;
  1250.  *    int (*outc)();
  1251.  *
  1252.  *  DESCRIPTION
  1253.  *
  1254.  *    Does any padding required as specified by ptime (in tenths
  1255.  *    of milliseconds), the output speed given in the external
  1256.  *    variable ospeed, and the pad character given in the
  1257.  *    external variable PC.
  1258.  *
  1259.  */
  1260.  
  1261. /*
  1262.  *  PSEUDO CODE
  1263.  *
  1264.  *    Begin do_padding
  1265.  *        If there is a non-zero pad time then
  1266.  *        If the external speed is in range then
  1267.  *            Look up the delay per pad character.
  1268.  *            Round pad time up by half a character.
  1269.  *            Compute number of characters to send.
  1270.  *            For each pad character to send
  1271.  *            Transmit the pad character.
  1272.  *            End for
  1273.  *        End if
  1274.  *        End if
  1275.  *    End do_padding
  1276.  *
  1277.  */
  1278.  
  1279. static do_padding(ptime,outc)
  1280. int ptime;
  1281. int (*outc)();
  1282. {
  1283.     register int nchars;
  1284.     register int tpc;
  1285.  
  1286.     if (ptime != 0) {
  1287.     if (ospeed >= 0 && ospeed <= (sizeof(times)/ sizeof(int))) {
  1288.         tpc = times[ospeed];
  1289.         ptime += (tpc / 2);
  1290.         nchars = ptime / tpc;
  1291.         for ( ; nchars > 0; --nchars) {
  1292.         (*outc)(PC);
  1293.         }
  1294.     }
  1295.     }
  1296. }
  1297. # endif
  1298.