home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / snews-20.zip / TERMCAP.C < prev    next >
C/C++ Source or Header  |  1992-03-28  |  29KB  |  1,300 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 MSDOS
  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 MSDOS
  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 MSDOS
  397.   {
  398.     char path[128];
  399.  
  400.     _searchenv(DEFAULT_FILE, "INIT", path);
  401.     if ( path[0] == 0 )
  402.       _searchenv(DEFAULT_FILE, "PATH", path);
  403.     if ( path[0] == 0 )
  404.       _searchenv(DEFAULT_FILE, "DPATH", path);
  405.  
  406.     return(fopen(path,"r"));
  407.   }
  408. # else
  409.     return(fopen(DEFAULT_FILE,"r"));
  410. # endif
  411. # endif
  412. }
  413.  
  414. /*
  415.  *  INTERNAL FUNCTION
  416.  *
  417.  *    gotcha   test to see if entry is for specified terminal
  418.  *
  419.  *  SYNOPSIS
  420.  *
  421.  *    gotcha(bp,name)
  422.  *    char *bp;
  423.  *    char *name;
  424.  *
  425.  *  DESCRIPTION
  426.  *
  427.  *    Tests to see if the entry in buffer bp matches the terminal
  428.  *    specified by name.  Returns TRUE if match is detected, FALSE
  429.  *    otherwise.
  430.  *
  431.  */
  432.  
  433. /*
  434.  *  PSEUDO CODE
  435.  *
  436.  *    Begin gotcha
  437.  *        If buffer character is comment character then
  438.  *        Return FALSE since remainder is comment
  439.  *        Else
  440.  *        Initialize name scan pointer.
  441.  *        Compare name and buffer until end or mismatch.
  442.  *        If valid terminators for both name and buffer strings
  443.  *            Return TRUE since a match was found.
  444.  *        Else
  445.  *            Find next non-name character in buffer.
  446.  *            If not an alternate name separater character
  447.  *            Return FALSE since no more names to check.
  448.  *            Else
  449.  *            Test next name and return results.
  450.  *            End if
  451.  *        End if
  452.  *        End if
  453.  *    End gotcha
  454.  *
  455.  */
  456.  
  457. static int gotcha(bp,name)
  458. char *bp;
  459. char *name;
  460. {
  461.     char *np;
  462.  
  463.     if (*bp == '#') {
  464.     return(FALSE);
  465.     } else {
  466.     np = name;
  467.     while (*np == *bp && *np != 0) {np++; bp++;}
  468.     if (*np == 0 && (*bp == 0 || *bp == '|' || *bp == ':')) {
  469.         return(TRUE);
  470.     } else {
  471.         while (*bp != 0 && *bp != ':' && *bp != '|') {bp++;}
  472.         if (*bp != '|') {
  473.         return(FALSE);
  474.         } else {
  475.         return(gotcha(++bp,name));
  476.         }
  477.     }
  478.     }
  479. }
  480.  
  481.  
  482. /*
  483.  *  LIBRARY FUNCTION
  484.  *
  485.  *    tgetflag   extract boolean termcap capability
  486.  *
  487.  *  KEY WORDS
  488.  *
  489.  *    termcap
  490.  *
  491.  *  SYNOPSIS
  492.  *
  493.  *    tgetflag(id)
  494.  *    char *id;
  495.  *
  496.  *  DESCRIPTION
  497.  *
  498.  *    Returns TRUE if specified id is present in terminal
  499.  *    entry, FALSE otherwise.
  500.  *
  501.  */
  502.  
  503. /*
  504.  *  PSEUDO CODE
  505.  *
  506.  *    Begin tgetflag
  507.  *        Initialize pointer to the termcap entry buffer.
  508.  *        While there is a field to process
  509.  *        Skip over the field separator character.
  510.  *        If this is the entry we want then
  511.  *            If entry is identifier only then
  512.  *            Return TRUE
  513.  *            Else
  514.  *            Return FALSE
  515.  *            End if
  516.  *        End if
  517.  *        End while
  518.  *        Return FALSE as default.
  519.  *    End tgetflag
  520.  *
  521.  */
  522.  
  523. tgetflag(id)
  524. char *id;
  525. {
  526.     char *bp;
  527.  
  528.     bp = _tcpbuf;
  529.     while ((bp = index(bp,':')) != NULL) {
  530.     bp++;
  531.     if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
  532.         if (*bp == 0 || *bp++ == ':') {
  533.         return(TRUE);
  534.         } else {
  535.         return(FALSE);
  536.         }
  537.     }
  538.     }
  539.     return(FALSE);
  540. }
  541.  
  542.  
  543. /*
  544.  *  LIBRARY FUNCTION
  545.  *
  546.  *    tgetnum   extract numeric option from termcap entry
  547.  *
  548.  *  KEY WORDS
  549.  *
  550.  *    termcap
  551.  *    ce functions
  552.  *
  553.  *  SYNOPSIS
  554.  *
  555.  *    tgetnum(id)
  556.  *    char *id;
  557.  *
  558.  *  DESCRIPTION
  559.  *
  560.  *    Returns numeric value of capability <id>, or -1 if <id>
  561.  *    is not found.   Knows about octal numbers, which
  562.  *    begin with 0.
  563.  *
  564.  */
  565.  
  566. /*
  567.  *  PSEUDO CODE
  568.  *
  569.  *    Begin tgetnum
  570.  *        Initialize pointer to the termcap entry buffer.
  571.  *        While there is a field to process
  572.  *        Skip over the field separator character.
  573.  *        If this is the entry we want then
  574.  *            If the entry is not a numeric then
  575.  *            Return failure value.
  576.  *            Else
  577.  *            Initialize value to zero.
  578.  *            If number begins with zero then
  579.  *                Set accumulation base to 8.
  580.  *            Else
  581.  *                Set accumulation base to 10.
  582.  *            End if
  583.  *            While there is a numeric character
  584.  *                Accumulate the value.
  585.  *            End while
  586.  *            Return value.
  587.  *            End if
  588.  *        End if
  589.  *        End while
  590.  *        Return failure value.
  591.  *    End tgetnum
  592.  *
  593.  */
  594.  
  595. tgetnum(id)
  596. char *id;
  597. {
  598.     int value, base;
  599.     char *bp;
  600.  
  601.     bp = _tcpbuf;
  602.     while ((bp = index(bp,':')) != NULL) {
  603.     bp++;
  604.     if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
  605.         if (*bp != 0 && *bp++ != '#') {
  606.         return(-1);
  607.         } else {
  608.         value = 0;
  609.         if (*bp == '0') {
  610.             base = 8;
  611.         } else {
  612.             base = 10;
  613.         }
  614.         while (isdigit(*bp)) {
  615.             value *= base;
  616.             value += (*bp++ - '0');
  617.         }
  618.         return(value);
  619.         }
  620.     }
  621.     }
  622.     return(-1);
  623. }
  624.  
  625.  
  626. /*
  627.  *  LIBRARY FUNCTION
  628.  *
  629.  *    tgetstr   extract string capability from termcap entry
  630.  *
  631.  *  KEY WORDS
  632.  *
  633.  *    termcap
  634.  *
  635.  *  SYNOPSIS
  636.  *
  637.  *    char *tgetstr(id,area)
  638.  *    char *id;
  639.  *    char **area;
  640.  *
  641.  *  DESCRIPTION
  642.  *
  643.  *    Gets the string capability for <id>, placing it in
  644.  *    the buffer at *area, and advancing *area to point
  645.  *    to next available storage.
  646.  *
  647.  *    For example, if the following capabilities are
  648.  *    in the termcap file:
  649.  *
  650.  *        ZZ=zzzz
  651.  *        YY=yyyyyy
  652.  *        WW=www
  653.  *
  654.  *    then successive calls using YY, ZZ, and WW will
  655.  *    build the following buffer:
  656.  *
  657.  *        yyyyyy0zzzz0www0
  658.  *
  659.  *    The first call will return a pointer to yyyyyy, the
  660.  *    second will return a pointer to zzzz and the third
  661.  *    will return a pointer to www.  Note that each
  662.  *    string is null terminated, as are all C strings.
  663.  *
  664.  *    Characters preceded by the carot character (\136)
  665.  *    are mapped into the corresponding control character.
  666.  *    For example, the two character sequence ^A becomes
  667.  *    a single control-A (\001) character.
  668.  *
  669.  *    The escape character is the normal C backslash and
  670.  *    the normal C escape sequences are recognized, along
  671.  *    with a special sequence for the ASCII escape character
  672.  *    (\033).  The recognized sequences are:
  673.  *
  674.  *        \E   =>  '\033'  (ASCII escape character)
  675.  *        \b   =>  '\010'  (ASCII backspace character)
  676.  *        \f   =>  '\014'  (ASCII form feed character)
  677.  *        \n   =>  '\012'  (ASCII newline/linefeed char)
  678.  *        \r   =>  '\015'  (ASCII carriage return char)
  679.  *        \t   =>  '\011'  (ASCII tab character)
  680.  *        \ddd =>  '\ddd'  (arbitrary ASCII digit)
  681.  *        \x   =>  'x'     (ordinary ASCII character)
  682.  *
  683.  */
  684.  
  685. /*
  686.  *  PSEUDO CODE
  687.  *
  688.  *    Begin tgetstr
  689.  *        Initialize pointer to the termcap entry buffer.
  690.  *        While there is a field to process
  691.  *        Skip over the field separator character.
  692.  *        If this is the entry we want then
  693.  *            If the entry is not a string then
  694.  *            Return NULL.
  695.  *            Else
  696.  *            Transfer string and rtn pointer.
  697.  *            End if
  698.  *        End if
  699.  *        End while
  700.  *        Return NULL
  701.  *    End tgetstr
  702.  *
  703.  */
  704.  
  705. char *tgetstr(id,area)
  706. char *id;
  707. char **area;
  708. {
  709.     char *bp;
  710.     char *decode();
  711.  
  712.     bp = _tcpbuf;
  713.     while ((bp = index(bp,':')) != NULL) {
  714.     bp++;
  715.     if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
  716.         if (*bp != 0 && *bp++ != '=') {
  717.         return(NULL);
  718.         } else {
  719.         return(decode(bp,area));
  720.         }
  721.     }
  722.     }
  723.     return(NULL);
  724. }
  725.  
  726. /*
  727.  *  INTERNAL FUNCTION
  728.  *
  729.  *    decode   transfer string capability, decoding escapes
  730.  *
  731.  *  SYNOPSIS
  732.  *
  733.  *    static char *decode(bp,area)
  734.  *    char *bp;
  735.  *    char **area;
  736.  *
  737.  *  DESCRIPTION
  738.  *
  739.  *    Transfers the string capability, up to the next ':'
  740.  *    character, or null, to the buffer pointed to by
  741.  *    the pointer in *area.  Note that the initial
  742.  *    value of *area and *area is updated to point
  743.  *    to the next available location after the null
  744.  *    terminating the transfered string.
  745.  *
  746.  *  BUGS
  747.  *
  748.  *    There is no overflow checking done on the destination
  749.  *    buffer, so it better be large enough to hold
  750.  *    all expected strings.
  751.  *
  752.  */
  753.  
  754. /*
  755.  *  PSEUDO CODE
  756.  *
  757.  *    Begin decode
  758.  *        Initialize the transfer pointer.
  759.  *        While there is an input character left to process
  760.  *        Switch on input character
  761.  *        Case ESCAPE:
  762.  *            Decode and xfer the escaped sequence.
  763.  *            Break
  764.  *        Case CONTROLIFY:
  765.  *            Controlify and xfer the next character.
  766.  *            Advance the buffer pointer.
  767.  *            Break
  768.  *        Default:
  769.  *            Xfer a normal character.
  770.  *        End switch
  771.  *        End while
  772.  *        Null terminate the output string.
  773.  *        Remember where the output string starts.
  774.  *        Update the output buffer pointer.
  775.  *        Return pointer to the output string.
  776.  *    End decode
  777.  *
  778.  */
  779.  
  780. static char *decode(bp,area)
  781. char *bp;
  782. char **area;
  783. {
  784.     char *cp, *bgn;
  785.     char *do_esc();
  786.  
  787.     cp = *area;
  788.     while (*bp != 0 && *bp != ':') {
  789.     switch(*bp) {
  790.     case '\\':
  791.         bp = do_esc(cp++,++bp);
  792.         break;
  793.     case '^':
  794.         *cp++ = (char) (*++bp & 037);
  795.         bp++;
  796.         break;
  797.     default:
  798.         *cp++ = *bp++;
  799.         break;
  800.     }
  801.     }
  802.     *cp++ = 0;
  803.     bgn = *area;
  804.     *area = cp;
  805.     return(bgn);
  806. }
  807.  
  808. /*
  809.  *  INTERNAL FUNCTION
  810.  *
  811.  *    do_esc    process an escaped sequence
  812.  *
  813.  *  SYNOPSIS
  814.  *
  815.  *    char *do_esc(out,in);
  816.  *    char *out;
  817.  *    char *in;
  818.  *
  819.  *  DESCRIPTION
  820.  *
  821.  *    Processes an escape sequence pointed to by
  822.  *    in, transfering it to location pointed to
  823.  *    by out, and updating the pointer to in.
  824.  *
  825.  */
  826.  
  827. /*
  828.  *  PSEUDO CODE
  829.  *
  830.  *    Begin do_esc
  831.  *        If the first character is not a NULL then
  832.  *        If is a digit then
  833.  *            Set value to zero.
  834.  *            For up to 3 digits
  835.  *                Accumulate the sum.
  836.  *            End for
  837.  *            Transfer the sum.
  838.  *            Else if character is in remap list then
  839.  *            Transfer the remapped character.
  840.  *            Advance the input pointer once.
  841.  *            Else
  842.  *            Simply transfer the character.
  843.  *            End if
  844.  *        End if
  845.  *        Return updated input pointer.
  846.  *    End do_esc
  847.  *
  848.  */
  849.  
  850. static char *maplist = {
  851.     "E\033b\bf\fn\nr\rt\t"
  852. };
  853.  
  854. char *do_esc(out,in)
  855. char *out;
  856. char *in;
  857. {
  858.     int count;
  859.     char ch;
  860.     char *cp;
  861.  
  862.     if (*in != 0) {
  863.     if (isdigit(*in)) {
  864.         ch = 0;
  865.         for (count = 0; count < 3 && isdigit(*in); in++) {
  866.          ch <<= 3;
  867.          ch |= (*in - '0');
  868.         }
  869.         *out++ = ch;
  870.     } else if ((cp = index(maplist,*in)) != NULL) {
  871.         *out++ = *++cp;
  872.         in++;
  873.     } else {
  874.         *out++ = *in++;
  875.     }
  876.     }
  877.     return(in);
  878. }
  879.  
  880.  
  881. /*
  882.  *  LIBRARY FUNCTION
  883.  *
  884.  *    tgoto   expand cursor addressing string from cm capability
  885.  *
  886.  *  KEY WORDS
  887.  *
  888.  *    termcap
  889.  *
  890.  *  SYNOPSIS
  891.  *
  892.  *    char *tgoto(cm,destcol,destline)
  893.  *    char *cm;
  894.  *    int destcol;
  895.  *    int destline;
  896.  *
  897.  *  DESCRIPTION
  898.  *
  899.  *    Returns cursor addressing string, decoded from the cm
  900.  *    capability string, to move cursor to column destcol on
  901.  *    line destline.
  902.  *
  903.  *    The following sequences uses one input argument, either
  904.  *    line or column, and place the appropriate substitution
  905.  *    in the output string:
  906.  *
  907.  *        %d    substitute decimal value (in ASCII)
  908.  *        %2    like %d but forces field width to 2
  909.  *        %3    like %d but forces field width to 3
  910.  *        %.    like %c
  911.  *        %+x    like %c but adds ASCII value of x
  912.  *
  913.  *    The following sequences cause processing modifications
  914.  *    but do not "use up" one of the arguments.  If they
  915.  *    act on an argument they act on the next one to
  916.  *    be converted.
  917.  *
  918.  *        %>xy    if next value to be converted is
  919.  *            greater than value of ASCII char x
  920.  *            then add value of ASCII char y.
  921.  *        %r    reverse substitution of line
  922.  *            and column (line is substituted
  923.  *            first by default).
  924.  *        %i    causes input values destcol and
  925.  *            destline to be incremented.
  926.  *        %%    gives single % character in output.
  927.  *
  928.  *  BUGS
  929.  *
  930.  *    Does not implement some of the more arcane sequences for
  931.  *    radically weird terminals (specifically %n, %B, & %D).
  932.  *    If you have one of these you deserve whatever happens.
  933.  *
  934.  */
  935.  
  936. #define MAXARGS 2
  937.  
  938. static char *in;        /* Internal copy of input string pointer */
  939. static char *out;        /* Pointer to output array */
  940. static int args[MAXARGS];    /* Maximum number of args to convert */
  941. static int pcount;        /* Count of args processed */
  942. static char output[64];        /* Converted string */
  943.  
  944. /*
  945.  *  PSEUDO CODE
  946.  *
  947.  *    Begin tgoto
  948.  *        If no string to process then
  949.  *        Return pointer to error string.
  950.  *        Else
  951.  *        Initialize pointer to input string.
  952.  *        Initialize pointer to result string.
  953.  *        First arg is line number by default.
  954.  *        Second arg is col number by default.
  955.  *        No arguments processed yet.
  956.  *        While there is another character to process
  957.  *            If character is a not a % character then
  958.  *            Simply copy to output.
  959.  *            Else
  960.  *            Process the control sequence.
  961.  *            End if
  962.  *        End while
  963.  *        Return pointer to static output string.
  964.  *        End if
  965.  *    End tgoto
  966.  *
  967.  */
  968.  
  969. char *tgoto(cm,destcol,destline)
  970. char *cm;
  971. int destcol;
  972. int destline;
  973. {
  974.     if (cm == NULL) {
  975.     return("OOPS");
  976.     } else {
  977.     in = cm;
  978.     out = output;
  979.     args[0] = destline;
  980.     args[1] = destcol;
  981.     pcount = 0;
  982.     while (*in != 0) {
  983.         if (*in != '%') {
  984.         *out++ = *in++;
  985.         } else {
  986.         process();
  987.         }
  988.     }
  989.         *out++ = 0;
  990.     return(output);
  991.     }
  992. }
  993.  
  994. /*
  995.  *  INTERNAL FUNCTION
  996.  *
  997.  *    process   process the conversion/command sequence
  998.  *
  999.  *  SYNOPSIS
  1000.  *
  1001.  *    static process()
  1002.  *
  1003.  *  DESCRIPTION
  1004.  *
  1005.  *    Processes the sequence beginning with the % character.
  1006.  *    Directly manipulates the input string pointer, the
  1007.  *    output string pointer, and the arguments.  Leaves
  1008.  *    the input string pointer pointing to the next character
  1009.  *    to be processed, and the output string pointer pointing
  1010.  *    to the next output location.  If conversion of
  1011.  *    one of the numeric arguments occurs, then the pcount
  1012.  *    is incremented.
  1013.  *
  1014.  */
  1015.  
  1016. /*
  1017.  *  PSEUDO CODE
  1018.  *
  1019.  *    Begin process
  1020.  *        Skip over the % character.
  1021.  *        Switch on next character after %
  1022.  *        Case 'd':
  1023.  *        Process %d type conversion (variable width).
  1024.  *        Reinitialize output pointer.
  1025.  *        Break;
  1026.  *        Case '2':
  1027.  *        Process %d type conversion (width 2).
  1028.  *        Reinitialize output pointer.
  1029.  *        Break;
  1030.  *        Case '3':
  1031.  *        Process %d type conversion (width 3).
  1032.  *        Reinitialize output pointer.
  1033.  *        Break;
  1034.  *        Case '.'
  1035.  *        Process %c type conversion.
  1036.  *        Break;
  1037.  *        Case '+':
  1038.  *        Process %c type conversion with offset.
  1039.  *        Break;
  1040.  *        Case '>':
  1041.  *        Process argument modification.
  1042.  *        Break;
  1043.  *        Case 'r':
  1044.  *        Process argument reversal.
  1045.  *        Break;
  1046.  *        Case 'i':
  1047.  *        Increment argument values.
  1048.  *        Break;
  1049.  *        Case '%':
  1050.  *        Copy to output, incrementing pointers.
  1051.  *        Break;
  1052.  *        End switch
  1053.  *    End process
  1054.  *
  1055.  */
  1056.  
  1057. static void process()
  1058. {
  1059.     int temp;
  1060.  
  1061.     in++;
  1062.     switch(*in++) {
  1063.     case 'd':
  1064.     sprintf(out,"%d",args[pcount++]);
  1065.     out = &output[strlen(output)];    
  1066.     break;
  1067.     case '2':
  1068.     sprintf(out,"%02d",args[pcount++]);
  1069.     out = &output[strlen(output)];
  1070.     break;
  1071.     case '3':
  1072.     sprintf(out,"%03d",args[pcount++]);
  1073.     out = &output[strlen(output)];
  1074.     break;
  1075.     case '.':
  1076.     *out++ = (char) args[pcount++];
  1077.     break;
  1078.     case '+':
  1079.     *out++ = (char) args[pcount++] + *in++;
  1080.     break;
  1081.     case '>':
  1082.     if (args[pcount] > (int) *in++) {
  1083.         args[pcount] += *in++;
  1084.     } else {
  1085.         in++;
  1086.     }
  1087.     break;
  1088.     case 'r':
  1089.     temp = args[pcount];
  1090.     args[pcount] = args[pcount+1];
  1091.     args[pcount+1] = temp;
  1092.     break;
  1093.     case 'i':
  1094.     args[pcount]++;
  1095.     args[pcount+1]++;
  1096.     break;
  1097.     case '%':
  1098.     *out++ = '%';
  1099.     break;
  1100.     }
  1101. }
  1102.  
  1103.  
  1104. /*
  1105.  *  LIBRARY FUNCTION
  1106.  *
  1107.  *    tputs     output string with appropriate padding
  1108.  *
  1109.  *  KEY WORDS
  1110.  *
  1111.  *    termcap
  1112.  *
  1113.  *  SYNOPSIS
  1114.  *
  1115.  *    tputs(cp,affcnt,outc)
  1116.  *    char *cp;
  1117.  *    int affcnt;
  1118.  *    int (*outc)();
  1119.  *
  1120.  *  DESCRIPTION
  1121.  *
  1122.  *    Outputs string pointed to by cp, using function outc, and
  1123.  *    following it with the appropriate number of padding characters.
  1124.  *    Affcnt contains the number of lines affected, which is used
  1125.  *    as a multiplier for the specified per line pad time.  If
  1126.  *    per line pad count is not applicable, affcnt should be 1,
  1127.  *    NOT zero.
  1128.  *
  1129.  *    The format of the string pointed to by cp is:
  1130.  *
  1131.  *        [pad time][*]<string to send>
  1132.  *
  1133.  *        where:    pad time => time to delay in milliseconds
  1134.  *            * => specifies that time is per line
  1135.  *            
  1136.  *    The pad character is assumed to reside in the external
  1137.  *    variable "PC".  Also, the external variable "ospeed"
  1138.  *    should contain the output speed of the terminal as
  1139.  *    encoded in /usr/include/sgtty.h  (B0-B9600).
  1140.  *
  1141.  *  BUGS
  1142.  *
  1143.  *    Digit conversion is based on native character set
  1144.  *    being ASCII.
  1145.  *
  1146.  */
  1147.  
  1148. /*
  1149.  *    Miscellaneous stuff
  1150.  */
  1151.  
  1152.  
  1153. # ifndef MSDOS
  1154. extern char PC;            /* Pad character to use */
  1155. extern char ospeed;        /* Encoding of output speed */
  1156.  
  1157. static int times[] = {
  1158.     0,                /* Tenths of ms per char 0 baud */
  1159.     2000,            /* Tenths of ms per char 50 baud */
  1160.     1333,            /* Tenths of ms per char 75 baud */
  1161.     909,            /* Tenths of ms per char 110 baud */
  1162.     743,            /* Tenths of ms per char 134 baud */
  1163.     666,            /* Tenths of ms per char 150 baud */
  1164.     500,            /* Tenths of ms per char 200 baud */
  1165.     333,            /* Tenths of ms per char 300 baud */
  1166.     166,            /* Tenths of ms per char 600 baud */
  1167.     83,                /* Tenths of ms per char 1200 baud */
  1168.     55,                /* Tenths of ms per char 1800 baud */
  1169.     41,                /* Tenths of ms per char 2400 baud */
  1170.     20,                /* Tenths of ms per char 4800 baud */
  1171.     10                /* Tenths of ms per char 9600 baud */
  1172. };
  1173. # endif
  1174.  
  1175. /*
  1176.  *  PSEUDO CODE
  1177.  *
  1178.  *    Begin tgoto
  1179.  *        If string pointer is invalid then
  1180.  *        Return without doing anything.
  1181.  *        Else
  1182.  *        For each pad digit (if any)
  1183.  *            Do decimal left shift.
  1184.  *            Accumulate the lower digit.
  1185.  *        End for
  1186.  *        Adjust scale to tenths of milliseconds
  1187.  *        If there is a fractional field
  1188.  *            Skip the decimal point.
  1189.  *            If there is a valid tenths digit
  1190.  *            Accumulate the tenths.
  1191.  *            End if
  1192.  *            Discard remaining digits.
  1193.  *        End if
  1194.  *        If per line is specified then
  1195.  *            Adjust the pad time.
  1196.  *            Discard the per line flag char.
  1197.  *        End if
  1198.  *        While there are any characters left
  1199.  *            Send them out via output function.
  1200.  *        End while
  1201.  *        Transmit any padding required.
  1202.  *        End if
  1203.  *    End tgoto
  1204.  *
  1205.  */
  1206.  
  1207. void tputs(cp,affcnt,outc)
  1208. char *cp;
  1209. int affcnt;
  1210. int (*outc)(int);
  1211. {
  1212.     int ptime;            /* Pad time in tenths of milliseconds */
  1213.  
  1214.     if (cp == NULL || *cp == 0) {
  1215.     return;
  1216.     } else {
  1217.     for (ptime = 0; isdigit(*cp); cp++) {
  1218.         ptime *= 10;
  1219.         ptime += (*cp - '0');
  1220.     }
  1221.     ptime *= 10;
  1222.     if (*cp == '.') {
  1223.         cp++;
  1224.         if (isdigit(*cp)) {
  1225.         ptime += (*cp++ - '0');
  1226.         }
  1227.         while (isdigit(*cp)) {cp++;}
  1228.     }
  1229.     if (*cp == '*') {
  1230.         ptime *= affcnt;
  1231.         cp++;
  1232.     }
  1233.     while (*cp != 0) {
  1234.         (*outc)(*cp++);
  1235.     }
  1236. # ifndef MSDOS
  1237.     do_padding(ptime,outc);
  1238. # endif
  1239.     }
  1240. }
  1241.  
  1242. # ifndef MSDOS
  1243. /*
  1244.  *  FUNCTION
  1245.  *
  1246.  *    do_padding    transmit any pad characters required
  1247.  *
  1248.  *  SYNOPSIS
  1249.  *
  1250.  *    static do_padding(ptime,outc)
  1251.  *    int ptime;
  1252.  *    int (*outc)();
  1253.  *
  1254.  *  DESCRIPTION
  1255.  *
  1256.  *    Does any padding required as specified by ptime (in tenths
  1257.  *    of milliseconds), the output speed given in the external
  1258.  *    variable ospeed, and the pad character given in the
  1259.  *    external variable PC.
  1260.  *
  1261.  */
  1262.  
  1263. /*
  1264.  *  PSEUDO CODE
  1265.  *
  1266.  *    Begin do_padding
  1267.  *        If there is a non-zero pad time then
  1268.  *        If the external speed is in range then
  1269.  *            Look up the delay per pad character.
  1270.  *            Round pad time up by half a character.
  1271.  *            Compute number of characters to send.
  1272.  *            For each pad character to send
  1273.  *            Transmit the pad character.
  1274.  *            End for
  1275.  *        End if
  1276.  *        End if
  1277.  *    End do_padding
  1278.  *
  1279.  */
  1280.  
  1281. static do_padding(ptime,outc)
  1282. int ptime;
  1283. int (*outc)();
  1284. {
  1285.     register int nchars;
  1286.     register int tpc;
  1287.  
  1288.     if (ptime != 0) {
  1289.     if (ospeed >= 0 && ospeed <= (sizeof(times)/ sizeof(int))) {
  1290.         tpc = times[ospeed];
  1291.         ptime += (tpc / 2);
  1292.         nchars = ptime / tpc;
  1293.         for ( ; nchars > 0; --nchars) {
  1294.         (*outc)(PC);
  1295.         }
  1296.     }
  1297.     }
  1298. }
  1299. # endif
  1300.