Back to index

tetex-bin  3.0
lib_tparm.c
Go to the documentation of this file.
00001 /****************************************************************************
00002  * Copyright (c) 1998-2003,2004 Free Software Foundation, Inc.              *
00003  *                                                                          *
00004  * Permission is hereby granted, free of charge, to any person obtaining a  *
00005  * copy of this software and associated documentation files (the            *
00006  * "Software"), to deal in the Software without restriction, including      *
00007  * without limitation the rights to use, copy, modify, merge, publish,      *
00008  * distribute, distribute with modifications, sublicense, and/or sell       *
00009  * copies of the Software, and to permit persons to whom the Software is    *
00010  * furnished to do so, subject to the following conditions:                 *
00011  *                                                                          *
00012  * The above copyright notice and this permission notice shall be included  *
00013  * in all copies or substantial portions of the Software.                   *
00014  *                                                                          *
00015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
00016  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
00017  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
00018  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
00019  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
00020  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
00021  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
00022  *                                                                          *
00023  * Except as contained in this notice, the name(s) of the above copyright   *
00024  * holders shall not be used in advertising or otherwise to promote the     *
00025  * sale, use or other dealings in this Software without prior written       *
00026  * authorization.                                                           *
00027  ****************************************************************************/
00028 
00029 /****************************************************************************
00030  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
00031  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
00032  *     and: Thomas E. Dickey, 1996 on                                       *
00033  ****************************************************************************/
00034 
00035 /*
00036  *     tparm.c
00037  *
00038  */
00039 
00040 #include <curses.priv.h>
00041 
00042 #include <ctype.h>
00043 #include <term.h>
00044 #include <tic.h>
00045 
00046 MODULE_ID("$Id: lib_tparm.c,v 1.68 2004/02/07 20:52:51 tom Exp $")
00047 
00048 /*
00049  *     char *
00050  *     tparm(string, ...)
00051  *
00052  *     Substitute the given parameters into the given string by the following
00053  *     rules (taken from terminfo(5)):
00054  *
00055  *          Cursor addressing and other strings  requiring  parame-
00056  *     ters in the terminal are described by a parameterized string
00057  *     capability, with like escapes %x in  it.   For  example,  to
00058  *     address  the  cursor, the cup capability is given, using two
00059  *     parameters: the row and column to  address  to.   (Rows  and
00060  *     columns  are  numbered  from  zero and refer to the physical
00061  *     screen visible to the user, not to any  unseen  memory.)  If
00062  *     the terminal has memory relative cursor addressing, that can
00063  *     be indicated by
00064  *
00065  *          The parameter mechanism uses  a  stack  and  special  %
00066  *     codes  to manipulate it.  Typically a sequence will push one
00067  *     of the parameters onto the stack and then print it  in  some
00068  *     format.  Often more complex operations are necessary.
00069  *
00070  *          The % encodings have the following meanings:
00071  *
00072  *          %%        outputs `%'
00073  *          %c        print pop() like %c in printf()
00074  *          %s        print pop() like %s in printf()
00075  *           %[[:]flags][width[.precision]][doxXs]
00076  *                     as in printf, flags are [-+#] and space
00077  *                     The ':' is used to avoid making %+ or %-
00078  *                     patterns (see below).
00079  *
00080  *          %p[1-9]   push ith parm
00081  *          %P[a-z]   set dynamic variable [a-z] to pop()
00082  *          %g[a-z]   get dynamic variable [a-z] and push it
00083  *          %P[A-Z]   set static variable [A-Z] to pop()
00084  *          %g[A-Z]   get static variable [A-Z] and push it
00085  *          %l        push strlen(pop)
00086  *          %'c'      push char constant c
00087  *          %{nn}     push integer constant nn
00088  *
00089  *          %+ %- %* %/ %m
00090  *                    arithmetic (%m is mod): push(pop() op pop())
00091  *          %& %| %^  bit operations: push(pop() op pop())
00092  *          %= %> %<  logical operations: push(pop() op pop())
00093  *          %A %O     logical and & or operations for conditionals
00094  *          %! %~     unary operations push(op pop())
00095  *          %i        add 1 to first two parms (for ANSI terminals)
00096  *
00097  *          %? expr %t thenpart %e elsepart %;
00098  *                    if-then-else, %e elsepart is optional.
00099  *                    else-if's are possible ala Algol 68:
00100  *                    %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
00101  *
00102  *     For those of the above operators which are binary and not commutative,
00103  *     the stack works in the usual way, with
00104  *                   %gx %gy %m
00105  *     resulting in x mod y, not the reverse.
00106  */
00107 
00108 #define STACKSIZE    20
00109 
00110 typedef struct {
00111     union {
00112        int num;
00113        char *str;
00114     } data;
00115     bool num_type;
00116 } stack_frame;
00117 
00118 NCURSES_EXPORT_VAR(int) _nc_tparm_err = 0;
00119 
00120 static stack_frame stack[STACKSIZE];
00121 static int stack_ptr;
00122 static const char *tparam_base = "";
00123 
00124 #ifdef TRACE
00125 static const char *tname;
00126 #endif /* TRACE */
00127 
00128 static char *out_buff;
00129 static size_t out_size;
00130 static size_t out_used;
00131 
00132 static char *fmt_buff;
00133 static size_t fmt_size;
00134 
00135 #if NO_LEAKS
00136 NCURSES_EXPORT(void)
00137 _nc_free_tparm(void)
00138 {
00139     if (out_buff != 0) {
00140        FreeAndNull(out_buff);
00141        out_size = 0;
00142        out_used = 0;
00143        FreeAndNull(fmt_buff);
00144        fmt_size = 0;
00145     }
00146 }
00147 #endif
00148 
00149 static inline void
00150 get_space(size_t need)
00151 {
00152     need += out_used;
00153     if (need > out_size) {
00154        out_size = need * 2;
00155        out_buff = typeRealloc(char, out_size, out_buff);
00156        if (out_buff == 0)
00157            _nc_err_abort(MSG_NO_MEMORY);
00158     }
00159 }
00160 
00161 static inline void
00162 save_text(const char *fmt, const char *s, int len)
00163 {
00164     size_t s_len = strlen(s);
00165     if (len > (int) s_len)
00166        s_len = len;
00167 
00168     get_space(s_len + 1);
00169 
00170     (void) sprintf(out_buff + out_used, fmt, s);
00171     out_used += strlen(out_buff + out_used);
00172 }
00173 
00174 static inline void
00175 save_number(const char *fmt, int number, int len)
00176 {
00177     if (len < 30)
00178        len = 30;            /* actually log10(MAX_INT)+1 */
00179 
00180     get_space((unsigned) len + 1);
00181 
00182     (void) sprintf(out_buff + out_used, fmt, number);
00183     out_used += strlen(out_buff + out_used);
00184 }
00185 
00186 static inline void
00187 save_char(int c)
00188 {
00189     if (c == 0)
00190        c = 0200;
00191     get_space(1);
00192     out_buff[out_used++] = c;
00193 }
00194 
00195 static inline void
00196 npush(int x)
00197 {
00198     if (stack_ptr < STACKSIZE) {
00199        stack[stack_ptr].num_type = TRUE;
00200        stack[stack_ptr].data.num = x;
00201        stack_ptr++;
00202     } else {
00203        DEBUG(2, ("npush: stack overflow: %s", _nc_visbuf(tparam_base)));
00204        _nc_tparm_err++;
00205     }
00206 }
00207 
00208 static inline int
00209 npop(void)
00210 {
00211     int result = 0;
00212     if (stack_ptr > 0) {
00213        stack_ptr--;
00214        if (stack[stack_ptr].num_type)
00215            result = stack[stack_ptr].data.num;
00216     } else {
00217        DEBUG(2, ("npop: stack underflow: %s", _nc_visbuf(tparam_base)));
00218        _nc_tparm_err++;
00219     }
00220     return result;
00221 }
00222 
00223 static inline void
00224 spush(char *x)
00225 {
00226     if (stack_ptr < STACKSIZE) {
00227        stack[stack_ptr].num_type = FALSE;
00228        stack[stack_ptr].data.str = x;
00229        stack_ptr++;
00230     } else {
00231        DEBUG(2, ("spush: stack overflow: %s", _nc_visbuf(tparam_base)));
00232        _nc_tparm_err++;
00233     }
00234 }
00235 
00236 static inline char *
00237 spop(void)
00238 {
00239     static char dummy[] = "";      /* avoid const-cast */
00240     char *result = dummy;
00241     if (stack_ptr > 0) {
00242        stack_ptr--;
00243        if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0)
00244            result = stack[stack_ptr].data.str;
00245     } else {
00246        DEBUG(2, ("spop: stack underflow: %s", _nc_visbuf(tparam_base)));
00247        _nc_tparm_err++;
00248     }
00249     return result;
00250 }
00251 
00252 static inline const char *
00253 parse_format(const char *s, char *format, int *len)
00254 {
00255     *len = 0;
00256     if (format != 0) {
00257        bool done = FALSE;
00258        bool allowminus = FALSE;
00259        bool dot = FALSE;
00260        bool err = FALSE;
00261        char *fmt = format;
00262        int my_width = 0;
00263        int my_prec = 0;
00264        int value = 0;
00265 
00266        *len = 0;
00267        *format++ = '%';
00268        while (*s != '\0' && !done) {
00269            switch (*s) {
00270            case 'c':        /* FALLTHRU */
00271            case 'd':        /* FALLTHRU */
00272            case 'o':        /* FALLTHRU */
00273            case 'x':        /* FALLTHRU */
00274            case 'X':        /* FALLTHRU */
00275            case 's':
00276               *format++ = *s;
00277               done = TRUE;
00278               break;
00279            case '.':
00280               *format++ = *s++;
00281               if (dot) {
00282                   err = TRUE;
00283               } else {      /* value before '.' is the width */
00284                   dot = TRUE;
00285                   my_width = value;
00286               }
00287               value = 0;
00288               break;
00289            case '#':
00290               *format++ = *s++;
00291               break;
00292            case ' ':
00293               *format++ = *s++;
00294               break;
00295            case ':':
00296               s++;
00297               allowminus = TRUE;
00298               break;
00299            case '-':
00300               if (allowminus) {
00301                   *format++ = *s++;
00302               } else {
00303                   done = TRUE;
00304               }
00305               break;
00306            default:
00307               if (isdigit(UChar(*s))) {
00308                   value = (value * 10) + (*s - '0');
00309                   if (value > 10000)
00310                      err = TRUE;
00311                   *format++ = *s++;
00312               } else {
00313                   done = TRUE;
00314               }
00315            }
00316        }
00317 
00318        /*
00319         * If we found an error, ignore (and remove) the flags.
00320         */
00321        if (err) {
00322            my_width = my_prec = value = 0;
00323            format = fmt;
00324            *format++ = '%';
00325            *format++ = *s;
00326        }
00327 
00328        /*
00329         * Any value after '.' is the precision.  If we did not see '.', then
00330         * the value is the width.
00331         */
00332        if (dot)
00333            my_prec = value;
00334        else
00335            my_width = value;
00336 
00337        *format = '\0';
00338        /* return maximum string length in print */
00339        *len = (my_width > my_prec) ? my_width : my_prec;
00340     }
00341     return s;
00342 }
00343 
00344 #define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
00345 #define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
00346 
00347 /*
00348  * Analyze the string to see how many parameters we need from the varargs list,
00349  * and what their types are.  We will only accept string parameters if they
00350  * appear as a %l or %s format following an explicit parameter reference (e.g.,
00351  * %p2%s).  All other parameters are numbers.
00352  *
00353  * 'number' counts coarsely the number of pop's we see in the string, and
00354  * 'popcount' shows the highest parameter number in the string.  We would like
00355  * to simply use the latter count, but if we are reading termcap strings, there
00356  * may be cases that we cannot see the explicit parameter numbers.
00357  */
00358 NCURSES_EXPORT(int)
00359 _nc_tparm_analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
00360 {
00361     size_t len2;
00362     int i;
00363     int lastpop = -1;
00364     int len;
00365     int number = 0;
00366     const char *cp = string;
00367     static char dummy[] = "";
00368 
00369     if (cp == 0)
00370        return 0;
00371 
00372     if ((len2 = strlen(cp)) > fmt_size) {
00373        fmt_size = len2 + fmt_size + 2;
00374        if ((fmt_buff = typeRealloc(char, fmt_size, fmt_buff)) == 0)
00375              return 0;
00376     }
00377 
00378     memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
00379     *popcount = 0;
00380 
00381     while ((cp - string) < (int) len2) {
00382        if (*cp == '%') {
00383            cp++;
00384            cp = parse_format(cp, fmt_buff, &len);
00385            switch (*cp) {
00386            default:
00387               break;
00388 
00389            case 'd':        /* FALLTHRU */
00390            case 'o':        /* FALLTHRU */
00391            case 'x':        /* FALLTHRU */
00392            case 'X':        /* FALLTHRU */
00393            case 'c':        /* FALLTHRU */
00394               if (lastpop <= 0)
00395                   number++;
00396               lastpop = -1;
00397               break;
00398 
00399            case 'l':
00400            case 's':
00401               if (lastpop > 0)
00402                   p_is_s[lastpop - 1] = dummy;
00403               ++number;
00404               break;
00405 
00406            case 'p':
00407               cp++;
00408               i = (UChar(*cp) - '0');
00409               if (i >= 0 && i <= NUM_PARM) {
00410                   lastpop = i;
00411                   if (lastpop > *popcount)
00412                      *popcount = lastpop;
00413               }
00414               break;
00415 
00416            case 'P':
00417               ++number;
00418               ++cp;
00419               break;
00420 
00421            case 'g':
00422               cp++;
00423               break;
00424 
00425            case S_QUOTE:
00426               cp += 2;
00427               lastpop = -1;
00428               break;
00429 
00430            case L_BRACE:
00431               cp++;
00432               while (isdigit(UChar(*cp))) {
00433                   cp++;
00434               }
00435               break;
00436 
00437            case '+':
00438            case '-':
00439            case '*':
00440            case '/':
00441            case 'm':
00442            case 'A':
00443            case 'O':
00444            case '&':
00445            case '|':
00446            case '^':
00447            case '=':
00448            case '<':
00449            case '>':
00450               lastpop = -1;
00451               number += 2;
00452               break;
00453 
00454            case '!':
00455            case '~':
00456               lastpop = -1;
00457               ++number;
00458               break;
00459 
00460            case 'i':
00461               /* will add 1 to first (usually two) parameters */
00462               break;
00463            }
00464        }
00465        if (*cp != '\0')
00466            cp++;
00467     }
00468 
00469     if (number > NUM_PARM)
00470        number = NUM_PARM;
00471     return number;
00472 }
00473 
00474 static inline char *
00475 tparam_internal(const char *string, va_list ap)
00476 {
00477 #define NUM_VARS 26
00478     char *p_is_s[NUM_PARM];
00479     long param[NUM_PARM];
00480     int popcount;
00481     int number;
00482     int len;
00483     int level;
00484     int x, y;
00485     int i;
00486     const char *cp = string;
00487     size_t len2;
00488     static int dynamic_var[NUM_VARS];
00489     static int static_vars[NUM_VARS];
00490 
00491     if (cp == NULL)
00492        return NULL;
00493 
00494     out_used = 0;
00495     len2 = strlen(cp);
00496 
00497     /*
00498      * Find the highest parameter-number referred to in the format string.
00499      * Use this value to limit the number of arguments copied from the
00500      * variable-length argument list.
00501      */
00502     number = _nc_tparm_analyze(cp, p_is_s, &popcount);
00503     if (fmt_buff == 0)
00504        return NULL;
00505 
00506     for (i = 0; i < max(popcount, number); i++) {
00507        /*
00508         * A few caps (such as plab_norm) have string-valued parms.
00509         * We'll have to assume that the caller knows the difference, since
00510         * a char* and an int may not be the same size on the stack.  The
00511         * normal prototype for this uses 9 long's, which is consistent with
00512         * our va_arg() usage.
00513         */
00514        if (p_is_s[i] != 0) {
00515            p_is_s[i] = va_arg(ap, char *);
00516        } else {
00517            param[i] = va_arg(ap, long int);
00518        }
00519     }
00520 
00521     /*
00522      * This is a termcap compatibility hack.  If there are no explicit pop
00523      * operations in the string, load the stack in such a way that
00524      * successive pops will grab successive parameters.  That will make
00525      * the expansion of (for example) \E[%d;%dH work correctly in termcap
00526      * style, which means tparam() will expand termcap strings OK.
00527      */
00528     stack_ptr = 0;
00529     if (popcount == 0) {
00530        popcount = number;
00531        for (i = number - 1; i >= 0; i--)
00532            npush(param[i]);
00533     }
00534 #ifdef TRACE
00535     if (_nc_tracing & TRACE_CALLS) {
00536        for (i = 0; i < popcount; i++) {
00537            if (p_is_s[i] != 0)
00538               save_text(", %s", _nc_visbuf(p_is_s[i]), 0);
00539            else
00540               save_number(", %d", param[i], 0);
00541        }
00542        _tracef(T_CALLED("%s(%s%s)"), tname, _nc_visbuf(cp), out_buff);
00543        out_used = 0;
00544     }
00545 #endif /* TRACE */
00546 
00547     while ((cp - string) < (int) len2) {
00548        if (*cp != '%') {
00549            save_char(UChar(*cp));
00550        } else {
00551            tparam_base = cp++;
00552            cp = parse_format(cp, fmt_buff, &len);
00553            switch (*cp) {
00554            default:
00555               break;
00556            case '%':
00557               save_char('%');
00558               break;
00559 
00560            case 'd':        /* FALLTHRU */
00561            case 'o':        /* FALLTHRU */
00562            case 'x':        /* FALLTHRU */
00563            case 'X':        /* FALLTHRU */
00564               save_number(fmt_buff, npop(), len);
00565               break;
00566 
00567            case 'c':        /* FALLTHRU */
00568               save_char(npop());
00569               break;
00570 
00571            case 'l':
00572               save_number("%d", (int) strlen(spop()), 0);
00573               break;
00574 
00575            case 's':
00576               save_text(fmt_buff, spop(), len);
00577               break;
00578 
00579            case 'p':
00580               cp++;
00581               i = (UChar(*cp) - '1');
00582               if (i >= 0 && i < NUM_PARM) {
00583                   if (p_is_s[i])
00584                      spush(p_is_s[i]);
00585                   else
00586                      npush(param[i]);
00587               }
00588               break;
00589 
00590            case 'P':
00591               cp++;
00592               if (isUPPER(*cp)) {
00593                   i = (UChar(*cp) - 'A');
00594                   static_vars[i] = npop();
00595               } else if (isLOWER(*cp)) {
00596                   i = (UChar(*cp) - 'a');
00597                   dynamic_var[i] = npop();
00598               }
00599               break;
00600 
00601            case 'g':
00602               cp++;
00603               if (isUPPER(*cp)) {
00604                   i = (UChar(*cp) - 'A');
00605                   npush(static_vars[i]);
00606               } else if (isLOWER(*cp)) {
00607                   i = (UChar(*cp) - 'a');
00608                   npush(dynamic_var[i]);
00609               }
00610               break;
00611 
00612            case S_QUOTE:
00613               cp++;
00614               npush(UChar(*cp));
00615               cp++;
00616               break;
00617 
00618            case L_BRACE:
00619               number = 0;
00620               cp++;
00621               while (isdigit(UChar(*cp))) {
00622                   number = (number * 10) + (UChar(*cp) - '0');
00623                   cp++;
00624               }
00625               npush(number);
00626               break;
00627 
00628            case '+':
00629               npush(npop() + npop());
00630               break;
00631 
00632            case '-':
00633               y = npop();
00634               x = npop();
00635               npush(x - y);
00636               break;
00637 
00638            case '*':
00639               npush(npop() * npop());
00640               break;
00641 
00642            case '/':
00643               y = npop();
00644               x = npop();
00645               npush(y ? (x / y) : 0);
00646               break;
00647 
00648            case 'm':
00649               y = npop();
00650               x = npop();
00651               npush(y ? (x % y) : 0);
00652               break;
00653 
00654            case 'A':
00655               npush(npop() && npop());
00656               break;
00657 
00658            case 'O':
00659               npush(npop() || npop());
00660               break;
00661 
00662            case '&':
00663               npush(npop() & npop());
00664               break;
00665 
00666            case '|':
00667               npush(npop() | npop());
00668               break;
00669 
00670            case '^':
00671               npush(npop() ^ npop());
00672               break;
00673 
00674            case '=':
00675               y = npop();
00676               x = npop();
00677               npush(x == y);
00678               break;
00679 
00680            case '<':
00681               y = npop();
00682               x = npop();
00683               npush(x < y);
00684               break;
00685 
00686            case '>':
00687               y = npop();
00688               x = npop();
00689               npush(x > y);
00690               break;
00691 
00692            case '!':
00693               npush(!npop());
00694               break;
00695 
00696            case '~':
00697               npush(~npop());
00698               break;
00699 
00700            case 'i':
00701               if (p_is_s[0] == 0)
00702                   param[0]++;
00703               if (p_is_s[1] == 0)
00704                   param[1]++;
00705               break;
00706 
00707            case '?':
00708               break;
00709 
00710            case 't':
00711               x = npop();
00712               if (!x) {
00713                   /* scan forward for %e or %; at level zero */
00714                   cp++;
00715                   level = 0;
00716                   while (*cp) {
00717                      if (*cp == '%') {
00718                          cp++;
00719                          if (*cp == '?')
00720                             level++;
00721                          else if (*cp == ';') {
00722                             if (level > 0)
00723                                 level--;
00724                             else
00725                                 break;
00726                          } else if (*cp == 'e' && level == 0)
00727                             break;
00728                      }
00729 
00730                      if (*cp)
00731                          cp++;
00732                   }
00733               }
00734               break;
00735 
00736            case 'e':
00737               /* scan forward for a %; at level zero */
00738               cp++;
00739               level = 0;
00740               while (*cp) {
00741                   if (*cp == '%') {
00742                      cp++;
00743                      if (*cp == '?')
00744                          level++;
00745                      else if (*cp == ';') {
00746                          if (level > 0)
00747                             level--;
00748                          else
00749                             break;
00750                      }
00751                   }
00752 
00753                   if (*cp)
00754                      cp++;
00755               }
00756               break;
00757 
00758            case ';':
00759               break;
00760 
00761            }                /* endswitch (*cp) */
00762        }                    /* endelse (*cp == '%') */
00763 
00764        if (*cp == '\0')
00765            break;
00766 
00767        cp++;
00768     }                       /* endwhile (*cp) */
00769 
00770     get_space(1);
00771     out_buff[out_used] = '\0';
00772 
00773     T((T_RETURN("%s"), _nc_visbuf(out_buff)));
00774     return (out_buff);
00775 }
00776 
00777 NCURSES_EXPORT(char *)
00778 tparm(NCURSES_CONST char *string,...)
00779 {
00780     va_list ap;
00781     char *result;
00782 
00783     _nc_tparm_err = 0;
00784     va_start(ap, string);
00785 #ifdef TRACE
00786     tname = "tparm";
00787 #endif /* TRACE */
00788     result = tparam_internal(string, ap);
00789     va_end(ap);
00790     return result;
00791 }