home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume26 / mytinfo / part03 / tparm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-12-26  |  16.2 KB  |  811 lines

  1. /*
  2.  * tparm.c
  3.  *
  4.  * By Ross Ridge
  5.  * Public Domain
  6.  * 92/02/01 07:30:36
  7.  *
  8.  */
  9.  
  10. #include "defs.h"
  11. #include "term.h"
  12.  
  13. #include <ctype.h>
  14.  
  15. #ifdef USE_SCCS_IDS
  16. static const char SCCSid[] = "@(#) mytinfo tparm.c 3.2 92/02/01 public domain, By Ross Ridge";
  17. #endif
  18.  
  19. #ifndef MAX_PUSHED
  20. #define MAX_PUSHED    32
  21. #endif
  22.  
  23. #define ARG    1
  24. #define NUM    2
  25.  
  26. #define INTEGER    1
  27. #define STRING    2
  28.  
  29. typedef struct stack_str {
  30.     int    type;
  31.     int    argnum;
  32.     int    value;
  33. } stack;
  34.  
  35. static stack S[MAX_PUSHED];
  36. static stack vars['z'-'a'+1];
  37. static int pos = 0;
  38.  
  39. static struct arg_str {
  40.     int type;
  41.     int    integer;
  42.     char    *string;
  43. } arg_list[10];
  44.  
  45. static int argcnt;
  46.  
  47. static va_list tparm_args;
  48.  
  49. static int
  50. pusharg(arg)
  51. int arg; {
  52.     if (pos == MAX_PUSHED)
  53.         return 1;
  54.     S[pos].type = ARG;
  55.     S[pos++].argnum = arg;
  56.     return 0;
  57. }
  58.  
  59. static int
  60. pushnum(num)
  61. int num; {
  62.     if (pos == MAX_PUSHED)
  63.         return 1;
  64.     S[pos].type = NUM;
  65.     S[pos++].value = num;
  66.     return 0;
  67. }
  68.  
  69. /* VARARGS2 */
  70. static int
  71. getarg(argnum, type, p)
  72. int argnum, type;
  73. anyptr p; {
  74.     while (argcnt < argnum) {
  75.         arg_list[argcnt].type = INTEGER;
  76.         arg_list[argcnt++].integer = (int) va_arg(tparm_args, int);
  77.     }
  78.     if (argcnt > argnum) {
  79.         if (arg_list[argnum].type != type)
  80.             return 1;
  81.         else if (type == STRING)
  82.             *(char **)p = arg_list[argnum].string;
  83.         else 
  84.             *(int *)p = arg_list[argnum].integer;
  85.     } else {
  86.         arg_list[argcnt].type = type;
  87.         if (type == STRING)
  88.             *(char **)p = arg_list[argcnt++].string
  89.                 = (char *) va_arg(tparm_args, char *);
  90.         else
  91.             *(int *)p = arg_list[argcnt++].integer = (int) va_arg(tparm_args, int);
  92.     }
  93.     return 0;
  94. }
  95.  
  96.  
  97. static int
  98. popstring(str)
  99. char **str; {
  100.     if (pos-- == 0)
  101.         return 1;
  102.     if (S[pos].type != ARG)
  103.         return 1;
  104.     return(getarg(S[pos].argnum, STRING, (anyptr) str));
  105. }
  106.  
  107. static int
  108. popnum(num)
  109. int *num; {
  110.     if (pos-- == 0)
  111.         return 1;
  112.     switch (S[pos].type) {
  113.     case ARG:
  114.         return (getarg(S[pos].argnum, INTEGER, (anyptr) num));
  115.     case NUM:
  116.         *num = S[pos].value;
  117.         return 0;
  118.     }
  119.     return 1;
  120. }
  121.  
  122. static int
  123. cvtchar(sp, c)
  124. register char *sp, *c; {
  125.     switch(*sp) {
  126.     case '\\':
  127.         switch(*++sp) {
  128.         case '\'':
  129.         case '$':
  130.         case '\\':
  131.         case '%':
  132.             *c = *sp;
  133.             return 2;
  134.         case '\0':
  135.             *c = '\\';
  136.             return 1;
  137.         case '0':
  138.             if (sp[1] == '0' && sp[2] == '0') {
  139.                 *c = '\0';
  140.                 return 4;
  141.             }
  142.             *c = '\200'; /* '\0' ???? */
  143.             return 2;
  144.         default:
  145.             *c = *sp;
  146.             return 2;
  147.         }
  148.     default:
  149.         *c = *sp;
  150.         return 1;
  151.     }
  152. }
  153.  
  154. static int termcap;
  155.  
  156. /* sigh... this has got to be the ugliest code I've ever written.
  157.    Trying to handle everything has its cost, I guess.
  158.  
  159.    It actually isn't to hard to figure out if a given % code is supposed
  160.    to be interpeted with its termcap or terminfo meaning since almost
  161.    all terminfo codes are invalid unless something has been pushed on
  162.    the stack and termcap strings will never push things on the stack
  163.    (%p isn't used by termcap). So where we have a choice we make the
  164.    decision by wether or not somthing has been pushed on the stack.
  165.    The static variable termcap keeps track of this; it starts out set
  166.    to 1 and is incremented as each argument processed by a termcap % code,
  167.    however if something is pushed on the stack it's set to 0 and the
  168.    rest of the % codes are interpeted as terminfo % codes. Another way
  169.    of putting it is that if termcap equals one we haven't decided either
  170.    way yet, if it equals zero we're looking for terminfo codes, and if
  171.    its greater than 1 we're looking for termcap codes.
  172.  
  173.    Terminfo % codes:
  174.  
  175.     %%    output a '%'
  176.     %[[:][-+# ][width][.precision]][doxXs]
  177.         output pop according to the printf format
  178.     %c    output pop as a char
  179.     %'c'    push character constant c.
  180.     %{n}    push decimal constant n.
  181.     %p[1-9] push paramter [1-9]
  182.     %g[a-z] push variable [a-z]
  183.     %P[a-z] put pop in variable [a-z]
  184.     %l    push the length of pop (a string)
  185.     %+    add pop to pop and push the result
  186.     %-    subtract pop from pop and push the result
  187.     %*    multiply pop and pop and push the result
  188.     %&    bitwise and pop and pop and push the result
  189.     %|    bitwise or pop and pop and push the result
  190.     %^    bitwise xor pop and pop and push the result
  191.     %~    push the bitwise not of pop
  192.     %=    compare if pop and pop are equal and push the result
  193.     %>    compare if pop is less than pop and push the result
  194.     %<    compare if pop is greater than pop and push the result
  195.     %A    logical and pop and pop and push the result
  196.     %O    logical or pop and pop and push the result
  197.     %!    push the logical not of pop
  198.     %? condition %t if_true [%e if_false] %;
  199.         if condtion evaulates as true then evaluate if_true,
  200.         else evaluate if_false. elseif's can be done:
  201. %? cond %t true [%e cond2 %t true2] ... [%e condN %t trueN] [%e false] %;
  202.     %i    add one to parameters 1 and 2. (ANSI)
  203.  
  204.   Termcap Codes:
  205.  
  206.     %%    output a %
  207.     %.    output parameter as a character
  208.     %d    output parameter as a decimal number
  209.     %2    output parameter in printf format %02d
  210.     %3    output parameter in printf format %03d
  211.     %+x    add the character x to parameter and output it as a character
  212. (UW)    %-x    subtract parameter FROM the character x and output it as a char
  213. (UW)    %ax    add the character x to parameter
  214. (GNU)    %a[+*-/=][cp]x
  215.         GNU arithmetic. 
  216. (UW)    %sx    subtract parameter FROM the character x
  217.     %>xy    if parameter > character x then add character y to parameter
  218.     %B    convert to BCD (parameter = (parameter/10)*16 + parameter%16)
  219.     %D    Delta Data encode (parameter = parameter - 2*(paramter%16))
  220.     %i    increment the first two parameters by one
  221.     %n    xor the first two parameters by 0140
  222. (GNU)    %m    xor the first two parameters by 0177
  223.     %r    swap the first two parameters
  224. (GNU)    %b    backup to previous parameter
  225. (GNU)    %f    skip this parameter
  226.  
  227.   Note the two definitions of %a, the GNU defintion is used if the characters
  228.   after the 'a' are valid, otherwise the UW definition is used.
  229.  
  230.   (GNU) used by GNU Emacs termcap libraries
  231.   (UW) used by the University of Waterloo (MFCF) termcap libraries
  232.  
  233. */
  234.  
  235. #ifdef lint
  236. /* VARARGS1 */
  237. char *
  238. tparm(str)
  239. char *str; {
  240. #else /* lint */
  241. #ifdef USE_STDARG
  242. #ifdef USE_PROTOTYPES
  243. char *tparm(char *str, ...) {
  244. #else /* USE_PROTOTYPES */
  245. char *tparm(str)
  246. char *str; {
  247. #endif /* USE_PROTOTYPES */
  248. #else /* USE_STDARG */
  249. char *tparm(va_alist)
  250. va_dcl {
  251.     char *str;
  252. #endif
  253. #endif
  254.     static char OOPS[] = "OOPS";
  255.     static char buf[MAX_LINE];
  256.     register char *sp, *dp;
  257.     register char *fmt;
  258.     char conv_char;
  259.     char scan_for;
  260.     int scan_depth, if_depth;
  261.     static int i, j;
  262.     static char *s, c;
  263.     char fmt_buf[MAX_LINE];
  264.     char sbuf[MAX_LINE];
  265.  
  266. #ifdef lint
  267.     scan_depth = 0;
  268. #else
  269. #ifdef USE_STDARG
  270.     va_start(tparm_args, str);
  271. #else
  272.     va_start(tparm_args);
  273.     str = (char *) va_arg(tparm_args, char *);
  274. #endif
  275. #endif
  276.  
  277.     sp = str;
  278.     dp = buf;
  279.     scan_for = 0;
  280.     if_depth = 0;
  281.     argcnt = 0;
  282.     pos = 0;
  283.     termcap = 1;
  284.     while(*sp != '\0') {
  285.         switch(*sp) {
  286.         case '\\':
  287.             if (scan_for) {
  288.                 if (*++sp != '\0')
  289.                     sp++;
  290.                 break;
  291.             }
  292.             *dp++ = *sp++;
  293.             if (*sp != '\0')
  294.                 *dp++ = *sp++;
  295.             break;
  296.         case '%':
  297.             sp++;
  298.             if (scan_for) {
  299.                 if (*sp == scan_for && if_depth == scan_depth) {
  300.                     if (scan_for == ';')
  301.                         if_depth--;
  302.                     scan_for = 0;
  303.                 } else if (*sp == '?')
  304.                     if_depth++;
  305.                 else if (*sp == ';') {
  306.                     if (if_depth == 0)
  307.                         return OOPS;
  308.                     else
  309.                         if_depth--;
  310.                 }
  311.                 sp++;
  312.                 break;
  313.             }
  314.             fmt = NULL;
  315.             switch(*sp) {
  316.             case '%':
  317.                 *dp++ = *sp++;
  318.                 break;
  319.             case '+':
  320.                 if (!termcap) {
  321.                     if (popnum(&j) || popnum(&i))
  322.                         return OOPS;
  323.                     i += j;
  324.                     if (pushnum(i))
  325.                         return OOPS;
  326.                     sp++;
  327.                     break;
  328.                 }
  329.                 ;/* FALLTHROUGH */
  330.             case 'C':
  331.                 if (*sp == 'C') { 
  332.                     if (getarg(termcap - 1, INTEGER, &i))
  333.                         return OOPS;
  334.                     if (i >= 96) {
  335.                         i /= 96;
  336.                         if (i == '$')
  337.                             *dp++ = '\\';
  338.                         *dp++ = i;
  339.                     }
  340.                 }
  341.                 fmt = "%c";
  342.                 /* FALLTHROUGH */
  343.             case 'a':
  344.                 if (!termcap)
  345.                     return OOPS;
  346.                 if (getarg(termcap - 1, INTEGER, (anyptr) &i))
  347.                     return OOPS;
  348.                 if (*++sp == '\0')
  349.                     return OOPS;
  350.                 if ((sp[1] == 'p' || sp[1] == 'c')
  351.                         && sp[2] != '\0' && fmt == NULL) {
  352.                     /* GNU aritmitic parameter, what they
  353.                        realy need is terminfo.          */
  354.                     int val, lc;
  355.                     if (sp[1] == 'p'
  356.                         && getarg(termcap - 1 + sp[2] - '@',
  357.                               INTEGER, (anyptr) &val))
  358.                         return OOPS;
  359.                     if (sp[1] == 'c') {
  360.                         lc = cvtchar(sp + 2, &c) + 2;
  361.                     /* Mask out 8th bit so \200 can be
  362.                        used for \0 as per GNU doc's    */
  363.                         val = c & 0177;
  364.                     } else
  365.                         lc = 2;
  366.                     switch(sp[0]) {
  367.                     case '=':
  368.                         break;
  369.                     case '+':
  370.                         val = i + val;
  371.                         break;
  372.                     case '-':
  373.                         val = i - val;
  374.                         break;
  375.                     case '*':
  376.                         val = i * val;
  377.                         break;
  378.                     case '/':
  379.                         val = i / val;
  380.                         break;
  381.                     default:
  382.                     /* Not really GNU's %a after all... */
  383.                         lc = cvtchar(sp, &c);
  384.                         val = c + i;
  385.                         break;
  386.                     }
  387.                     arg_list[termcap - 1].integer = val;
  388.                     sp += lc;
  389.                     break;
  390.                 }
  391.                 sp += cvtchar(sp, &c);
  392.                 arg_list[termcap - 1].integer = c + i;
  393.                 if (fmt == NULL)
  394.                     break;
  395.                 sp--;
  396.                 /* FALLTHROUGH */
  397.             case '-':
  398.                 if (!termcap) {
  399.                     if (popnum(&j) || popnum(&i))
  400.                         return OOPS;
  401.                     i -= j;
  402.                     if (pushnum(i))
  403.                         return OOPS;
  404.                     sp++;
  405.                     break;
  406.                 }
  407.                 fmt = "%c";
  408.                 /* FALLTHROUGH */
  409.             case 's':
  410.                 if (termcap && (fmt == NULL || *sp == '-')) {
  411.                     if (getarg(termcap - 1, INTEGER, &i))
  412.                         return OOPS;
  413.                     if (*++sp == '\0')
  414.                         return OOPS;
  415.                     sp += cvtchar(sp, &c);
  416.                     arg_list[termcap - 1].integer = c - i;
  417.                     if (fmt == NULL)
  418.                         break;
  419.                     sp--;
  420.                 }
  421.                 if (!termcap)
  422.                     return OOPS;
  423.                 ;/* FALLTHROUGH */
  424.             case '.':
  425.                 if (termcap && fmt == NULL) 
  426.                     fmt = "%c";
  427.                 ;/* FALLTHROUGH */
  428.             case 'd':
  429.                 if (termcap && fmt == NULL) 
  430.                     fmt = "%d";
  431.                 ;/* FALLTHROUGH */
  432.             case '2':
  433.                 if (termcap && fmt == NULL)
  434.                     fmt = "%02d";
  435.                 ;/* FALLTHROUGH */
  436.             case '3':
  437.                 if (termcap && fmt == NULL) 
  438.                     fmt = "%03d";
  439.                 ;/* FALLTHROUGH */
  440.             case ':': case ' ': case '#': case 'u':
  441.             case 'x': case 'X': case 'o': case 'c':
  442.             case '0': case '1': case '4': case '5':
  443.             case '6': case '7': case '8': case '9':
  444.                 if (fmt == NULL) {
  445.                     if (termcap)
  446.                         return OOPS;
  447.                     if (*sp == ':')
  448.                         sp++;
  449.                     fmt = fmt_buf;
  450.                     *fmt++ = '%';
  451.                     while(*sp != 's' && *sp != 'x' && *sp != 'X' && *sp != 'd' && *sp != 'o' && *sp != 'c' && *sp != 'u') {
  452.                         if (*sp == '\0')
  453.                             return OOPS;
  454.                         *fmt++ = *sp++;
  455.                     }
  456.                     *fmt++ = *sp;
  457.                     *fmt = '\0';
  458.                     fmt = fmt_buf;
  459.                 }
  460.                 conv_char = fmt[strlen(fmt) - 1];
  461.                 if (conv_char == 's') {
  462.                     if (popstring(&s))
  463.                         return OOPS;
  464.                     sprintf(sbuf, fmt, s);
  465.                 } else {
  466.                     if (termcap) {
  467.                         if (getarg(termcap++ - 1,
  468.                                INTEGER, &i))
  469.                             return OOPS;
  470.                     } else
  471.                         if (popnum(&i))
  472.                             return OOPS;
  473.                     if (i == 0 && conv_char == 'c')
  474.                         strcpy(sbuf, "\000");
  475.                     else
  476.                         sprintf(sbuf, fmt, i);
  477.                 }
  478.                 sp++;
  479.                 fmt = sbuf;
  480.                 while(*fmt != '\0') {
  481.                     if (*fmt == '$') 
  482.                         *dp++ = '\\';
  483.                     *dp++ = *fmt++;
  484.                 }
  485.                 break;
  486.             case 'r':
  487.                 if (!termcap || getarg(1, INTEGER, &i))
  488.                     return OOPS;
  489.                 arg_list[1].integer = arg_list[0].integer;
  490.                 arg_list[0].integer = i;
  491.                 sp++;
  492.                 break;
  493.             case 'i':
  494.                 if (getarg(1, INTEGER, &i)
  495.                     || arg_list[0].type != INTEGER)
  496.                     return OOPS;
  497.                 arg_list[1].integer++;
  498.                 arg_list[0].integer++;
  499.                 sp++;
  500.                 break;
  501.             case 'n':
  502.                 if (!termcap || getarg(1, INTEGER, &i))
  503.                     return OOPS;
  504.                 arg_list[0].integer ^= 0140;
  505.                 arg_list[1].integer ^= 0140;
  506.                 sp++;
  507.                 break;
  508.             case '>':
  509.                 if (!termcap) {
  510.                     if (popnum(&j) || popnum(&i))
  511.                         return OOPS;
  512.                     i = (i > j);
  513.                     if (pushnum(i))
  514.                         return OOPS;
  515.                     sp++;
  516.                     break;
  517.                 }
  518.                 if (getarg(termcap-1, INTEGER, &i))
  519.                     return OOPS;
  520.                 sp += cvtchar(sp, &c);
  521.                 if (i > c) {
  522.                     sp += cvtchar(sp, &c);
  523.                     arg_list[termcap-1].integer += c;
  524.                 } else
  525.                     sp += cvtchar(sp, &c);
  526.                 sp++;
  527.                 break;
  528.             case 'B':
  529.                 if (!termcap || getarg(termcap-1, INTEGER, &i))
  530.                     return OOPS;
  531.                 arg_list[termcap-1].integer = 16*(i/10)+i%10;
  532.                 sp++;
  533.                 break;
  534.             case 'D':
  535.                 if (!termcap || getarg(termcap-1, INTEGER, &i))
  536.                     return OOPS;
  537.                 arg_list[termcap-1].integer = i - 2 * (i % 16);
  538.                 sp++;
  539.                 break;
  540.             case 'p':
  541.                 if (termcap > 1)
  542.                     return OOPS;
  543.                 if (*++sp == '\0')
  544.                     return OOPS;
  545.                 if (*sp == '0')
  546.                     i = 9;
  547.                 else
  548.                     i = *sp - '1';
  549.                 if (i < 0 || i > 9)
  550.                     return OOPS;
  551.                 if (pusharg(i))
  552.                     return OOPS;
  553.                 termcap = 0;
  554.                 sp++;
  555.                 break;
  556.             case 'P':
  557.                 if (termcap || *++sp == '\0')
  558.                     return OOPS;
  559.                 i = *sp++ - 'a';
  560.                 if (i < 0 || i > 25)
  561.                     return OOPS;
  562.                 if (pos-- == 0)
  563.                     return OOPS;
  564.                 switch(vars[i].type = S[pos].type) {
  565.                 case ARG:
  566.                     vars[i].argnum = S[pos].argnum;
  567.                     break;
  568.                 case NUM:
  569.                     vars[i].value = S[pos].value;
  570.                     break;
  571.                 }
  572.                 break;
  573.             case 'g':
  574.                 if (termcap || *++sp == '\0')
  575.                     return OOPS;
  576.                 i = *sp++ - 'a';
  577.                 if (i < 0 || i > 25)
  578.                     return OOPS;
  579.                 switch(vars[i].type) {
  580.                 case ARG:
  581.                     if (pusharg(vars[i].argnum))
  582.                         return OOPS;
  583.                     break;
  584.                 case NUM:
  585.                     if (pushnum(vars[i].value))
  586.                         return OOPS;
  587.                     break;
  588.                 }
  589.                 break;
  590.             case '\'':
  591.                 if (termcap > 1)
  592.                     return OOPS;
  593.                 if (*++sp == '\0')
  594.                     return OOPS;
  595.                 sp += cvtchar(sp, &c);
  596.                 if (pushnum(c) || *sp++ != '\'')
  597.                     return OOPS;
  598.                 termcap = 0;
  599.                 break;
  600.             case '{':
  601.                 if (termcap > 1)
  602.                     return OOPS;
  603.                 i = 0;
  604.                 sp++;
  605.                 while(isdigit(*sp))
  606.                     i = 10 * i + *sp++ - '0';
  607.                 if (*sp++ != '}' || pushnum(i))
  608.                     return OOPS;
  609.                 termcap = 0;
  610.                 break;
  611.             case 'l':
  612.                 if (termcap || popstring(&s))
  613.                     return OOPS;
  614.                 i = strlen(s);
  615.                 if (pushnum(i))
  616.                     return OOPS;
  617.                 sp++;
  618.                 break;
  619.             case '*':
  620.                 if (termcap || popnum(&j) || popnum(&i))
  621.                     return OOPS;
  622.                 i *= j;
  623.                 if (pushnum(i))
  624.                     return OOPS;
  625.                 sp++;
  626.                 break;
  627.             case '/':
  628.                 if (termcap || popnum(&j) || popnum(&i))
  629.                     return OOPS;
  630.                 i /= j;
  631.                 if (pushnum(i))
  632.                     return OOPS;
  633.                 sp++;
  634.                 break;
  635.             case 'm':
  636.                 if (termcap) {
  637.                     if (getarg(1, INTEGER, &i))
  638.                         return OOPS;
  639.                     arg_list[0].integer ^= 0177;
  640.                     arg_list[1].integer ^= 0177;
  641.                     sp++;
  642.                     break;
  643.                 }
  644.                 if (popnum(&j) || popnum(&i))
  645.                     return OOPS;
  646.                 i %= j;
  647.                 if (pushnum(i))
  648.                     return OOPS;
  649.                 sp++;
  650.                 break;
  651.             case '&':
  652.                 if (popnum(&j) || popnum(&i))
  653.                     return OOPS;
  654.                 i &= j;
  655.                 if (pushnum(i))
  656.                     return OOPS;
  657.                 sp++;
  658.                 break;
  659.             case '|':
  660.                 if (popnum(&j) || popnum(&i))
  661.                     return OOPS;
  662.                 i |= j;
  663.                 if (pushnum(i))
  664.                     return OOPS;
  665.                 sp++;
  666.                 break;
  667.             case '^':
  668.                 if (popnum(&j) || popnum(&i))
  669.                     return OOPS;
  670.                 i ^= j;
  671.                 if (pushnum(i))
  672.                     return OOPS;
  673.                 sp++;
  674.                 break;
  675.             case '=':
  676.                 if (popnum(&j) || popnum(&i))
  677.                     return OOPS;
  678.                 i = (i == j);
  679.                 if (pushnum(i))
  680.                     return OOPS;
  681.                 sp++;
  682.                 break;
  683.             case '<':
  684.                 if (popnum(&j) || popnum(&i))
  685.                     return OOPS;
  686.                 i = (i < j);
  687.                 if (pushnum(i))
  688.                     return OOPS;
  689.                 sp++;
  690.                 break;
  691.             case 'A':
  692.                 if (popnum(&j) || popnum(&i))
  693.                     return OOPS;
  694.                 i = (i && j);
  695.                 if (pushnum(i))
  696.                     return OOPS;
  697.                 sp++;
  698.                 break;
  699.             case 'O':
  700.                 if (popnum(&j) || popnum(&i))
  701.                     return OOPS;
  702.                 i = (i || j);
  703.                 if (pushnum(i))
  704.                     return OOPS;
  705.                 sp++;
  706.                 break;
  707.             case '!':
  708.                 if (popnum(&i))
  709.                     return OOPS;
  710.                 i = !i;
  711.                 if (pushnum(i))
  712.                     return OOPS;
  713.                 sp++;
  714.                 break;
  715.             case '~':
  716.                 if (popnum(&i))
  717.                     return OOPS;
  718.                 i = ~i;
  719.                 if (pushnum(i))
  720.                     return OOPS;
  721.                 sp++;
  722.                 break;
  723.             case '?':
  724.                 if (termcap > 1)
  725.                     return OOPS;
  726.                 termcap = 0;
  727.                 if_depth++;
  728.                 sp++;
  729.                 break;
  730.             case 't':
  731.                 if (popnum(&i) || if_depth == 0)
  732.                     return OOPS;
  733.                 if (!i) {
  734.                     scan_for = 'e';
  735.                     scan_depth = if_depth;
  736.                 }
  737.                 sp++;
  738.                 break;
  739.             case 'e':
  740.                 if (if_depth == 0)
  741.                     return OOPS;
  742.                 scan_for = ';';
  743.                 scan_depth = if_depth;
  744.                 sp++;
  745.                 break;
  746.             case ';':
  747.                 if (if_depth-- == 0)
  748.                     return OOPS;
  749.                 sp++;
  750.                 break;
  751.             case 'b':
  752.                 if (--termcap < 1)
  753.                     return OOPS;
  754.                 sp++;
  755.                 break;
  756.             case 'f':
  757.                 if (!termcap++)
  758.                     return OOPS;
  759.                 sp++;
  760.                 break;
  761.             }
  762.             break;
  763.         default:
  764.             if (scan_for)
  765.                 sp++;
  766.             else
  767.                 *dp++ = *sp++;
  768.             break;
  769.         }
  770.     }
  771.     va_end(tparm_args);
  772.     *dp = '\0';
  773.     return buf;
  774. }
  775.  
  776. #ifdef TEST
  777.  
  778. void
  779. putch(c)
  780. int c; {
  781.     c &= 0xff;
  782.     if (c > 127 || c < 0) {
  783.         printf("\\%03o", c);
  784.     } else if (c < 32) {
  785.         printf("^%c", c + '@');
  786.     } else if (c == 127) {
  787.         printf("^?");
  788.     } else {
  789.         putchar(c);
  790.     }
  791. }
  792.  
  793. char line[MAX_LINE];
  794.  
  795. int
  796. main(argc, argv)
  797. int argc;
  798. char **argv; {
  799.     register char *sp;
  800.     putchar('\n');
  801.  
  802.     while(gets(line) != NULL) {
  803.         sp = tparm(line, 1, -2, 30, 0, "bob was here");
  804.         while(*sp)
  805.             putch(*sp++);
  806.         putchar('\n');
  807.     }
  808.     return 0;
  809. }
  810. #endif
  811.