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