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