1 /**************************************************************************** 2 * Copyright (c) 1998-2014,2015 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.94 2015/07/17 01:03:35 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 escapes like %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 #define popcount _nc_popcount /* workaround for NetBSD 6.0 defect */ 111 112 #if NO_LEAKS 113 NCURSES_EXPORT(void) 114 _nc_free_tparm(void) 115 { 116 if (TPS(out_buff) != 0) { 117 FreeAndNull(TPS(out_buff)); 118 TPS(out_size) = 0; 119 TPS(out_used) = 0; 120 FreeAndNull(TPS(fmt_buff)); 121 TPS(fmt_size) = 0; 122 } 123 } 124 #endif 125 126 static NCURSES_INLINE void 127 get_space(size_t need) 128 { 129 need += TPS(out_used); 130 if (need > TPS(out_size)) { 131 TPS(out_size) = need * 2; 132 TYPE_REALLOC(char, TPS(out_size), TPS(out_buff)); 133 } 134 } 135 136 static NCURSES_INLINE void 137 save_text(const char *fmt, const char *s, int len) 138 { 139 size_t s_len = strlen(s); 140 if (len > (int) s_len) 141 s_len = (size_t) len; 142 143 get_space(s_len + 1); 144 145 _nc_SPRINTF(TPS(out_buff) + TPS(out_used), 146 _nc_SLIMIT(TPS(out_size) - TPS(out_used)) 147 fmt, s); 148 TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used)); 149 } 150 151 static NCURSES_INLINE void 152 save_number(const char *fmt, int number, int len) 153 { 154 if (len < 30) 155 len = 30; /* actually log10(MAX_INT)+1 */ 156 157 get_space((size_t) len + 1); 158 159 _nc_SPRINTF(TPS(out_buff) + TPS(out_used), 160 _nc_SLIMIT(TPS(out_size) - TPS(out_used)) 161 fmt, number); 162 TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used)); 163 } 164 165 static NCURSES_INLINE void 166 save_char(int c) 167 { 168 if (c == 0) 169 c = 0200; 170 get_space((size_t) 1); 171 TPS(out_buff)[TPS(out_used)++] = (char) c; 172 } 173 174 static NCURSES_INLINE void 175 npush(int x) 176 { 177 if (TPS(stack_ptr) < STACKSIZE) { 178 TPS(stack)[TPS(stack_ptr)].num_type = TRUE; 179 TPS(stack)[TPS(stack_ptr)].data.num = x; 180 TPS(stack_ptr)++; 181 } else { 182 DEBUG(2, ("npush: stack overflow: %s", _nc_visbuf(TPS(tparam_base)))); 183 _nc_tparm_err++; 184 } 185 } 186 187 static NCURSES_INLINE int 188 npop(void) 189 { 190 int result = 0; 191 if (TPS(stack_ptr) > 0) { 192 TPS(stack_ptr)--; 193 if (TPS(stack)[TPS(stack_ptr)].num_type) 194 result = TPS(stack)[TPS(stack_ptr)].data.num; 195 } else { 196 DEBUG(2, ("npop: stack underflow: %s", _nc_visbuf(TPS(tparam_base)))); 197 _nc_tparm_err++; 198 } 199 return result; 200 } 201 202 static NCURSES_INLINE void 203 spush(char *x) 204 { 205 if (TPS(stack_ptr) < STACKSIZE) { 206 TPS(stack)[TPS(stack_ptr)].num_type = FALSE; 207 TPS(stack)[TPS(stack_ptr)].data.str = x; 208 TPS(stack_ptr)++; 209 } else { 210 DEBUG(2, ("spush: stack overflow: %s", _nc_visbuf(TPS(tparam_base)))); 211 _nc_tparm_err++; 212 } 213 } 214 215 static NCURSES_INLINE char * 216 spop(void) 217 { 218 static char dummy[] = ""; /* avoid const-cast */ 219 char *result = dummy; 220 if (TPS(stack_ptr) > 0) { 221 TPS(stack_ptr)--; 222 if (!TPS(stack)[TPS(stack_ptr)].num_type 223 && TPS(stack)[TPS(stack_ptr)].data.str != 0) 224 result = TPS(stack)[TPS(stack_ptr)].data.str; 225 } else { 226 DEBUG(2, ("spop: stack underflow: %s", _nc_visbuf(TPS(tparam_base)))); 227 _nc_tparm_err++; 228 } 229 return result; 230 } 231 232 static NCURSES_INLINE const char * 233 parse_format(const char *s, char *format, int *len) 234 { 235 *len = 0; 236 if (format != 0) { 237 bool done = FALSE; 238 bool allowminus = FALSE; 239 bool dot = FALSE; 240 bool err = FALSE; 241 char *fmt = format; 242 int my_width = 0; 243 int my_prec = 0; 244 int value = 0; 245 246 *len = 0; 247 *format++ = '%'; 248 while (*s != '\0' && !done) { 249 switch (*s) { 250 case 'c': /* FALLTHRU */ 251 case 'd': /* FALLTHRU */ 252 case 'o': /* FALLTHRU */ 253 case 'x': /* FALLTHRU */ 254 case 'X': /* FALLTHRU */ 255 case 's': 256 #ifdef EXP_XTERM_1005 257 case 'u': 258 #endif 259 *format++ = *s; 260 done = TRUE; 261 break; 262 case '.': 263 *format++ = *s++; 264 if (dot) { 265 err = TRUE; 266 } else { /* value before '.' is the width */ 267 dot = TRUE; 268 my_width = value; 269 } 270 value = 0; 271 break; 272 case '#': 273 *format++ = *s++; 274 break; 275 case ' ': 276 *format++ = *s++; 277 break; 278 case ':': 279 s++; 280 allowminus = TRUE; 281 break; 282 case '-': 283 if (allowminus) { 284 *format++ = *s++; 285 } else { 286 done = TRUE; 287 } 288 break; 289 default: 290 if (isdigit(UChar(*s))) { 291 value = (value * 10) + (*s - '0'); 292 if (value > 10000) 293 err = TRUE; 294 *format++ = *s++; 295 } else { 296 done = TRUE; 297 } 298 } 299 } 300 301 /* 302 * If we found an error, ignore (and remove) the flags. 303 */ 304 if (err) { 305 my_width = my_prec = value = 0; 306 format = fmt; 307 *format++ = '%'; 308 *format++ = *s; 309 } 310 311 /* 312 * Any value after '.' is the precision. If we did not see '.', then 313 * the value is the width. 314 */ 315 if (dot) 316 my_prec = value; 317 else 318 my_width = value; 319 320 *format = '\0'; 321 /* return maximum string length in print */ 322 *len = (my_width > my_prec) ? my_width : my_prec; 323 } 324 return s; 325 } 326 327 #define isUPPER(c) ((c) >= 'A' && (c) <= 'Z') 328 #define isLOWER(c) ((c) >= 'a' && (c) <= 'z') 329 330 /* 331 * Analyze the string to see how many parameters we need from the varargs list, 332 * and what their types are. We will only accept string parameters if they 333 * appear as a %l or %s format following an explicit parameter reference (e.g., 334 * %p2%s). All other parameters are numbers. 335 * 336 * 'number' counts coarsely the number of pop's we see in the string, and 337 * 'popcount' shows the highest parameter number in the string. We would like 338 * to simply use the latter count, but if we are reading termcap strings, there 339 * may be cases that we cannot see the explicit parameter numbers. 340 */ 341 NCURSES_EXPORT(int) 342 _nc_tparm_analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount) 343 { 344 size_t len2; 345 int i; 346 int lastpop = -1; 347 int len; 348 int number = 0; 349 const char *cp = string; 350 static char dummy[] = ""; 351 352 if (cp == 0) 353 return 0; 354 355 if ((len2 = strlen(cp)) > TPS(fmt_size)) { 356 TPS(fmt_size) = len2 + TPS(fmt_size) + 2; 357 TPS(fmt_buff) = typeRealloc(char, TPS(fmt_size), TPS(fmt_buff)); 358 if (TPS(fmt_buff) == 0) 359 return 0; 360 } 361 362 memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM); 363 *popcount = 0; 364 365 while ((cp - string) < (int) len2) { 366 if (*cp == '%') { 367 cp++; 368 cp = parse_format(cp, TPS(fmt_buff), &len); 369 switch (*cp) { 370 default: 371 break; 372 373 case 'd': /* FALLTHRU */ 374 case 'o': /* FALLTHRU */ 375 case 'x': /* FALLTHRU */ 376 case 'X': /* FALLTHRU */ 377 case 'c': /* FALLTHRU */ 378 #ifdef EXP_XTERM_1005 379 case 'u': 380 #endif 381 if (lastpop <= 0) 382 number++; 383 lastpop = -1; 384 break; 385 386 case 'l': 387 case 's': 388 if (lastpop > 0) 389 p_is_s[lastpop - 1] = dummy; 390 ++number; 391 break; 392 393 case 'p': 394 cp++; 395 i = (UChar(*cp) - '0'); 396 if (i >= 0 && i <= NUM_PARM) { 397 lastpop = i; 398 if (lastpop > *popcount) 399 *popcount = lastpop; 400 } 401 break; 402 403 case 'P': 404 ++number; 405 ++cp; 406 break; 407 408 case 'g': 409 cp++; 410 break; 411 412 case S_QUOTE: 413 cp += 2; 414 lastpop = -1; 415 break; 416 417 case L_BRACE: 418 cp++; 419 while (isdigit(UChar(*cp))) { 420 cp++; 421 } 422 break; 423 424 case '+': 425 case '-': 426 case '*': 427 case '/': 428 case 'm': 429 case 'A': 430 case 'O': 431 case '&': 432 case '|': 433 case '^': 434 case '=': 435 case '<': 436 case '>': 437 lastpop = -1; 438 number += 2; 439 break; 440 441 case '!': 442 case '~': 443 lastpop = -1; 444 ++number; 445 break; 446 447 case 'i': 448 /* will add 1 to first (usually two) parameters */ 449 break; 450 } 451 } 452 if (*cp != '\0') 453 cp++; 454 } 455 456 if (number > NUM_PARM) 457 number = NUM_PARM; 458 return number; 459 } 460 461 static NCURSES_INLINE char * 462 tparam_internal(int use_TPARM_ARG, const char *string, va_list ap) 463 { 464 char *p_is_s[NUM_PARM]; 465 TPARM_ARG param[NUM_PARM]; 466 int popcount = 0; 467 int number; 468 int num_args; 469 int len; 470 int level; 471 int x, y; 472 int i; 473 const char *cp = string; 474 size_t len2; 475 bool termcap_hack; 476 bool incremented_two; 477 478 if (cp == NULL) 479 return NULL; 480 481 TPS(out_used) = 0; 482 len2 = strlen(cp); 483 484 /* 485 * Find the highest parameter-number referred to in the format string. 486 * Use this value to limit the number of arguments copied from the 487 * variable-length argument list. 488 */ 489 number = _nc_tparm_analyze(cp, p_is_s, &popcount); 490 if (TPS(fmt_buff) == 0) 491 return NULL; 492 493 incremented_two = FALSE; 494 495 if (number > NUM_PARM) 496 number = NUM_PARM; 497 if (popcount > NUM_PARM) 498 popcount = NUM_PARM; 499 num_args = max(popcount, number); 500 501 for (i = 0; i < num_args; i++) { 502 /* 503 * A few caps (such as plab_norm) have string-valued parms. 504 * We'll have to assume that the caller knows the difference, since 505 * a char* and an int may not be the same size on the stack. The 506 * normal prototype for this uses 9 long's, which is consistent with 507 * our va_arg() usage. 508 */ 509 if (p_is_s[i] != 0) { 510 p_is_s[i] = va_arg(ap, char *); 511 param[i] = 0; 512 } else if (use_TPARM_ARG) { 513 param[i] = va_arg(ap, TPARM_ARG); 514 } else { 515 param[i] = (TPARM_ARG) va_arg(ap, int); 516 } 517 } 518 519 /* 520 * This is a termcap compatibility hack. If there are no explicit pop 521 * operations in the string, load the stack in such a way that 522 * successive pops will grab successive parameters. That will make 523 * the expansion of (for example) \E[%d;%dH work correctly in termcap 524 * style, which means tparam() will expand termcap strings OK. 525 */ 526 TPS(stack_ptr) = 0; 527 termcap_hack = FALSE; 528 if (popcount == 0) { 529 termcap_hack = TRUE; 530 popcount = number; 531 for (i = number - 1; i >= 0; i--) { 532 if (p_is_s[i]) 533 spush(p_is_s[i]); 534 else 535 npush((int) param[i]); 536 } 537 } 538 #ifdef TRACE 539 if (USE_TRACEF(TRACE_CALLS)) { 540 for (i = 0; i < num_args; i++) { 541 if (p_is_s[i] != 0) 542 save_text(", %s", _nc_visbuf(p_is_s[i]), 0); 543 else 544 save_number(", %d", (int) param[i], 0); 545 } 546 _tracef(T_CALLED("%s(%s%s)"), TPS(tname), _nc_visbuf(cp), TPS(out_buff)); 547 TPS(out_used) = 0; 548 _nc_unlock_global(tracef); 549 } 550 #endif /* TRACE */ 551 552 while ((cp - string) < (int) len2) { 553 if (*cp != '%') { 554 save_char(UChar(*cp)); 555 } else { 556 TPS(tparam_base) = cp++; 557 cp = parse_format(cp, TPS(fmt_buff), &len); 558 switch (*cp) { 559 default: 560 break; 561 case '%': 562 save_char('%'); 563 break; 564 565 case 'd': /* FALLTHRU */ 566 case 'o': /* FALLTHRU */ 567 case 'x': /* FALLTHRU */ 568 case 'X': /* FALLTHRU */ 569 save_number(TPS(fmt_buff), npop(), len); 570 break; 571 572 case 'c': /* FALLTHRU */ 573 save_char(npop()); 574 break; 575 576 #ifdef EXP_XTERM_1005 577 case 'u': 578 { 579 unsigned char target[10]; 580 unsigned source = (unsigned) npop(); 581 int rc = _nc_conv_to_utf8(target, source, (unsigned) 582 sizeof(target)); 583 int n; 584 for (n = 0; n < rc; ++n) { 585 save_char(target[n]); 586 } 587 } 588 break; 589 #endif 590 case 'l': 591 npush((int) strlen(spop())); 592 break; 593 594 case 's': 595 save_text(TPS(fmt_buff), spop(), len); 596 break; 597 598 case 'p': 599 cp++; 600 i = (UChar(*cp) - '1'); 601 if (i >= 0 && i < NUM_PARM) { 602 if (p_is_s[i]) { 603 spush(p_is_s[i]); 604 } else { 605 npush((int) param[i]); 606 } 607 } 608 break; 609 610 case 'P': 611 cp++; 612 if (isUPPER(*cp)) { 613 i = (UChar(*cp) - 'A'); 614 TPS(static_vars)[i] = npop(); 615 } else if (isLOWER(*cp)) { 616 i = (UChar(*cp) - 'a'); 617 TPS(dynamic_var)[i] = npop(); 618 } 619 break; 620 621 case 'g': 622 cp++; 623 if (isUPPER(*cp)) { 624 i = (UChar(*cp) - 'A'); 625 npush(TPS(static_vars)[i]); 626 } else if (isLOWER(*cp)) { 627 i = (UChar(*cp) - 'a'); 628 npush(TPS(dynamic_var)[i]); 629 } 630 break; 631 632 case S_QUOTE: 633 cp++; 634 npush(UChar(*cp)); 635 cp++; 636 break; 637 638 case L_BRACE: 639 number = 0; 640 cp++; 641 while (isdigit(UChar(*cp))) { 642 number = (number * 10) + (UChar(*cp) - '0'); 643 cp++; 644 } 645 npush(number); 646 break; 647 648 case '+': 649 npush(npop() + npop()); 650 break; 651 652 case '-': 653 y = npop(); 654 x = npop(); 655 npush(x - y); 656 break; 657 658 case '*': 659 npush(npop() * npop()); 660 break; 661 662 case '/': 663 y = npop(); 664 x = npop(); 665 npush(y ? (x / y) : 0); 666 break; 667 668 case 'm': 669 y = npop(); 670 x = npop(); 671 npush(y ? (x % y) : 0); 672 break; 673 674 case 'A': 675 y = npop(); 676 x = npop(); 677 npush(y && x); 678 break; 679 680 case 'O': 681 y = npop(); 682 x = npop(); 683 npush(y || x); 684 break; 685 686 case '&': 687 npush(npop() & npop()); 688 break; 689 690 case '|': 691 npush(npop() | npop()); 692 break; 693 694 case '^': 695 npush(npop() ^ npop()); 696 break; 697 698 case '=': 699 y = npop(); 700 x = npop(); 701 npush(x == y); 702 break; 703 704 case '<': 705 y = npop(); 706 x = npop(); 707 npush(x < y); 708 break; 709 710 case '>': 711 y = npop(); 712 x = npop(); 713 npush(x > y); 714 break; 715 716 case '!': 717 npush(!npop()); 718 break; 719 720 case '~': 721 npush(~npop()); 722 break; 723 724 case 'i': 725 /* 726 * Increment the first two parameters -- if they are numbers 727 * rather than strings. As a side effect, assign into the 728 * stack; if this is termcap, then the stack was populated 729 * using the termcap hack above rather than via the terminfo 730 * 'p' case. 731 */ 732 if (!incremented_two) { 733 incremented_two = TRUE; 734 if (p_is_s[0] == 0) { 735 param[0]++; 736 if (termcap_hack) 737 TPS(stack)[0].data.num = (int) param[0]; 738 } 739 if (p_is_s[1] == 0) { 740 param[1]++; 741 if (termcap_hack) 742 TPS(stack)[1].data.num = (int) param[1]; 743 } 744 } 745 break; 746 747 case '?': 748 break; 749 750 case 't': 751 x = npop(); 752 if (!x) { 753 /* scan forward for %e or %; at level zero */ 754 cp++; 755 level = 0; 756 while (*cp) { 757 if (*cp == '%') { 758 cp++; 759 if (*cp == '?') 760 level++; 761 else if (*cp == ';') { 762 if (level > 0) 763 level--; 764 else 765 break; 766 } else if (*cp == 'e' && level == 0) 767 break; 768 } 769 770 if (*cp) 771 cp++; 772 } 773 } 774 break; 775 776 case 'e': 777 /* scan forward for a %; at level zero */ 778 cp++; 779 level = 0; 780 while (*cp) { 781 if (*cp == '%') { 782 cp++; 783 if (*cp == '?') 784 level++; 785 else if (*cp == ';') { 786 if (level > 0) 787 level--; 788 else 789 break; 790 } 791 } 792 793 if (*cp) 794 cp++; 795 } 796 break; 797 798 case ';': 799 break; 800 801 } /* endswitch (*cp) */ 802 } /* endelse (*cp == '%') */ 803 804 if (*cp == '\0') 805 break; 806 807 cp++; 808 } /* endwhile (*cp) */ 809 810 get_space((size_t) 1); 811 TPS(out_buff)[TPS(out_used)] = '\0'; 812 813 T((T_RETURN("%s"), _nc_visbuf(TPS(out_buff)))); 814 return (TPS(out_buff)); 815 } 816 817 #if NCURSES_TPARM_VARARGS 818 #define tparm_varargs tparm 819 #else 820 #define tparm_proto tparm 821 #endif 822 823 NCURSES_EXPORT(char *) 824 tparm_varargs(NCURSES_CONST char *string,...) 825 { 826 va_list ap; 827 char *result; 828 829 _nc_tparm_err = 0; 830 va_start(ap, string); 831 #ifdef TRACE 832 TPS(tname) = "tparm"; 833 #endif /* TRACE */ 834 result = tparam_internal(TRUE, string, ap); 835 va_end(ap); 836 return result; 837 } 838 839 #if !NCURSES_TPARM_VARARGS 840 NCURSES_EXPORT(char *) 841 tparm_proto(NCURSES_CONST char *string, 842 TPARM_ARG a1, 843 TPARM_ARG a2, 844 TPARM_ARG a3, 845 TPARM_ARG a4, 846 TPARM_ARG a5, 847 TPARM_ARG a6, 848 TPARM_ARG a7, 849 TPARM_ARG a8, 850 TPARM_ARG a9) 851 { 852 return tparm_varargs(string, a1, a2, a3, a4, a5, a6, a7, a8, a9); 853 } 854 #endif /* NCURSES_TPARM_VARARGS */ 855 856 NCURSES_EXPORT(char *) 857 tiparm(const char *string,...) 858 { 859 va_list ap; 860 char *result; 861 862 _nc_tparm_err = 0; 863 va_start(ap, string); 864 #ifdef TRACE 865 TPS(tname) = "tiparm"; 866 #endif /* TRACE */ 867 result = tparam_internal(FALSE, string, ap); 868 va_end(ap); 869 return result; 870 } 871