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