]> ncurses.scripts.mit.edu Git - ncurses.git/blob - ncurses/tinfo/lib_tparm.c
ncurses 5.9 - patch 20120901
[ncurses.git] / ncurses / tinfo / lib_tparm.c
1 /****************************************************************************
2  * Copyright (c) 1998-2011,2012 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28
29 /****************************************************************************
30  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32  *     and: Thomas E. Dickey, 1996 on                                       *
33  ****************************************************************************/
34
35 /*
36  *      tparm.c
37  *
38  */
39
40 #include <curses.priv.h>
41
42 #include <ctype.h>
43 #include <tic.h>
44
45 MODULE_ID("$Id: lib_tparm.c,v 1.86 2012/02/22 22:40:24 tom Exp $")
46
47 /*
48  *      char *
49  *      tparm(string, ...)
50  *
51  *      Substitute the given parameters into the given string by the following
52  *      rules (taken from terminfo(5)):
53  *
54  *           Cursor addressing and other strings  requiring  parame-
55  *      ters in the terminal are described by a parameterized string
56  *      capability, with like escapes %x in  it.   For  example,  to
57  *      address  the  cursor, the cup capability is given, using two
58  *      parameters: the row and column to  address  to.   (Rows  and
59  *      columns  are  numbered  from  zero and refer to the physical
60  *      screen visible to the user, not to any  unseen  memory.)  If
61  *      the terminal has memory relative cursor addressing, that can
62  *      be indicated by
63  *
64  *           The parameter mechanism uses  a  stack  and  special  %
65  *      codes  to manipulate it.  Typically a sequence will push one
66  *      of the parameters onto the stack and then print it  in  some
67  *      format.  Often more complex operations are necessary.
68  *
69  *           The % encodings have the following meanings:
70  *
71  *           %%        outputs `%'
72  *           %c        print pop() like %c in printf()
73  *           %s        print pop() like %s in printf()
74  *           %[[:]flags][width[.precision]][doxXs]
75  *                     as in printf, flags are [-+#] and space
76  *                     The ':' is used to avoid making %+ or %-
77  *                     patterns (see below).
78  *
79  *           %p[1-9]   push ith parm
80  *           %P[a-z]   set dynamic variable [a-z] to pop()
81  *           %g[a-z]   get dynamic variable [a-z] and push it
82  *           %P[A-Z]   set static variable [A-Z] to pop()
83  *           %g[A-Z]   get static variable [A-Z] and push it
84  *           %l        push strlen(pop)
85  *           %'c'      push char constant c
86  *           %{nn}     push integer constant nn
87  *
88  *           %+ %- %* %/ %m
89  *                     arithmetic (%m is mod): push(pop() op pop())
90  *           %& %| %^  bit operations: push(pop() op pop())
91  *           %= %> %<  logical operations: push(pop() op pop())
92  *           %A %O     logical and & or operations for conditionals
93  *           %! %~     unary operations push(op pop())
94  *           %i        add 1 to first two parms (for ANSI terminals)
95  *
96  *           %? expr %t thenpart %e elsepart %;
97  *                     if-then-else, %e elsepart is optional.
98  *                     else-if's are possible ala Algol 68:
99  *                     %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
100  *
101  *      For those of the above operators which are binary and not commutative,
102  *      the stack works in the usual way, with
103  *                      %gx %gy %m
104  *      resulting in x mod y, not the reverse.
105  */
106
107 NCURSES_EXPORT_VAR(int) _nc_tparm_err = 0;
108
109 #define TPS(var) _nc_prescreen.tparm_state.var
110
111 #if NO_LEAKS
112 NCURSES_EXPORT(void)
113 _nc_free_tparm(void)
114 {
115     if (TPS(out_buff) != 0) {
116         FreeAndNull(TPS(out_buff));
117         TPS(out_size) = 0;
118         TPS(out_used) = 0;
119         FreeAndNull(TPS(fmt_buff));
120         TPS(fmt_size) = 0;
121     }
122 }
123 #endif
124
125 static NCURSES_INLINE void
126 get_space(size_t need)
127 {
128     need += TPS(out_used);
129     if (need > TPS(out_size)) {
130         TPS(out_size) = need * 2;
131         TPS(out_buff) = typeRealloc(char, TPS(out_size), TPS(out_buff));
132         if (TPS(out_buff) == 0)
133             _nc_err_abort(MSG_NO_MEMORY);
134     }
135 }
136
137 static NCURSES_INLINE void
138 save_text(const char *fmt, const char *s, int len)
139 {
140     size_t s_len = strlen(s);
141     if (len > (int) s_len)
142         s_len = (size_t) len;
143
144     get_space(s_len + 1);
145
146     _nc_SPRINTF(TPS(out_buff) + TPS(out_used),
147                 _nc_SLIMIT(TPS(out_size) - TPS(out_used))
148                 fmt, s);
149     TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used));
150 }
151
152 static NCURSES_INLINE void
153 save_number(const char *fmt, int number, int len)
154 {
155     if (len < 30)
156         len = 30;               /* actually log10(MAX_INT)+1 */
157
158     get_space((size_t) len + 1);
159
160     _nc_SPRINTF(TPS(out_buff) + TPS(out_used),
161                 _nc_SLIMIT(TPS(out_size) - TPS(out_used))
162                 fmt, number);
163     TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used));
164 }
165
166 static NCURSES_INLINE void
167 save_char(int c)
168 {
169     if (c == 0)
170         c = 0200;
171     get_space((size_t) 1);
172     TPS(out_buff)[TPS(out_used)++] = (char) c;
173 }
174
175 static NCURSES_INLINE void
176 npush(int x)
177 {
178     if (TPS(stack_ptr) < STACKSIZE) {
179         TPS(stack)[TPS(stack_ptr)].num_type = TRUE;
180         TPS(stack)[TPS(stack_ptr)].data.num = x;
181         TPS(stack_ptr)++;
182     } else {
183         DEBUG(2, ("npush: stack overflow: %s", _nc_visbuf(TPS(tparam_base))));
184         _nc_tparm_err++;
185     }
186 }
187
188 static NCURSES_INLINE int
189 npop(void)
190 {
191     int result = 0;
192     if (TPS(stack_ptr) > 0) {
193         TPS(stack_ptr)--;
194         if (TPS(stack)[TPS(stack_ptr)].num_type)
195             result = TPS(stack)[TPS(stack_ptr)].data.num;
196     } else {
197         DEBUG(2, ("npop: stack underflow: %s", _nc_visbuf(TPS(tparam_base))));
198         _nc_tparm_err++;
199     }
200     return result;
201 }
202
203 static NCURSES_INLINE void
204 spush(char *x)
205 {
206     if (TPS(stack_ptr) < STACKSIZE) {
207         TPS(stack)[TPS(stack_ptr)].num_type = FALSE;
208         TPS(stack)[TPS(stack_ptr)].data.str = x;
209         TPS(stack_ptr)++;
210     } else {
211         DEBUG(2, ("spush: stack overflow: %s", _nc_visbuf(TPS(tparam_base))));
212         _nc_tparm_err++;
213     }
214 }
215
216 static NCURSES_INLINE char *
217 spop(void)
218 {
219     static char dummy[] = "";   /* avoid const-cast */
220     char *result = dummy;
221     if (TPS(stack_ptr) > 0) {
222         TPS(stack_ptr)--;
223         if (!TPS(stack)[TPS(stack_ptr)].num_type
224             && TPS(stack)[TPS(stack_ptr)].data.str != 0)
225             result = TPS(stack)[TPS(stack_ptr)].data.str;
226     } else {
227         DEBUG(2, ("spop: stack underflow: %s", _nc_visbuf(TPS(tparam_base))));
228         _nc_tparm_err++;
229     }
230     return result;
231 }
232
233 static NCURSES_INLINE const char *
234 parse_format(const char *s, char *format, int *len)
235 {
236     *len = 0;
237     if (format != 0) {
238         bool done = FALSE;
239         bool allowminus = FALSE;
240         bool dot = FALSE;
241         bool err = FALSE;
242         char *fmt = format;
243         int my_width = 0;
244         int my_prec = 0;
245         int value = 0;
246
247         *len = 0;
248         *format++ = '%';
249         while (*s != '\0' && !done) {
250             switch (*s) {
251             case 'c':           /* FALLTHRU */
252             case 'd':           /* FALLTHRU */
253             case 'o':           /* FALLTHRU */
254             case 'x':           /* FALLTHRU */
255             case 'X':           /* FALLTHRU */
256             case 's':
257                 *format++ = *s;
258                 done = TRUE;
259                 break;
260             case '.':
261                 *format++ = *s++;
262                 if (dot) {
263                     err = TRUE;
264                 } else {        /* value before '.' is the width */
265                     dot = TRUE;
266                     my_width = value;
267                 }
268                 value = 0;
269                 break;
270             case '#':
271                 *format++ = *s++;
272                 break;
273             case ' ':
274                 *format++ = *s++;
275                 break;
276             case ':':
277                 s++;
278                 allowminus = TRUE;
279                 break;
280             case '-':
281                 if (allowminus) {
282                     *format++ = *s++;
283                 } else {
284                     done = TRUE;
285                 }
286                 break;
287             default:
288                 if (isdigit(UChar(*s))) {
289                     value = (value * 10) + (*s - '0');
290                     if (value > 10000)
291                         err = TRUE;
292                     *format++ = *s++;
293                 } else {
294                     done = TRUE;
295                 }
296             }
297         }
298
299         /*
300          * If we found an error, ignore (and remove) the flags.
301          */
302         if (err) {
303             my_width = my_prec = value = 0;
304             format = fmt;
305             *format++ = '%';
306             *format++ = *s;
307         }
308
309         /*
310          * Any value after '.' is the precision.  If we did not see '.', then
311          * the value is the width.
312          */
313         if (dot)
314             my_prec = value;
315         else
316             my_width = value;
317
318         *format = '\0';
319         /* return maximum string length in print */
320         *len = (my_width > my_prec) ? my_width : my_prec;
321     }
322     return s;
323 }
324
325 #define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
326 #define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
327
328 /*
329  * Analyze the string to see how many parameters we need from the varargs list,
330  * and what their types are.  We will only accept string parameters if they
331  * appear as a %l or %s format following an explicit parameter reference (e.g.,
332  * %p2%s).  All other parameters are numbers.
333  *
334  * 'number' counts coarsely the number of pop's we see in the string, and
335  * 'popcount' shows the highest parameter number in the string.  We would like
336  * to simply use the latter count, but if we are reading termcap strings, there
337  * may be cases that we cannot see the explicit parameter numbers.
338  */
339 NCURSES_EXPORT(int)
340 _nc_tparm_analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
341 {
342     size_t len2;
343     int i;
344     int lastpop = -1;
345     int len;
346     int number = 0;
347     const char *cp = string;
348     static char dummy[] = "";
349
350     if (cp == 0)
351         return 0;
352
353     if ((len2 = strlen(cp)) > TPS(fmt_size)) {
354         TPS(fmt_size) = len2 + TPS(fmt_size) + 2;
355         TPS(fmt_buff) = typeRealloc(char, TPS(fmt_size), TPS(fmt_buff));
356         if (TPS(fmt_buff) == 0)
357             return 0;
358     }
359
360     memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
361     *popcount = 0;
362
363     while ((cp - string) < (int) len2) {
364         if (*cp == '%') {
365             cp++;
366             cp = parse_format(cp, TPS(fmt_buff), &len);
367             switch (*cp) {
368             default:
369                 break;
370
371             case 'd':           /* FALLTHRU */
372             case 'o':           /* FALLTHRU */
373             case 'x':           /* FALLTHRU */
374             case 'X':           /* FALLTHRU */
375             case 'c':           /* FALLTHRU */
376                 if (lastpop <= 0)
377                     number++;
378                 lastpop = -1;
379                 break;
380
381             case 'l':
382             case 's':
383                 if (lastpop > 0)
384                     p_is_s[lastpop - 1] = dummy;
385                 ++number;
386                 break;
387
388             case 'p':
389                 cp++;
390                 i = (UChar(*cp) - '0');
391                 if (i >= 0 && i <= NUM_PARM) {
392                     lastpop = i;
393                     if (lastpop > *popcount)
394                         *popcount = lastpop;
395                 }
396                 break;
397
398             case 'P':
399                 ++number;
400                 ++cp;
401                 break;
402
403             case 'g':
404                 cp++;
405                 break;
406
407             case S_QUOTE:
408                 cp += 2;
409                 lastpop = -1;
410                 break;
411
412             case L_BRACE:
413                 cp++;
414                 while (isdigit(UChar(*cp))) {
415                     cp++;
416                 }
417                 break;
418
419             case '+':
420             case '-':
421             case '*':
422             case '/':
423             case 'm':
424             case 'A':
425             case 'O':
426             case '&':
427             case '|':
428             case '^':
429             case '=':
430             case '<':
431             case '>':
432                 lastpop = -1;
433                 number += 2;
434                 break;
435
436             case '!':
437             case '~':
438                 lastpop = -1;
439                 ++number;
440                 break;
441
442             case 'i':
443                 /* will add 1 to first (usually two) parameters */
444                 break;
445             }
446         }
447         if (*cp != '\0')
448             cp++;
449     }
450
451     if (number > NUM_PARM)
452         number = NUM_PARM;
453     return number;
454 }
455
456 static NCURSES_INLINE char *
457 tparam_internal(int use_TPARM_ARG, const char *string, va_list ap)
458 {
459     char *p_is_s[NUM_PARM];
460     TPARM_ARG param[NUM_PARM];
461     int popcount = 0;
462     int number;
463     int num_args;
464     int len;
465     int level;
466     int x, y;
467     int i;
468     const char *cp = string;
469     size_t len2;
470
471     if (cp == NULL)
472         return NULL;
473
474     TPS(out_used) = 0;
475     len2 = strlen(cp);
476
477     /*
478      * Find the highest parameter-number referred to in the format string.
479      * Use this value to limit the number of arguments copied from the
480      * variable-length argument list.
481      */
482     number = _nc_tparm_analyze(cp, p_is_s, &popcount);
483     if (TPS(fmt_buff) == 0)
484         return NULL;
485
486     if (number > NUM_PARM)
487         number = NUM_PARM;
488     if (popcount > NUM_PARM)
489         popcount = NUM_PARM;
490     num_args = max(popcount, number);
491
492     for (i = 0; i < num_args; i++) {
493         /*
494          * A few caps (such as plab_norm) have string-valued parms.
495          * We'll have to assume that the caller knows the difference, since
496          * a char* and an int may not be the same size on the stack.  The
497          * normal prototype for this uses 9 long's, which is consistent with
498          * our va_arg() usage.
499          */
500         if (p_is_s[i] != 0) {
501             p_is_s[i] = va_arg(ap, char *);
502             param[i] = 0;
503         } else if (use_TPARM_ARG) {
504             param[i] = va_arg(ap, TPARM_ARG);
505         } else {
506             param[i] = (TPARM_ARG) va_arg(ap, int);
507         }
508     }
509
510     /*
511      * This is a termcap compatibility hack.  If there are no explicit pop
512      * operations in the string, load the stack in such a way that
513      * successive pops will grab successive parameters.  That will make
514      * the expansion of (for example) \E[%d;%dH work correctly in termcap
515      * style, which means tparam() will expand termcap strings OK.
516      */
517     TPS(stack_ptr) = 0;
518     if (popcount == 0) {
519         popcount = number;
520         for (i = number - 1; i >= 0; i--) {
521             if (p_is_s[i])
522                 spush(p_is_s[i]);
523             else
524                 npush((int) param[i]);
525         }
526     }
527 #ifdef TRACE
528     if (USE_TRACEF(TRACE_CALLS)) {
529         for (i = 0; i < num_args; i++) {
530             if (p_is_s[i] != 0)
531                 save_text(", %s", _nc_visbuf(p_is_s[i]), 0);
532             else
533                 save_number(", %d", (int) param[i], 0);
534         }
535         _tracef(T_CALLED("%s(%s%s)"), TPS(tname), _nc_visbuf(cp), TPS(out_buff));
536         TPS(out_used) = 0;
537         _nc_unlock_global(tracef);
538     }
539 #endif /* TRACE */
540
541     while ((cp - string) < (int) len2) {
542         if (*cp != '%') {
543             save_char(UChar(*cp));
544         } else {
545             TPS(tparam_base) = cp++;
546             cp = parse_format(cp, TPS(fmt_buff), &len);
547             switch (*cp) {
548             default:
549                 break;
550             case '%':
551                 save_char('%');
552                 break;
553
554             case 'd':           /* FALLTHRU */
555             case 'o':           /* FALLTHRU */
556             case 'x':           /* FALLTHRU */
557             case 'X':           /* FALLTHRU */
558                 save_number(TPS(fmt_buff), npop(), len);
559                 break;
560
561             case 'c':           /* FALLTHRU */
562                 save_char(npop());
563                 break;
564
565             case 'l':
566                 save_number("%d", (int) strlen(spop()), 0);
567                 break;
568
569             case 's':
570                 save_text(TPS(fmt_buff), spop(), len);
571                 break;
572
573             case 'p':
574                 cp++;
575                 i = (UChar(*cp) - '1');
576                 if (i >= 0 && i < NUM_PARM) {
577                     if (p_is_s[i])
578                         spush(p_is_s[i]);
579                     else
580                         npush((int) param[i]);
581                 }
582                 break;
583
584             case 'P':
585                 cp++;
586                 if (isUPPER(*cp)) {
587                     i = (UChar(*cp) - 'A');
588                     TPS(static_vars)[i] = npop();
589                 } else if (isLOWER(*cp)) {
590                     i = (UChar(*cp) - 'a');
591                     TPS(dynamic_var)[i] = npop();
592                 }
593                 break;
594
595             case 'g':
596                 cp++;
597                 if (isUPPER(*cp)) {
598                     i = (UChar(*cp) - 'A');
599                     npush(TPS(static_vars)[i]);
600                 } else if (isLOWER(*cp)) {
601                     i = (UChar(*cp) - 'a');
602                     npush(TPS(dynamic_var)[i]);
603                 }
604                 break;
605
606             case S_QUOTE:
607                 cp++;
608                 npush(UChar(*cp));
609                 cp++;
610                 break;
611
612             case L_BRACE:
613                 number = 0;
614                 cp++;
615                 while (isdigit(UChar(*cp))) {
616                     number = (number * 10) + (UChar(*cp) - '0');
617                     cp++;
618                 }
619                 npush(number);
620                 break;
621
622             case '+':
623                 npush(npop() + npop());
624                 break;
625
626             case '-':
627                 y = npop();
628                 x = npop();
629                 npush(x - y);
630                 break;
631
632             case '*':
633                 npush(npop() * npop());
634                 break;
635
636             case '/':
637                 y = npop();
638                 x = npop();
639                 npush(y ? (x / y) : 0);
640                 break;
641
642             case 'm':
643                 y = npop();
644                 x = npop();
645                 npush(y ? (x % y) : 0);
646                 break;
647
648             case 'A':
649                 npush(npop() && npop());
650                 break;
651
652             case 'O':
653                 npush(npop() || npop());
654                 break;
655
656             case '&':
657                 npush(npop() & npop());
658                 break;
659
660             case '|':
661                 npush(npop() | npop());
662                 break;
663
664             case '^':
665                 npush(npop() ^ npop());
666                 break;
667
668             case '=':
669                 y = npop();
670                 x = npop();
671                 npush(x == y);
672                 break;
673
674             case '<':
675                 y = npop();
676                 x = npop();
677                 npush(x < y);
678                 break;
679
680             case '>':
681                 y = npop();
682                 x = npop();
683                 npush(x > y);
684                 break;
685
686             case '!':
687                 npush(!npop());
688                 break;
689
690             case '~':
691                 npush(~npop());
692                 break;
693
694             case 'i':
695                 if (p_is_s[0] == 0)
696                     param[0]++;
697                 if (p_is_s[1] == 0)
698                     param[1]++;
699                 break;
700
701             case '?':
702                 break;
703
704             case 't':
705                 x = npop();
706                 if (!x) {
707                     /* scan forward for %e or %; at level zero */
708                     cp++;
709                     level = 0;
710                     while (*cp) {
711                         if (*cp == '%') {
712                             cp++;
713                             if (*cp == '?')
714                                 level++;
715                             else if (*cp == ';') {
716                                 if (level > 0)
717                                     level--;
718                                 else
719                                     break;
720                             } else if (*cp == 'e' && level == 0)
721                                 break;
722                         }
723
724                         if (*cp)
725                             cp++;
726                     }
727                 }
728                 break;
729
730             case 'e':
731                 /* scan forward for a %; at level zero */
732                 cp++;
733                 level = 0;
734                 while (*cp) {
735                     if (*cp == '%') {
736                         cp++;
737                         if (*cp == '?')
738                             level++;
739                         else if (*cp == ';') {
740                             if (level > 0)
741                                 level--;
742                             else
743                                 break;
744                         }
745                     }
746
747                     if (*cp)
748                         cp++;
749                 }
750                 break;
751
752             case ';':
753                 break;
754
755             }                   /* endswitch (*cp) */
756         }                       /* endelse (*cp == '%') */
757
758         if (*cp == '\0')
759             break;
760
761         cp++;
762     }                           /* endwhile (*cp) */
763
764     get_space((size_t) 1);
765     TPS(out_buff)[TPS(out_used)] = '\0';
766
767     T((T_RETURN("%s"), _nc_visbuf(TPS(out_buff))));
768     return (TPS(out_buff));
769 }
770
771 #if NCURSES_TPARM_VARARGS
772 #define tparm_varargs tparm
773 #else
774 #define tparm_proto tparm
775 #endif
776
777 NCURSES_EXPORT(char *)
778 tparm_varargs(NCURSES_CONST char *string,...)
779 {
780     va_list ap;
781     char *result;
782
783     _nc_tparm_err = 0;
784     va_start(ap, string);
785 #ifdef TRACE
786     TPS(tname) = "tparm";
787 #endif /* TRACE */
788     result = tparam_internal(TRUE, string, ap);
789     va_end(ap);
790     return result;
791 }
792
793 #if !NCURSES_TPARM_VARARGS
794 NCURSES_EXPORT(char *)
795 tparm_proto(NCURSES_CONST char *string,
796             TPARM_ARG a1,
797             TPARM_ARG a2,
798             TPARM_ARG a3,
799             TPARM_ARG a4,
800             TPARM_ARG a5,
801             TPARM_ARG a6,
802             TPARM_ARG a7,
803             TPARM_ARG a8,
804             TPARM_ARG a9)
805 {
806     return tparm_varargs(string, a1, a2, a3, a4, a5, a6, a7, a8, a9);
807 }
808 #endif /* NCURSES_TPARM_VARARGS */
809
810 NCURSES_EXPORT(char *)
811 tiparm(const char *string,...)
812 {
813     va_list ap;
814     char *result;
815
816     _nc_tparm_err = 0;
817     va_start(ap, string);
818 #ifdef TRACE
819     TPS(tname) = "tiparm";
820 #endif /* TRACE */
821     result = tparam_internal(FALSE, string, ap);
822     va_end(ap);
823     return result;
824 }