1 /**************************************************************************** 2 * Copyright (c) 1998,2000,2002 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 * This code is a modification of lib_tparm.c found in ncurses-5.2. The 31 * modification are for use in grub by replacing all libc function through 32 * special grub functions. This also meant to delete all dynamic memory 33 * allocation and replace it by a number of fixed buffers. 34 * 35 * Modifications by Tilmann Bubeck <t.bubeck@reinform.de> 2002 36 **********************************************************************/ 37 38 /**************************************************************************** 39 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 40 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 41 ****************************************************************************/ 42 43 /* 44 * tparm.c 45 * 46 */ 47 48 #include "shared.h" 49 50 #include "tparm.h" 51 52 /* 53 * Common/troublesome character definitions 54 */ 55 typedef char grub_bool; 56 #undef isdigit 57 #define isdigit(c) ((c) >= '0' && (c) <= '9') 58 #ifndef FALSE 59 # define FALSE (0) 60 #endif 61 #ifndef TRUE 62 # define TRUE (!FALSE) 63 #endif 64 #define MAX_FORMAT_LEN 256 65 #define max(a,b) ((a) > (b) ? (a) : (b)) 66 67 //MODULE_ID("$Id: tparm.c,v 1.1.1.1 2003/11/20 02:04:59 fengshuo Exp $") 68 69 /* 70 * char * 71 * tparm(string, ...) 72 * 73 * Substitute the given parameters into the given string by the following 74 * rules (taken from terminfo(5)): 75 * 76 * Cursor addressing and other strings requiring parame- 77 * ters in the terminal are described by a parameterized string 78 * capability, with like escapes %x in it. For example, to 79 * address the cursor, the cup capability is given, using two 80 * parameters: the row and column to address to. (Rows and 81 * columns are numbered from zero and refer to the physical 82 * screen visible to the user, not to any unseen memory.) If 83 * the terminal has memory relative cursor addressing, that can 84 * be indicated by 85 * 86 * The parameter mechanism uses a stack and special % 87 * codes to manipulate it. Typically a sequence will push one 88 * of the parameters onto the stack and then print it in some 89 * format. Often more complex operations are necessary. 90 * 91 * The % encodings have the following meanings: 92 * 93 * %% outputs `%' 94 * %c print pop() like %c in printf() 95 * %s print pop() like %s in printf() 96 * %[[:]flags][width[.precision]][doxXs] 97 * as in printf, flags are [-+#] and space 98 * The ':' is used to avoid making %+ or %- 99 * patterns (see below). 100 * 101 * %p[1-9] push ith parm 102 * %P[a-z] set dynamic variable [a-z] to pop() 103 * %g[a-z] get dynamic variable [a-z] and push it 104 * %P[A-Z] set static variable [A-Z] to pop() 105 * %g[A-Z] get static variable [A-Z] and push it 106 * %l push strlen(pop) 107 * %'c' push char constant c 108 * %{nn} push integer constant nn 109 * 110 * %+ %- %* %/ %m 111 * arithmetic (%m is mod): push(pop() op pop()) 112 * %& %| %^ bit operations: push(pop() op pop()) 113 * %= %> %< logical operations: push(pop() op pop()) 114 * %A %O logical and & or operations for conditionals 115 * %! %~ unary operations push(op pop()) 116 * %i add 1 to first two parms (for ANSI terminals) 117 * 118 * %? expr %t thenpart %e elsepart %; 119 * if-then-else, %e elsepart is optional. 120 * else-if's are possible ala Algol 68: 121 * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %; 122 * 123 * For those of the above operators which are binary and not commutative, 124 * the stack works in the usual way, with 125 * %gx %gy %m 126 * resulting in x mod y, not the reverse. 127 */ 128 129 #define STACKSIZE 20 130 131 typedef struct { 132 union { 133 unsigned int num; 134 char *str; 135 } data; 136 grub_bool num_type; 137 } stack_frame; 138 139 static stack_frame stack[STACKSIZE]; 140 static int stack_ptr; 141 142 static char out_buff[256]; 143 static int out_size = 256; 144 static int out_used; 145 146 static inline void 147 get_space(int need) 148 { 149 need += out_used; 150 if (need > out_size) { 151 // FIX ME! buffer full, what now? 152 ; 153 } 154 } 155 156 static inline void 157 save_text(const char *fmt, const char *s, int len) 158 { 159 int s_len = grub_strlen(s); 160 if (len > (int) s_len) 161 s_len = len; 162 163 get_space(s_len + 1); 164 165 (void) grub_sprintf(out_buff + out_used, fmt, s); 166 out_used += grub_strlen(out_buff + out_used); 167 } 168 169 static inline void 170 save_number(const char *fmt, int number, int len) 171 { 172 if (len < 30) 173 len = 30; /* actually log10(MAX_INT)+1 */ 174 175 get_space(len + 1); 176 177 (void) grub_sprintf(out_buff + out_used, fmt, number); 178 out_used += grub_strlen(out_buff + out_used); 179 } 180 181 static inline void 182 save_char(int c) 183 { 184 if (c == 0) 185 c = 0200; 186 get_space(1); 187 out_buff[out_used++] = c; 188 } 189 190 static inline void 191 npush(int x) 192 { 193 if (stack_ptr < STACKSIZE) { 194 stack[stack_ptr].num_type = TRUE; 195 stack[stack_ptr].data.num = x; 196 stack_ptr++; 197 } 198 } 199 200 static inline int 201 npop(void) 202 { 203 int result = 0; 204 if (stack_ptr > 0) { 205 stack_ptr--; 206 if (stack[stack_ptr].num_type) 207 result = stack[stack_ptr].data.num; 208 } 209 return result; 210 } 211 212 static inline void 213 spush(char *x) 214 { 215 if (stack_ptr < STACKSIZE) { 216 stack[stack_ptr].num_type = FALSE; 217 stack[stack_ptr].data.str = x; 218 stack_ptr++; 219 } 220 } 221 222 static inline char * 223 spop(void) 224 { 225 static char dummy[] = ""; /* avoid const-cast */ 226 char *result = dummy; 227 if (stack_ptr > 0) { 228 stack_ptr--; 229 if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0) 230 result = stack[stack_ptr].data.str; 231 } 232 return result; 233 } 234 235 static inline const char * 236 parse_format(const char *s, char *format, int *len) 237 { 238 grub_bool done = FALSE; 239 grub_bool allowminus = FALSE; 240 grub_bool dot = FALSE; 241 grub_bool err = FALSE; 242 char *fmt = format; 243 int prec = 0; 244 int width = 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 { 265 dot = TRUE; 266 prec = 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(*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 prec = width = value = 0; 304 format = fmt; 305 *format++ = '%'; 306 *format++ = *s; 307 } 308 309 if (dot) 310 width = value; 311 else 312 prec = value; 313 314 *format = '\0'; 315 /* return maximum string length in print */ 316 *len = (prec > width) ? prec : width; 317 return s; 318 } 319 320 #define isUPPER(c) ((c) >= 'A' && (c) <= 'Z') 321 #define isLOWER(c) ((c) >= 'a' && (c) <= 'z') 322 323 static inline char * 324 tparam_internal(const char *string, int *dataptr) 325 { 326 #define NUM_VARS 26 327 char *p_is_s[9]; 328 int param[9]; 329 int lastpop; 330 int popcount; 331 int number; 332 int len; 333 int level; 334 int x, y; 335 int i; 336 int len2; 337 register const char *cp; 338 static int len_fmt = MAX_FORMAT_LEN; 339 static char dummy[] = ""; 340 static char format[MAX_FORMAT_LEN]; 341 static int dynamic_var[NUM_VARS]; 342 static int static_vars[NUM_VARS]; 343 344 out_used = 0; 345 if (string == NULL) 346 return NULL; 347 348 if ((len2 = grub_strlen(string)) > len_fmt) { 349 return NULL; 350 } 351 352 /* 353 * Find the highest parameter-number referred to in the format string. 354 * Use this value to limit the number of arguments copied from the 355 * variable-length argument list. 356 */ 357 358 number = 0; 359 lastpop = -1; 360 popcount = 0; 361 grub_memset(p_is_s, 0, sizeof(p_is_s)); 362 363 /* 364 * Analyze the string to see how many parameters we need from the varargs 365 * list, and what their types are. We will only accept string parameters 366 * if they appear as a %l or %s format following an explicit parameter 367 * reference (e.g., %p2%s). All other parameters are numbers. 368 * 369 * 'number' counts coarsely the number of pop's we see in the string, and 370 * 'popcount' shows the highest parameter number in the string. We would 371 * like to simply use the latter count, but if we are reading termcap 372 * strings, there may be cases that we cannot see the explicit parameter 373 * numbers. 374 */ 375 for (cp = string; (cp - string) < (int) len2;) { 376 if (*cp == '%') { 377 cp++; 378 cp = parse_format(cp, format, &len); 379 switch (*cp) { 380 default: 381 break; 382 383 case 'd': /* FALLTHRU */ 384 case 'o': /* FALLTHRU */ 385 case 'x': /* FALLTHRU */ 386 case 'X': /* FALLTHRU */ 387 case 'c': /* FALLTHRU */ 388 number++; 389 lastpop = -1; 390 break; 391 392 case 'l': 393 case 's': 394 if (lastpop > 0) 395 p_is_s[lastpop - 1] = dummy; 396 ++number; 397 break; 398 399 case 'p': 400 cp++; 401 i = (*cp - '0'); 402 if (i >= 0 && i <= 9) { 403 lastpop = i; 404 if (lastpop > popcount) 405 popcount = lastpop; 406 } 407 break; 408 409 case 'P': 410 case 'g': 411 cp++; 412 break; 413 414 case '\'': 415 cp += 2; 416 lastpop = -1; 417 break; 418 419 case '{': 420 cp++; 421 while (*cp >= '0' && *cp <= '9') { 422 cp++; 423 } 424 break; 425 426 case '+': 427 case '-': 428 case '*': 429 case '/': 430 case 'm': 431 case 'A': 432 case 'O': 433 case '&': 434 case '|': 435 case '^': 436 case '=': 437 case '<': 438 case '>': 439 case '!': 440 case '~': 441 lastpop = -1; 442 number += 2; 443 break; 444 445 case 'i': 446 lastpop = -1; 447 if (popcount < 2) 448 popcount = 2; 449 break; 450 } 451 } 452 if (*cp != '\0') 453 cp++; 454 } 455 456 if (number > 9) 457 number = 9; 458 for (i = 0; i < max(popcount, number); i++) { 459 /* 460 * A few caps (such as plab_norm) have string-valued parms. 461 * We'll have to assume that the caller knows the difference, since 462 * a char* and an int may not be the same size on the stack. 463 */ 464 if (p_is_s[i] != 0) { 465 p_is_s[i] = (char *)(*(dataptr++)); 466 } else { 467 param[i] = (int)(*(dataptr++)); 468 } 469 } 470 471 /* 472 * This is a termcap compatibility hack. If there are no explicit pop 473 * operations in the string, load the stack in such a way that 474 * successive pops will grab successive parameters. That will make 475 * the expansion of (for example) \E[%d;%dH work correctly in termcap 476 * style, which means tparam() will expand termcap strings OK. 477 */ 478 stack_ptr = 0; 479 if (popcount == 0) { 480 popcount = number; 481 for (i = number - 1; i >= 0; i--) 482 npush(param[i]); 483 } 484 485 while (*string) { 486 /* skip delay timings */ 487 if (*string == '$' && *(string + 1) == '<') { 488 while( *string && *string != '>') 489 string++; 490 if ( *string == '>' ) string++; 491 } else if ( *string == '%') { 492 string++; 493 string = parse_format(string, format, &len); 494 switch (*string) { 495 default: 496 break; 497 case '%': 498 save_char('%'); 499 break; 500 501 case 'd': /* FALLTHRU */ 502 case 'o': /* FALLTHRU */ 503 case 'x': /* FALLTHRU */ 504 case 'X': /* FALLTHRU */ 505 case 'c': /* FALLTHRU */ 506 save_number(format, npop(), len); 507 break; 508 509 case 'l': 510 save_number("%d", strlen(spop()), 0); 511 break; 512 513 case 's': 514 save_text(format, spop(), len); 515 break; 516 517 case 'p': 518 string++; 519 i = (*string - '1'); 520 if (i >= 0 && i < 9) { 521 if (p_is_s[i]) 522 spush(p_is_s[i]); 523 else 524 npush(param[i]); 525 } 526 break; 527 528 case 'P': 529 string++; 530 if (isUPPER(*string)) { 531 i = (*string - 'A'); 532 static_vars[i] = npop(); 533 } else if (isLOWER(*string)) { 534 i = (*string - 'a'); 535 dynamic_var[i] = npop(); 536 } 537 break; 538 539 case 'g': 540 string++; 541 if (isUPPER(*string)) { 542 i = (*string - 'A'); 543 npush(static_vars[i]); 544 } else if (isLOWER(*string)) { 545 i = (*string - 'a'); 546 npush(dynamic_var[i]); 547 } 548 break; 549 550 case '\'': 551 string++; 552 npush(*string); 553 string++; 554 break; 555 556 case '{': 557 number = 0; 558 string++; 559 while (*string >= '0' && *string <= '9') { 560 number = number * 10 + *string - '0'; 561 string++; 562 } 563 npush(number); 564 break; 565 566 case '+': 567 npush(npop() + npop()); 568 break; 569 570 case '-': 571 y = npop(); 572 x = npop(); 573 npush(x - y); 574 break; 575 576 case '*': 577 npush(npop() * npop()); 578 break; 579 580 case '/': 581 y = npop(); 582 x = npop(); 583 npush(y ? (x / y) : 0); 584 break; 585 586 case 'm': 587 y = npop(); 588 x = npop(); 589 npush(y ? (x % y) : 0); 590 break; 591 592 case 'A': 593 npush(npop() && npop()); 594 break; 595 596 case 'O': 597 npush(npop() || npop()); 598 break; 599 600 case '&': 601 npush(npop() & npop()); 602 break; 603 604 case '|': 605 npush(npop() | npop()); 606 break; 607 608 case '^': 609 npush(npop() ^ npop()); 610 break; 611 612 case '=': 613 y = npop(); 614 x = npop(); 615 npush(x == y); 616 break; 617 618 case '<': 619 y = npop(); 620 x = npop(); 621 npush(x < y); 622 break; 623 624 case '>': 625 y = npop(); 626 x = npop(); 627 npush(x > y); 628 break; 629 630 case '!': 631 npush(!npop()); 632 break; 633 634 case '~': 635 npush(~npop()); 636 break; 637 638 case 'i': 639 if (p_is_s[0] == 0) 640 param[0]++; 641 if (p_is_s[1] == 0) 642 param[1]++; 643 break; 644 645 case '?': 646 break; 647 648 case 't': 649 x = npop(); 650 if (!x) { 651 /* scan forward for %e or %; at level zero */ 652 string++; 653 level = 0; 654 while (*string) { 655 if (*string == '%') { 656 string++; 657 if (*string == '?') 658 level++; 659 else if (*string == ';') { 660 if (level > 0) 661 level--; 662 else 663 break; 664 } else if (*string == 'e' && level == 0) 665 break; 666 } 667 668 if (*string) 669 string++; 670 } 671 } 672 break; 673 674 case 'e': 675 /* scan forward for a %; at level zero */ 676 string++; 677 level = 0; 678 while (*string) { 679 if (*string == '%') { 680 string++; 681 if (*string == '?') 682 level++; 683 else if (*string == ';') { 684 if (level > 0) 685 level--; 686 else 687 break; 688 } 689 } 690 691 if (*string) 692 string++; 693 } 694 break; 695 696 case ';': 697 break; 698 699 } /* endswitch (*string) */ 700 } else { /* endelse (*string == '%') */ 701 save_char(*string); 702 } 703 704 if (*string == '\0') 705 break; 706 707 string++; 708 } /* endwhile (*string) */ 709 710 get_space(1); 711 out_buff[out_used] = '\0'; 712 713 return (out_buff); 714 } 715 716 char * 717 grub_tparm(const char *string,...) 718 { 719 char *result; 720 int *dataptr = (int *) &string; 721 722 dataptr++; 723 724 result = tparam_internal(string, dataptr); 725 726 return result; 727 } 728