1 /**************************************************************************** 2 * Copyright 2018-2020,2021 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 #define entry _ncu_entry 42 #define ENTRY _ncu_ENTRY 43 44 #include <curses.priv.h> 45 46 #undef entry 47 #undef ENTRY 48 49 #if HAVE_TSEARCH 50 #include <search.h> 51 #endif 52 53 #include <ctype.h> 54 #include <tic.h> 55 56 MODULE_ID("$Id: lib_tparm.c,v 1.129 2021/02/14 00:09:49 tom Exp $") 57 58 /* 59 * char * 60 * tparm(string, ...) 61 * 62 * Substitute the given parameters into the given string by the following 63 * rules (taken from terminfo(5)): 64 * 65 * Cursor addressing and other strings requiring parame- 66 * ters in the terminal are described by a parameterized string 67 * capability, with escapes like %x in it. For example, to 68 * address the cursor, the cup capability is given, using two 69 * parameters: the row and column to address to. (Rows and 70 * columns are numbered from zero and refer to the physical 71 * screen visible to the user, not to any unseen memory.) If 72 * the terminal has memory relative cursor addressing, that can 73 * be indicated by 74 * 75 * The parameter mechanism uses a stack and special % 76 * codes to manipulate it. Typically a sequence will push one 77 * of the parameters onto the stack and then print it in some 78 * format. Often more complex operations are necessary. 79 * 80 * The % encodings have the following meanings: 81 * 82 * %% outputs `%' 83 * %c print pop() like %c in printf() 84 * %s print pop() like %s in printf() 85 * %[[:]flags][width[.precision]][doxXs] 86 * as in printf, flags are [-+#] and space 87 * The ':' is used to avoid making %+ or %- 88 * patterns (see below). 89 * 90 * %p[1-9] push ith parm 91 * %P[a-z] set dynamic variable [a-z] to pop() 92 * %g[a-z] get dynamic variable [a-z] and push it 93 * %P[A-Z] set static variable [A-Z] to pop() 94 * %g[A-Z] get static variable [A-Z] and push it 95 * %l push strlen(pop) 96 * %'c' push char constant c 97 * %{nn} push integer constant nn 98 * 99 * %+ %- %* %/ %m 100 * arithmetic (%m is mod): push(pop() op pop()) 101 * %& %| %^ bit operations: push(pop() op pop()) 102 * %= %> %< logical operations: push(pop() op pop()) 103 * %A %O logical and & or operations for conditionals 104 * %! %~ unary operations push(op pop()) 105 * %i add 1 to first two parms (for ANSI terminals) 106 * 107 * %? expr %t thenpart %e elsepart %; 108 * if-then-else, %e elsepart is optional. 109 * else-if's are possible ala Algol 68: 110 * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %; 111 * 112 * For those of the above operators which are binary and not commutative, 113 * the stack works in the usual way, with 114 * %gx %gy %m 115 * resulting in x mod y, not the reverse. 116 */ 117 118 NCURSES_EXPORT_VAR(int) _nc_tparm_err = 0; 119 120 #define TPS(var) _nc_prescreen.tparm_state.var 121 #define popcount _nc_popcount /* workaround for NetBSD 6.0 defect */ 122 123 #define isUPPER(c) ((c) >= 'A' && (c) <= 'Z') 124 #define isLOWER(c) ((c) >= 'a' && (c) <= 'z') 125 #define tc_BUMP() if (level < 0 && number < 2) number++ 126 127 typedef struct { 128 const char *format; /* format-string can be used as cache-key */ 129 int tparm_type; /* bit-set for each string-parameter */ 130 int num_actual; 131 int num_parsed; 132 int num_popped; 133 TPARM_ARG param[NUM_PARM]; 134 char *p_is_s[NUM_PARM]; 135 } TPARM_DATA; 136 137 #if HAVE_TSEARCH 138 #define MyCache _nc_globals.cached_tparm 139 #define MyCount _nc_globals.count_tparm 140 #if NO_LEAKS 141 static int which_tparm; 142 static TPARM_DATA **delete_tparm; 143 #endif 144 #endif /* HAVE_TSEARCH */ 145 146 static char dummy[] = ""; /* avoid const-cast */ 147 148 #if HAVE_TSEARCH 149 static int 150 cmp_format(const void *p, const void *q) 151 { 152 const char *a = *(char *const *) p; 153 const char *b = *(char *const *) q; 154 return strcmp(a, b); 155 } 156 #endif 157 158 #if NO_LEAKS 159 #if HAVE_TSEARCH 160 static void 161 visit_nodes(const void *nodep, const VISIT which, const int depth) 162 { 163 (void) depth; 164 if (which == preorder || which == leaf) { 165 delete_tparm[which_tparm] = *(TPARM_DATA **) nodep; 166 which_tparm++; 167 } 168 } 169 #endif 170 171 NCURSES_EXPORT(void) 172 _nc_free_tparm(void) 173 { 174 #if HAVE_TSEARCH 175 if (MyCount != 0) { 176 delete_tparm = typeMalloc(TPARM_DATA *, MyCount); 177 which_tparm = 0; 178 twalk(MyCache, visit_nodes); 179 for (which_tparm = 0; which_tparm < MyCount; ++which_tparm) { 180 TPARM_DATA *ptr = delete_tparm[which_tparm]; 181 tdelete(ptr, &MyCache, cmp_format); 182 free((char *) ptr->format); 183 free(ptr); 184 } 185 which_tparm = 0; 186 twalk(MyCache, visit_nodes); 187 FreeAndNull(delete_tparm); 188 MyCount = 0; 189 which_tparm = 0; 190 } 191 #endif 192 FreeAndNull(TPS(out_buff)); 193 TPS(out_size) = 0; 194 TPS(out_used) = 0; 195 196 FreeAndNull(TPS(fmt_buff)); 197 TPS(fmt_size) = 0; 198 } 199 #endif 200 201 static NCURSES_INLINE void 202 get_space(size_t need) 203 { 204 need += TPS(out_used); 205 if (need > TPS(out_size)) { 206 TPS(out_size) = need * 2; 207 TYPE_REALLOC(char, TPS(out_size), TPS(out_buff)); 208 } 209 } 210 211 static NCURSES_INLINE void 212 save_text(const char *fmt, const char *s, int len) 213 { 214 size_t s_len = (size_t) len + strlen(s) + strlen(fmt); 215 get_space(s_len + 1); 216 217 _nc_SPRINTF(TPS(out_buff) + TPS(out_used), 218 _nc_SLIMIT(TPS(out_size) - TPS(out_used)) 219 fmt, s); 220 TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used)); 221 } 222 223 static NCURSES_INLINE void 224 save_number(const char *fmt, int number, int len) 225 { 226 size_t s_len = (size_t) len + 30 + strlen(fmt); 227 get_space(s_len + 1); 228 229 _nc_SPRINTF(TPS(out_buff) + TPS(out_used), 230 _nc_SLIMIT(TPS(out_size) - TPS(out_used)) 231 fmt, number); 232 TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used)); 233 } 234 235 static NCURSES_INLINE void 236 save_char(int c) 237 { 238 if (c == 0) 239 c = 0200; 240 get_space((size_t) 1); 241 TPS(out_buff)[TPS(out_used)++] = (char) c; 242 } 243 244 static NCURSES_INLINE void 245 npush(int x) 246 { 247 if (TPS(stack_ptr) < STACKSIZE) { 248 TPS(stack)[TPS(stack_ptr)].num_type = TRUE; 249 TPS(stack)[TPS(stack_ptr)].data.num = x; 250 TPS(stack_ptr)++; 251 } else { 252 DEBUG(2, ("npush: stack overflow: %s", _nc_visbuf(TPS(tparam_base)))); 253 _nc_tparm_err++; 254 } 255 } 256 257 static NCURSES_INLINE int 258 npop(void) 259 { 260 int result = 0; 261 if (TPS(stack_ptr) > 0) { 262 TPS(stack_ptr)--; 263 if (TPS(stack)[TPS(stack_ptr)].num_type) 264 result = TPS(stack)[TPS(stack_ptr)].data.num; 265 } else { 266 DEBUG(2, ("npop: stack underflow: %s", _nc_visbuf(TPS(tparam_base)))); 267 _nc_tparm_err++; 268 } 269 return result; 270 } 271 272 static NCURSES_INLINE void 273 spush(char *x) 274 { 275 if (TPS(stack_ptr) < STACKSIZE) { 276 TPS(stack)[TPS(stack_ptr)].num_type = FALSE; 277 TPS(stack)[TPS(stack_ptr)].data.str = x; 278 TPS(stack_ptr)++; 279 } else { 280 DEBUG(2, ("spush: stack overflow: %s", _nc_visbuf(TPS(tparam_base)))); 281 _nc_tparm_err++; 282 } 283 } 284 285 static NCURSES_INLINE char * 286 spop(void) 287 { 288 char *result = dummy; 289 if (TPS(stack_ptr) > 0) { 290 TPS(stack_ptr)--; 291 if (!TPS(stack)[TPS(stack_ptr)].num_type 292 && TPS(stack)[TPS(stack_ptr)].data.str != 0) 293 result = TPS(stack)[TPS(stack_ptr)].data.str; 294 } else { 295 DEBUG(2, ("spop: stack underflow: %s", _nc_visbuf(TPS(tparam_base)))); 296 _nc_tparm_err++; 297 } 298 return result; 299 } 300 301 static NCURSES_INLINE const char * 302 parse_format(const char *s, char *format, int *len) 303 { 304 *len = 0; 305 if (format != 0) { 306 bool done = FALSE; 307 bool allowminus = FALSE; 308 bool dot = FALSE; 309 bool err = FALSE; 310 char *fmt = format; 311 int my_width = 0; 312 int my_prec = 0; 313 int value = 0; 314 315 *len = 0; 316 *format++ = '%'; 317 while (*s != '\0' && !done) { 318 switch (*s) { 319 case 'c': /* FALLTHRU */ 320 case 'd': /* FALLTHRU */ 321 case 'o': /* FALLTHRU */ 322 case 'x': /* FALLTHRU */ 323 case 'X': /* FALLTHRU */ 324 case 's': 325 #ifdef EXP_XTERM_1005 326 case 'u': 327 #endif 328 *format++ = *s; 329 done = TRUE; 330 break; 331 case '.': 332 *format++ = *s++; 333 if (dot) { 334 err = TRUE; 335 } else { /* value before '.' is the width */ 336 dot = TRUE; 337 my_width = value; 338 } 339 value = 0; 340 break; 341 case '#': 342 *format++ = *s++; 343 break; 344 case ' ': 345 *format++ = *s++; 346 break; 347 case ':': 348 s++; 349 allowminus = TRUE; 350 break; 351 case '-': 352 if (allowminus) { 353 *format++ = *s++; 354 } else { 355 done = TRUE; 356 } 357 break; 358 default: 359 if (isdigit(UChar(*s))) { 360 value = (value * 10) + (*s - '0'); 361 if (value > 10000) 362 err = TRUE; 363 *format++ = *s++; 364 } else { 365 done = TRUE; 366 } 367 } 368 } 369 370 /* 371 * If we found an error, ignore (and remove) the flags. 372 */ 373 if (err) { 374 my_width = my_prec = value = 0; 375 format = fmt; 376 *format++ = '%'; 377 *format++ = *s; 378 } 379 380 /* 381 * Any value after '.' is the precision. If we did not see '.', then 382 * the value is the width. 383 */ 384 if (dot) 385 my_prec = value; 386 else 387 my_width = value; 388 389 *format = '\0'; 390 /* return maximum string length in print */ 391 *len = (my_width > my_prec) ? my_width : my_prec; 392 } 393 return s; 394 } 395 396 /* 397 * Analyze the string to see how many parameters we need from the varargs list, 398 * and what their types are. We will only accept string parameters if they 399 * appear as a %l or %s format following an explicit parameter reference (e.g., 400 * %p2%s). All other parameters are numbers. 401 * 402 * 'number' counts coarsely the number of pop's we see in the string, and 403 * 'popcount' shows the highest parameter number in the string. We would like 404 * to simply use the latter count, but if we are reading termcap strings, there 405 * may be cases that we cannot see the explicit parameter numbers. 406 */ 407 NCURSES_EXPORT(int) 408 _nc_tparm_analyze(const char *string, char **p_is_s, int *popcount) 409 { 410 size_t len2; 411 int i; 412 int lastpop = -1; 413 int len; 414 int number = 0; 415 int level = -1; 416 const char *cp = string; 417 418 if (cp == 0) 419 return 0; 420 421 if ((len2 = strlen(cp)) + 2 > TPS(fmt_size)) { 422 TPS(fmt_size) += len2 + 2; 423 TPS(fmt_buff) = typeRealloc(char, TPS(fmt_size), TPS(fmt_buff)); 424 if (TPS(fmt_buff) == 0) 425 return 0; 426 } 427 428 memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM); 429 *popcount = 0; 430 431 while ((cp - string) < (int) len2) { 432 if (*cp == '%') { 433 cp++; 434 cp = parse_format(cp, TPS(fmt_buff), &len); 435 switch (*cp) { 436 default: 437 break; 438 439 case 'd': /* FALLTHRU */ 440 case 'o': /* FALLTHRU */ 441 case 'x': /* FALLTHRU */ 442 case 'X': /* FALLTHRU */ 443 case 'c': /* FALLTHRU */ 444 #ifdef EXP_XTERM_1005 445 case 'u': 446 #endif 447 if (lastpop <= 0) { 448 tc_BUMP(); 449 } 450 level -= 1; 451 lastpop = -1; 452 break; 453 454 case 'l': 455 case 's': 456 if (lastpop > 0) { 457 level -= 1; 458 p_is_s[lastpop - 1] = dummy; 459 } 460 tc_BUMP(); 461 break; 462 463 case 'p': 464 cp++; 465 i = (UChar(*cp) - '0'); 466 if (i >= 0 && i <= NUM_PARM) { 467 ++level; 468 lastpop = i; 469 if (lastpop > *popcount) 470 *popcount = lastpop; 471 } 472 break; 473 474 case 'P': 475 ++cp; 476 break; 477 478 case 'g': 479 ++level; 480 cp++; 481 break; 482 483 case S_QUOTE: 484 ++level; 485 cp += 2; 486 lastpop = -1; 487 break; 488 489 case L_BRACE: 490 ++level; 491 cp++; 492 while (isdigit(UChar(*cp))) { 493 cp++; 494 } 495 break; 496 497 case '+': 498 case '-': 499 case '*': 500 case '/': 501 case 'm': 502 case 'A': 503 case 'O': 504 case '&': 505 case '|': 506 case '^': 507 case '=': 508 case '<': 509 case '>': 510 tc_BUMP(); 511 level -= 1; /* pop 2, operate, push 1 */ 512 lastpop = -1; 513 break; 514 515 case '!': 516 case '~': 517 tc_BUMP(); 518 lastpop = -1; 519 break; 520 521 case 'i': 522 /* will add 1 to first (usually two) parameters */ 523 break; 524 } 525 } 526 if (*cp != '\0') 527 cp++; 528 } 529 530 if (number > NUM_PARM) 531 number = NUM_PARM; 532 return number; 533 } 534 535 /* 536 * Analyze the capability string, finding the number of parameters and their 537 * types. 538 * 539 * TODO: cache the result so that this is done once per capability per term. 540 */ 541 static int 542 tparm_setup(const char *string, TPARM_DATA * result) 543 { 544 int rc = OK; 545 546 TPS(out_used) = 0; 547 memset(result, 0, sizeof(*result)); 548 549 if (string == NULL) { 550 TR(TRACE_CALLS, ("%s: format is null", TPS(tname))); 551 rc = ERR; 552 } else { 553 #if HAVE_TSEARCH 554 TPARM_DATA *fs; 555 void *ft; 556 557 result->format = string; 558 if ((ft = tfind(result, &MyCache, cmp_format)) != 0) { 559 fs = *(TPARM_DATA **) ft; 560 *result = *fs; 561 } else 562 #endif 563 { 564 /* 565 * Find the highest parameter-number referred to in the format 566 * string. Use this value to limit the number of arguments copied 567 * from the variable-length argument list. 568 */ 569 result->num_parsed = _nc_tparm_analyze(string, 570 result->p_is_s, 571 &(result->num_popped)); 572 if (TPS(fmt_buff) == 0) { 573 TR(TRACE_CALLS, ("%s: error in analysis", TPS(tname))); 574 rc = ERR; 575 } else { 576 int n; 577 578 if (result->num_parsed > NUM_PARM) 579 result->num_parsed = NUM_PARM; 580 if (result->num_popped > NUM_PARM) 581 result->num_popped = NUM_PARM; 582 result->num_actual = max(result->num_popped, result->num_parsed); 583 584 for (n = 0; n < result->num_actual; ++n) { 585 if (result->p_is_s[n]) 586 result->tparm_type |= (1 << n); 587 } 588 #if HAVE_TSEARCH 589 if ((fs = typeCalloc(TPARM_DATA, 1)) != 0) { 590 *fs = *result; 591 if ((fs->format = strdup(string)) != 0) { 592 if (tsearch(fs, &MyCache, cmp_format) != 0) { 593 ++MyCount; 594 } else { 595 rc = ERR; 596 } 597 } else { 598 rc = ERR; 599 } 600 } else { 601 rc = ERR; 602 } 603 #endif 604 } 605 } 606 } 607 608 return rc; 609 } 610 611 /* 612 * A few caps (such as plab_norm) have string-valued parms. We'll have to 613 * assume that the caller knows the difference, since a char* and an int may 614 * not be the same size on the stack. The normal prototype for tparm uses 9 615 * long's, which is consistent with our va_arg() usage. 616 */ 617 static void 618 tparm_copy_valist(TPARM_DATA * data, int use_TPARM_ARG, va_list ap) 619 { 620 int i; 621 622 for (i = 0; i < data->num_actual; i++) { 623 if (data->p_is_s[i] != 0) { 624 char *value = va_arg(ap, char *); 625 if (value == 0) 626 value = dummy; 627 data->p_is_s[i] = value; 628 data->param[i] = 0; 629 } else if (use_TPARM_ARG) { 630 data->param[i] = va_arg(ap, TPARM_ARG); 631 } else { 632 data->param[i] = (TPARM_ARG) va_arg(ap, int); 633 } 634 } 635 } 636 637 /* 638 * This is a termcap compatibility hack. If there are no explicit pop 639 * operations in the string, load the stack in such a way that successive pops 640 * will grab successive parameters. That will make the expansion of (for 641 * example) \E[%d;%dH work correctly in termcap style, which means tparam() 642 * will expand termcap strings OK. 643 */ 644 static bool 645 tparm_tc_compat(TPARM_DATA * data) 646 { 647 bool termcap_hack = FALSE; 648 649 TPS(stack_ptr) = 0; 650 651 if (data->num_popped == 0) { 652 int i; 653 654 termcap_hack = TRUE; 655 for (i = data->num_parsed - 1; i >= 0; i--) { 656 if (data->p_is_s[i]) 657 spush(data->p_is_s[i]); 658 else 659 npush((int) data->param[i]); 660 } 661 } 662 return termcap_hack; 663 } 664 665 #ifdef TRACE 666 static void 667 tparm_trace_call(const char *string, TPARM_DATA * data) 668 { 669 if (USE_TRACEF(TRACE_CALLS)) { 670 int i; 671 for (i = 0; i < data->num_actual; i++) { 672 if (data->p_is_s[i] != 0) { 673 save_text(", %s", _nc_visbuf(data->p_is_s[i]), 0); 674 } else if ((long) data->param[i] > MAX_OF_TYPE(NCURSES_INT2) || 675 (long) data->param[i] < 0) { 676 _tracef("BUG: problem with tparm parameter #%d of %d", 677 i + 1, data->num_actual); 678 break; 679 } else { 680 save_number(", %d", (int) data->param[i], 0); 681 } 682 } 683 _tracef(T_CALLED("%s(%s%s)"), TPS(tname), _nc_visbuf(string), TPS(out_buff)); 684 TPS(out_used) = 0; 685 _nc_unlock_global(tracef); 686 } 687 } 688 689 #else 690 #define tparm_trace_call(string, data) /* nothing */ 691 #endif /* TRACE */ 692 693 static NCURSES_INLINE char * 694 tparam_internal(const char *string, TPARM_DATA * data) 695 { 696 int number; 697 int len; 698 int level; 699 int x, y; 700 int i; 701 const char *cp = string; 702 size_t len2 = strlen(cp); 703 bool incremented_two = FALSE; 704 bool termcap_hack = tparm_tc_compat(data); 705 706 tparm_trace_call(string, data); 707 708 while ((cp - string) < (int) len2) { 709 if (*cp != '%') { 710 save_char(UChar(*cp)); 711 } else { 712 TPS(tparam_base) = cp++; 713 cp = parse_format(cp, TPS(fmt_buff), &len); 714 switch (*cp) { 715 default: 716 break; 717 case '%': 718 save_char('%'); 719 break; 720 721 case 'd': /* FALLTHRU */ 722 case 'o': /* FALLTHRU */ 723 case 'x': /* FALLTHRU */ 724 case 'X': /* FALLTHRU */ 725 save_number(TPS(fmt_buff), npop(), len); 726 break; 727 728 case 'c': /* FALLTHRU */ 729 save_char(npop()); 730 break; 731 732 #ifdef EXP_XTERM_1005 733 case 'u': 734 { 735 unsigned char target[10]; 736 unsigned source = (unsigned) npop(); 737 int rc = _nc_conv_to_utf8(target, source, (unsigned) 738 sizeof(target)); 739 int n; 740 for (n = 0; n < rc; ++n) { 741 save_char(target[n]); 742 } 743 } 744 break; 745 #endif 746 case 'l': 747 npush((int) strlen(spop())); 748 break; 749 750 case 's': 751 save_text(TPS(fmt_buff), spop(), len); 752 break; 753 754 case 'p': 755 cp++; 756 i = (UChar(*cp) - '1'); 757 if (i >= 0 && i < NUM_PARM) { 758 if (data->p_is_s[i]) { 759 spush(data->p_is_s[i]); 760 } else { 761 npush((int) data->param[i]); 762 } 763 } 764 break; 765 766 case 'P': 767 cp++; 768 if (isUPPER(*cp)) { 769 i = (UChar(*cp) - 'A'); 770 TPS(static_vars)[i] = npop(); 771 } else if (isLOWER(*cp)) { 772 i = (UChar(*cp) - 'a'); 773 TPS(dynamic_var)[i] = npop(); 774 } 775 break; 776 777 case 'g': 778 cp++; 779 if (isUPPER(*cp)) { 780 i = (UChar(*cp) - 'A'); 781 npush(TPS(static_vars)[i]); 782 } else if (isLOWER(*cp)) { 783 i = (UChar(*cp) - 'a'); 784 npush(TPS(dynamic_var)[i]); 785 } 786 break; 787 788 case S_QUOTE: 789 cp++; 790 npush(UChar(*cp)); 791 cp++; 792 break; 793 794 case L_BRACE: 795 number = 0; 796 cp++; 797 while (isdigit(UChar(*cp))) { 798 number = (number * 10) + (UChar(*cp) - '0'); 799 cp++; 800 } 801 npush(number); 802 break; 803 804 case '+': 805 npush(npop() + npop()); 806 break; 807 808 case '-': 809 y = npop(); 810 x = npop(); 811 npush(x - y); 812 break; 813 814 case '*': 815 npush(npop() * npop()); 816 break; 817 818 case '/': 819 y = npop(); 820 x = npop(); 821 npush(y ? (x / y) : 0); 822 break; 823 824 case 'm': 825 y = npop(); 826 x = npop(); 827 npush(y ? (x % y) : 0); 828 break; 829 830 case 'A': 831 y = npop(); 832 x = npop(); 833 npush(y && x); 834 break; 835 836 case 'O': 837 y = npop(); 838 x = npop(); 839 npush(y || x); 840 break; 841 842 case '&': 843 npush(npop() & npop()); 844 break; 845 846 case '|': 847 npush(npop() | npop()); 848 break; 849 850 case '^': 851 npush(npop() ^ npop()); 852 break; 853 854 case '=': 855 y = npop(); 856 x = npop(); 857 npush(x == y); 858 break; 859 860 case '<': 861 y = npop(); 862 x = npop(); 863 npush(x < y); 864 break; 865 866 case '>': 867 y = npop(); 868 x = npop(); 869 npush(x > y); 870 break; 871 872 case '!': 873 npush(!npop()); 874 break; 875 876 case '~': 877 npush(~npop()); 878 break; 879 880 case 'i': 881 /* 882 * Increment the first two parameters -- if they are numbers 883 * rather than strings. As a side effect, assign into the 884 * stack; if this is termcap, then the stack was populated 885 * using the termcap hack above rather than via the terminfo 886 * 'p' case. 887 */ 888 if (!incremented_two) { 889 incremented_two = TRUE; 890 if (data->p_is_s[0] == 0) { 891 data->param[0]++; 892 if (termcap_hack) 893 TPS(stack)[0].data.num = (int) data->param[0]; 894 } 895 if (data->p_is_s[1] == 0) { 896 data->param[1]++; 897 if (termcap_hack) 898 TPS(stack)[1].data.num = (int) data->param[1]; 899 } 900 } 901 break; 902 903 case '?': 904 break; 905 906 case 't': 907 x = npop(); 908 if (!x) { 909 /* scan forward for %e or %; at level zero */ 910 cp++; 911 level = 0; 912 while (*cp) { 913 if (*cp == '%') { 914 cp++; 915 if (*cp == '?') 916 level++; 917 else if (*cp == ';') { 918 if (level > 0) 919 level--; 920 else 921 break; 922 } else if (*cp == 'e' && level == 0) 923 break; 924 } 925 926 if (*cp) 927 cp++; 928 } 929 } 930 break; 931 932 case 'e': 933 /* scan forward for a %; at level zero */ 934 cp++; 935 level = 0; 936 while (*cp) { 937 if (*cp == '%') { 938 cp++; 939 if (*cp == '?') 940 level++; 941 else if (*cp == ';') { 942 if (level > 0) 943 level--; 944 else 945 break; 946 } 947 } 948 949 if (*cp) 950 cp++; 951 } 952 break; 953 954 case ';': 955 break; 956 957 } /* endswitch (*cp) */ 958 } /* endelse (*cp == '%') */ 959 960 if (*cp == '\0') 961 break; 962 963 cp++; 964 } /* endwhile (*cp) */ 965 966 get_space((size_t) 1); 967 TPS(out_buff)[TPS(out_used)] = '\0'; 968 969 if (TPS(stack_ptr) && !_nc_tparm_err) { 970 DEBUG(2, ("tparm: stack has %d item%s on return", 971 TPS(stack_ptr), 972 TPS(stack_ptr) == 1 ? "" : "s")); 973 _nc_tparm_err++; 974 } 975 976 T((T_RETURN("%s"), _nc_visbuf(TPS(out_buff)))); 977 return (TPS(out_buff)); 978 } 979 980 #if NCURSES_TPARM_VARARGS 981 982 NCURSES_EXPORT(char *) 983 tparm(const char *string, ...) 984 { 985 TPARM_DATA myData; 986 va_list ap; 987 char *result = NULL; 988 989 _nc_tparm_err = 0; 990 #ifdef TRACE 991 TPS(tname) = "tparm"; 992 #endif /* TRACE */ 993 994 if (tparm_setup(string, &myData) == OK) { 995 996 va_start(ap, string); 997 tparm_copy_valist(&myData, TRUE, ap); 998 va_end(ap); 999 1000 result = tparam_internal(string, &myData); 1001 } 1002 return result; 1003 } 1004 1005 #else /* !NCURSES_TPARM_VARARGS */ 1006 1007 NCURSES_EXPORT(char *) 1008 tparm(const char *string, 1009 TPARM_ARG a1, 1010 TPARM_ARG a2, 1011 TPARM_ARG a3, 1012 TPARM_ARG a4, 1013 TPARM_ARG a5, 1014 TPARM_ARG a6, 1015 TPARM_ARG a7, 1016 TPARM_ARG a8, 1017 TPARM_ARG a9) 1018 { 1019 TPARM_DATA myData; 1020 char *result = NULL; 1021 1022 _nc_tparm_err = 0; 1023 #ifdef TRACE 1024 TPS(tname) = "tparm"; 1025 #endif /* TRACE */ 1026 1027 if (tparm_setup(string, &myData) == OK) { 1028 1029 myData.param[0] = a1; 1030 myData.param[1] = a2; 1031 myData.param[2] = a3; 1032 myData.param[3] = a4; 1033 myData.param[4] = a5; 1034 myData.param[5] = a6; 1035 myData.param[6] = a7; 1036 myData.param[7] = a8; 1037 myData.param[8] = a9; 1038 1039 result = tparam_internal(string, &myData); 1040 } 1041 return result; 1042 } 1043 1044 #endif /* NCURSES_TPARM_VARARGS */ 1045 1046 NCURSES_EXPORT(char *) 1047 tiparm(const char *string, ...) 1048 { 1049 TPARM_DATA myData; 1050 va_list ap; 1051 char *result = NULL; 1052 1053 _nc_tparm_err = 0; 1054 #ifdef TRACE 1055 TPS(tname) = "tiparm"; 1056 #endif /* TRACE */ 1057 1058 if (tparm_setup(string, &myData) == OK) { 1059 1060 va_start(ap, string); 1061 tparm_copy_valist(&myData, FALSE, ap); 1062 va_end(ap); 1063 1064 result = tparam_internal(string, &myData); 1065 } 1066 return result; 1067 } 1068 1069 /* 1070 * The internal-use flavor ensures that the parameters are numbers, not strings 1071 */ 1072 NCURSES_EXPORT(char *) 1073 _nc_tiparm(int expected, const char *string, ...) 1074 { 1075 TPARM_DATA myData; 1076 va_list ap; 1077 char *result = NULL; 1078 1079 _nc_tparm_err = 0; 1080 #ifdef TRACE 1081 TPS(tname) = "_nc_tiparm"; 1082 #endif /* TRACE */ 1083 1084 if (tparm_setup(string, &myData) == OK 1085 && myData.num_actual <= expected 1086 && myData.tparm_type == 0) { 1087 1088 va_start(ap, string); 1089 tparm_copy_valist(&myData, FALSE, ap); 1090 va_end(ap); 1091 1092 result = tparam_internal(string, &myData); 1093 } 1094 return result; 1095 } 1096