1/* Substitution of parameters in strings from terminal descriptions. 2 Copyright (C) 2006 Free Software Foundation, Inc. 3 4 This program is free software; you can redistribute it and/or modify it 5 under the terms of the GNU General Public License as published 6 by the Free Software Foundation; either version 2, or (at your option) 7 any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Library General Public License for more details. 13 14 You should have received a copy of the GNU General Public 15 License along with this program; if not, write to the Free Software 16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 17 USA. */ 18 19/* Originally by Ross Ridge, Public Domain, 92/02/01 07:30:36 */ 20 21#include <config.h> 22 23#include <stdarg.h> 24#include <stdio.h> 25#include <string.h> 26 27#include "c-ctype.h" 28 29#ifdef USE_SCCS_IDS 30static const char SCCSid[] = "@(#) mytinfo tparm.c 3.2 92/02/01 public domain, By Ross Ridge"; 31#endif 32 33#ifndef MAX_PUSHED 34#define MAX_PUSHED 32 35#endif 36 37#define ARG 1 38#define NUM 2 39 40#define INTEGER 1 41#define STRING 2 42 43#define MAX_LINE 640 44 45typedef struct stack_str 46{ 47 int type; 48 int argnum; 49 int value; 50} stack; 51 52static stack S[MAX_PUSHED]; 53static stack vars['z'-'a'+1]; 54static int pos = 0; 55 56static 57struct arg_str 58{ 59 int type; 60 int integer; 61 char *string; 62} arg_list[10]; 63 64static int argcnt; 65 66static va_list tparm_args; 67 68static int 69pusharg (int arg) 70{ 71 if (pos == MAX_PUSHED) 72 return 1; 73 S[pos].type = ARG; 74 S[pos++].argnum = arg; 75 return 0; 76} 77 78static int 79pushnum (int num) 80{ 81 if (pos == MAX_PUSHED) 82 return 1; 83 S[pos].type = NUM; 84 S[pos++].value = num; 85 return 0; 86} 87 88static int 89getarg (int argnum, int type, void *p) 90{ 91 while (argcnt < argnum) 92 { 93 arg_list[argcnt].type = INTEGER; 94 arg_list[argcnt++].integer = (int) va_arg (tparm_args, int); 95 } 96 if (argcnt > argnum) 97 { 98 if (arg_list[argnum].type != type) 99 return 1; 100 else if (type == STRING) 101 *(char **)p = arg_list[argnum].string; 102 else 103 *(int *)p = arg_list[argnum].integer; 104 } 105 else 106 { 107 arg_list[argcnt].type = type; 108 if (type == STRING) 109 *(char **)p = arg_list[argcnt++].string = (char *) va_arg (tparm_args, char *); 110 else 111 *(int *)p = arg_list[argcnt++].integer = (int) va_arg (tparm_args, int); 112 } 113 return 0; 114} 115 116static int 117popstring (char **str) 118{ 119 if (pos-- == 0) 120 return 1; 121 if (S[pos].type != ARG) 122 return 1; 123 return getarg (S[pos].argnum, STRING, str); 124} 125 126static int 127popnum (int *num) 128{ 129 if (pos-- == 0) 130 return 1; 131 switch (S[pos].type) 132 { 133 case ARG: 134 return getarg (S[pos].argnum, INTEGER, num); 135 case NUM: 136 *num = S[pos].value; 137 return 0; 138 } 139 return 1; 140} 141 142static int 143cvtchar (const char *sp, char *c) 144{ 145 switch (*sp) 146 { 147 case '\\': 148 switch (*++sp) 149 { 150 case '\'': 151 case '$': 152 case '\\': 153 case '%': 154 *c = *sp; 155 return 2; 156 case '\0': 157 *c = '\\'; 158 return 1; 159 case '0': 160 if (sp[1] == '0' && sp[2] == '0') 161 { 162 *c = '\0'; 163 return 4; 164 } 165 *c = '\200'; /* '\0' ???? */ 166 return 2; 167 default: 168 *c = *sp; 169 return 2; 170 } 171 default: 172 *c = *sp; 173 return 1; 174 } 175} 176 177/* sigh... this has got to be the ugliest code I've ever written. 178 Trying to handle everything has its cost, I guess. 179 180 It actually isn't to hard to figure out if a given % code is supposed 181 to be interpeted with its termcap or terminfo meaning since almost 182 all terminfo codes are invalid unless something has been pushed on 183 the stack and termcap strings will never push things on the stack 184 (%p isn't used by termcap). So where we have a choice we make the 185 decision by wether or not somthing has been pushed on the stack. 186 The static variable termcap keeps track of this; it starts out set 187 to 1 and is incremented as each argument processed by a termcap % code, 188 however if something is pushed on the stack it's set to 0 and the 189 rest of the % codes are interpeted as terminfo % codes. Another way 190 of putting it is that if termcap equals one we haven't decided either 191 way yet, if it equals zero we're looking for terminfo codes, and if 192 its greater than 1 we're looking for termcap codes. 193 194 Terminfo % codes: 195 196 %% output a '%' 197 %[[:][-+# ][width][.precision]][doxXs] 198 output pop according to the printf format 199 %c output pop as a char 200 %'c' push character constant c. 201 %{n} push decimal constant n. 202 %p[1-9] push paramter [1-9] 203 %g[a-z] push variable [a-z] 204 %P[a-z] put pop in variable [a-z] 205 %l push the length of pop (a string) 206 %+ add pop to pop and push the result 207 %- subtract pop from pop and push the result 208 %* multiply pop and pop and push the result 209 %& bitwise and pop and pop and push the result 210 %| bitwise or pop and pop and push the result 211 %^ bitwise xor pop and pop and push the result 212 %~ push the bitwise not of pop 213 %= compare if pop and pop are equal and push the result 214 %> compare if pop is less than pop and push the result 215 %< compare if pop is greater than pop and push the result 216 %A logical and pop and pop and push the result 217 %O logical or pop and pop and push the result 218 %! push the logical not of pop 219 %? condition %t if_true [%e if_false] %; 220 if condtion evaulates as true then evaluate if_true, 221 else evaluate if_false. elseif's can be done: 222 %? cond %t true [%e cond2 %t true2] ... [%e condN %t trueN] [%e false] %; 223 %i add one to parameters 1 and 2. (ANSI) 224 225 Termcap Codes: 226 227 %% output a % 228 %. output parameter as a character 229 %d output parameter as a decimal number 230 %2 output parameter in printf format %02d 231 %3 output parameter in printf format %03d 232 %+x add the character x to parameter and output it as a character 233(UW) %-x subtract parameter FROM the character x and output it as a char 234(UW) %ax add the character x to parameter 235(GNU) %a[+*-/=][cp]x 236 GNU arithmetic. 237(UW) %sx subtract parameter FROM the character x 238 %>xy if parameter > character x then add character y to parameter 239 %B convert to BCD (parameter = (parameter/10)*16 + parameter%16) 240 %D Delta Data encode (parameter = parameter - 2*(paramter%16)) 241 %i increment the first two parameters by one 242 %n xor the first two parameters by 0140 243(GNU) %m xor the first two parameters by 0177 244 %r swap the first two parameters 245(GNU) %b backup to previous parameter 246(GNU) %f skip this parameter 247 248 Note the two definitions of %a, the GNU defintion is used if the characters 249 after the 'a' are valid, otherwise the UW definition is used. 250 251 (GNU) used by GNU Emacs termcap libraries 252 (UW) used by the University of Waterloo (MFCF) termcap libraries 253 254*/ 255 256char * 257tparm (const char *str, ...) 258{ 259 static int termcap; 260 static char OOPS[] = "OOPS"; 261 static char buf[MAX_LINE]; 262 const char *sp; 263 char *dp; 264 char *fmt; 265 char scan_for; 266 int scan_depth; 267 int if_depth; 268 char fmt_buf[MAX_LINE]; 269 char sbuf[MAX_LINE]; 270 271 va_start (tparm_args, str); 272 273 sp = str; 274 dp = buf; 275 scan_for = 0; 276 scan_depth = 0; 277 if_depth = 0; 278 argcnt = 0; 279 pos = 0; 280 termcap = 1; 281 while (*sp != '\0') 282 { 283 switch (*sp) 284 { 285 case '\\': 286 if (scan_for) 287 { 288 if (*++sp != '\0') 289 sp++; 290 break; 291 } 292 *dp++ = *sp++; 293 if (*sp != '\0') 294 *dp++ = *sp++; 295 break; 296 case '%': 297 sp++; 298 if (scan_for) 299 { 300 if (*sp == scan_for && if_depth == scan_depth) 301 { 302 if (scan_for == ';') 303 if_depth--; 304 scan_for = 0; 305 } 306 else if (*sp == '?') 307 if_depth++; 308 else if (*sp == ';') 309 { 310 if (if_depth == 0) 311 return OOPS; 312 else 313 if_depth--; 314 } 315 sp++; 316 break; 317 } 318 fmt = NULL; 319 switch (*sp) 320 { 321 case '%': 322 *dp++ = *sp++; 323 break; 324 case '+': 325 if (!termcap) 326 { 327 int i, j; 328 if (popnum (&j) || popnum (&i)) 329 return OOPS; 330 i += j; 331 if (pushnum (i)) 332 return OOPS; 333 sp++; 334 break; 335 } 336 /* FALLTHROUGH */ 337 case 'C': 338 if (*sp == 'C') 339 { 340 int i; 341 if (getarg (termcap - 1, INTEGER, &i)) 342 return OOPS; 343 if (i >= 96) 344 { 345 i /= 96; 346 if (i == '$') 347 *dp++ = '\\'; 348 *dp++ = i; 349 } 350 } 351 fmt = "%c"; 352 /* FALLTHROUGH */ 353 case 'a': 354 if (!termcap) 355 return OOPS; 356 { 357 int i; 358 if (getarg (termcap - 1, INTEGER, &i)) 359 return OOPS; 360 if (*++sp == '\0') 361 return OOPS; 362 if ((sp[1] == 'p' || sp[1] == 'c') 363 && sp[2] != '\0' && fmt == NULL) 364 { 365 /* GNU arithmetic parameter, what they really need is 366 terminfo. */ 367 int val; 368 int lc; 369 if (sp[1] == 'p' 370 && getarg (termcap - 1 + sp[2] - '@', INTEGER, &val)) 371 return OOPS; 372 if (sp[1] == 'c') 373 { 374 char c; 375 lc = cvtchar (sp + 2, &c) + 2; 376 /* Mask out 8th bit so \200 can be used for \0 as per 377 GNU docs. */ 378 val = c & 0177; 379 } 380 else 381 lc = 2; 382 switch (sp[0]) 383 { 384 case '=': 385 break; 386 case '+': 387 val = i + val; 388 break; 389 case '-': 390 val = i - val; 391 break; 392 case '*': 393 val = i * val; 394 break; 395 case '/': 396 val = i / val; 397 break; 398 default: 399 /* Not really GNU's %a after all... */ 400 { 401 char c; 402 lc = cvtchar (sp, &c); 403 val = c + i; 404 } 405 break; 406 } 407 arg_list[termcap - 1].integer = val; 408 sp += lc; 409 break; 410 } 411 { 412 char c; 413 sp += cvtchar (sp, &c); 414 arg_list[termcap - 1].integer = c + i; 415 } 416 } 417 if (fmt == NULL) 418 break; 419 sp--; 420 /* FALLTHROUGH */ 421 case '-': 422 if (!termcap) 423 { 424 int i, j; 425 if (popnum (&j) || popnum (&i)) 426 return OOPS; 427 i -= j; 428 if (pushnum (i)) 429 return OOPS; 430 sp++; 431 break; 432 } 433 fmt = "%c"; 434 /* FALLTHROUGH */ 435 case 's': 436 if (termcap && (fmt == NULL || *sp == '-')) 437 { 438 int i; 439 if (getarg (termcap - 1, INTEGER, &i)) 440 return OOPS; 441 if (*++sp == '\0') 442 return OOPS; 443 { 444 char c; 445 sp += cvtchar (sp, &c); 446 arg_list[termcap - 1].integer = c - i; 447 } 448 if (fmt == NULL) 449 break; 450 sp--; 451 } 452 if (!termcap) 453 return OOPS; 454 /* FALLTHROUGH */ 455 case '.': 456 if (termcap && fmt == NULL) 457 fmt = "%c"; 458 /* FALLTHROUGH */ 459 case 'd': 460 if (termcap && fmt == NULL) 461 fmt = "%d"; 462 /* FALLTHROUGH */ 463 case '2': 464 if (termcap && fmt == NULL) 465 fmt = "%02d"; 466 /* FALLTHROUGH */ 467 case '3': 468 if (termcap && fmt == NULL) 469 fmt = "%03d"; 470 /* FALLTHROUGH */ 471 case ':': case ' ': case '#': case 'u': 472 case 'x': case 'X': case 'o': case 'c': 473 case '0': case '1': case '4': case '5': 474 case '6': case '7': case '8': case '9': 475 if (fmt == NULL) 476 { 477 if (termcap) 478 return OOPS; 479 if (*sp == ':') 480 sp++; 481 fmt = fmt_buf; 482 *fmt++ = '%'; 483 while (*sp != 's' && *sp != 'x' && *sp != 'X' && *sp != 'd' 484 && *sp != 'o' && *sp != 'c' && *sp != 'u') 485 { 486 if (*sp == '\0') 487 return OOPS; 488 *fmt++ = *sp++; 489 } 490 *fmt++ = *sp; 491 *fmt = '\0'; 492 fmt = fmt_buf; 493 } 494 { 495 char conv_char = fmt[strlen (fmt) - 1]; 496 if (conv_char == 's') 497 { 498 char *s; 499 if (popstring (&s)) 500 return OOPS; 501 sprintf (sbuf, fmt, s); 502 } 503 else 504 { 505 int i; 506 if (termcap) 507 { 508 if (getarg (termcap++ - 1, INTEGER, &i)) 509 return OOPS; 510 } 511 else 512 if (popnum (&i)) 513 return OOPS; 514 if (i == 0 && conv_char == 'c') 515 strcpy (sbuf, "\000"); 516 else 517 sprintf (sbuf, fmt, i); 518 } 519 } 520 sp++; 521 fmt = sbuf; 522 while (*fmt != '\0') 523 { 524 if (*fmt == '$') 525 *dp++ = '\\'; 526 *dp++ = *fmt++; 527 } 528 break; 529 case 'r': 530 { 531 int i; 532 if (!termcap || getarg (1, INTEGER, &i)) 533 return OOPS; 534 arg_list[1].integer = arg_list[0].integer; 535 arg_list[0].integer = i; 536 } 537 sp++; 538 break; 539 case 'i': 540 { 541 int i; 542 if (getarg (1, INTEGER, &i) || arg_list[0].type != INTEGER) 543 return OOPS; 544 } 545 arg_list[1].integer++; 546 arg_list[0].integer++; 547 sp++; 548 break; 549 case 'n': 550 { 551 int i; 552 if (!termcap || getarg (1, INTEGER, &i)) 553 return OOPS; 554 } 555 arg_list[0].integer ^= 0140; 556 arg_list[1].integer ^= 0140; 557 sp++; 558 break; 559 case '>': 560 if (!termcap) 561 { 562 int i, j; 563 if (popnum (&j) || popnum (&i)) 564 return OOPS; 565 i = (i > j); 566 if (pushnum (i)) 567 return OOPS; 568 sp++; 569 break; 570 } 571 { 572 int i; 573 if (getarg (termcap-1, INTEGER, &i)) 574 return OOPS; 575 { 576 char c; 577 sp += cvtchar (sp, &c); 578 if (i > c) 579 { 580 sp += cvtchar (sp, &c); 581 arg_list[termcap-1].integer += c; 582 } 583 else 584 sp += cvtchar (sp, &c); 585 } 586 } 587 sp++; 588 break; 589 case 'B': 590 { 591 int i; 592 if (!termcap || getarg (termcap-1, INTEGER, &i)) 593 return OOPS; 594 arg_list[termcap-1].integer = 16 * (i / 10) + i % 10; 595 } 596 sp++; 597 break; 598 case 'D': 599 { 600 int i; 601 if (!termcap || getarg (termcap-1, INTEGER, &i)) 602 return OOPS; 603 arg_list[termcap-1].integer = i - 2 * (i % 16); 604 } 605 sp++; 606 break; 607 case 'p': 608 if (termcap > 1) 609 return OOPS; 610 if (*++sp == '\0') 611 return OOPS; 612 { 613 int i = (*sp == '0' ? 9 : *sp - '1'); 614 if (i < 0 || i > 9) 615 return OOPS; 616 if (pusharg (i)) 617 return OOPS; 618 } 619 termcap = 0; 620 sp++; 621 break; 622 case 'P': 623 if (termcap || *++sp == '\0') 624 return OOPS; 625 { 626 int i = *sp++ - 'a'; 627 if (i < 0 || i > 25) 628 return OOPS; 629 if (pos-- == 0) 630 return OOPS; 631 switch (vars[i].type = S[pos].type) 632 { 633 case ARG: 634 vars[i].argnum = S[pos].argnum; 635 break; 636 case NUM: 637 vars[i].value = S[pos].value; 638 break; 639 } 640 } 641 break; 642 case 'g': 643 if (termcap || *++sp == '\0') 644 return OOPS; 645 { 646 int i = *sp++ - 'a'; 647 if (i < 0 || i > 25) 648 return OOPS; 649 switch (vars[i].type) 650 { 651 case ARG: 652 if (pusharg (vars[i].argnum)) 653 return OOPS; 654 break; 655 case NUM: 656 if (pushnum (vars[i].value)) 657 return OOPS; 658 break; 659 } 660 } 661 break; 662 case '\'': 663 if (termcap > 1) 664 return OOPS; 665 if (*++sp == '\0') 666 return OOPS; 667 { 668 char c; 669 sp += cvtchar (sp, &c); 670 if (pushnum (c) || *sp++ != '\'') 671 return OOPS; 672 } 673 termcap = 0; 674 break; 675 case '{': 676 if (termcap > 1) 677 return OOPS; 678 { 679 int i; 680 i = 0; 681 sp++; 682 while (c_isdigit (*sp)) 683 i = 10 * i + *sp++ - '0'; 684 if (*sp++ != '}' || pushnum (i)) 685 return OOPS; 686 } 687 termcap = 0; 688 break; 689 case 'l': 690 { 691 int i; 692 char *s; 693 if (termcap || popstring (&s)) 694 return OOPS; 695 i = strlen (s); 696 if (pushnum (i)) 697 return OOPS; 698 } 699 sp++; 700 break; 701 case '*': 702 { 703 int i, j; 704 if (termcap || popnum (&j) || popnum (&i)) 705 return OOPS; 706 i *= j; 707 if (pushnum (i)) 708 return OOPS; 709 } 710 sp++; 711 break; 712 case '/': 713 { 714 int i, j; 715 if (termcap || popnum (&j) || popnum (&i)) 716 return OOPS; 717 i /= j; 718 if (pushnum (i)) 719 return OOPS; 720 } 721 sp++; 722 break; 723 case 'm': 724 if (termcap) 725 { 726 int i; 727 if (getarg (1, INTEGER, &i)) 728 return OOPS; 729 arg_list[0].integer ^= 0177; 730 arg_list[1].integer ^= 0177; 731 sp++; 732 break; 733 } 734 { 735 int i, j; 736 if (popnum (&j) || popnum (&i)) 737 return OOPS; 738 i %= j; 739 if (pushnum (i)) 740 return OOPS; 741 } 742 sp++; 743 break; 744 case '&': 745 { 746 int i, j; 747 if (popnum (&j) || popnum (&i)) 748 return OOPS; 749 i &= j; 750 if (pushnum (i)) 751 return OOPS; 752 } 753 sp++; 754 break; 755 case '|': 756 { 757 int i, j; 758 if (popnum (&j) || popnum (&i)) 759 return OOPS; 760 i |= j; 761 if (pushnum (i)) 762 return OOPS; 763 } 764 sp++; 765 break; 766 case '^': 767 { 768 int i, j; 769 if (popnum (&j) || popnum (&i)) 770 return OOPS; 771 i ^= j; 772 if (pushnum (i)) 773 return OOPS; 774 } 775 sp++; 776 break; 777 case '=': 778 { 779 int i, j; 780 if (popnum (&j) || popnum (&i)) 781 return OOPS; 782 i = (i == j); 783 if (pushnum (i)) 784 return OOPS; 785 } 786 sp++; 787 break; 788 case '<': 789 { 790 int i, j; 791 if (popnum (&j) || popnum (&i)) 792 return OOPS; 793 i = (i < j); 794 if (pushnum (i)) 795 return OOPS; 796 } 797 sp++; 798 break; 799 case 'A': 800 { 801 int i, j; 802 if (popnum (&j) || popnum (&i)) 803 return OOPS; 804 i = (i && j); 805 if (pushnum (i)) 806 return OOPS; 807 } 808 sp++; 809 break; 810 case 'O': 811 { 812 int i, j; 813 if (popnum (&j) || popnum (&i)) 814 return OOPS; 815 i = (i || j); 816 if (pushnum (i)) 817 return OOPS; 818 } 819 sp++; 820 break; 821 case '!': 822 { 823 int i; 824 if (popnum (&i)) 825 return OOPS; 826 i = !i; 827 if (pushnum (i)) 828 return OOPS; 829 } 830 sp++; 831 break; 832 case '~': 833 { 834 int i; 835 if (popnum (&i)) 836 return OOPS; 837 i = ~i; 838 if (pushnum (i)) 839 return OOPS; 840 } 841 sp++; 842 break; 843 case '?': 844 if (termcap > 1) 845 return OOPS; 846 termcap = 0; 847 if_depth++; 848 sp++; 849 break; 850 case 't': 851 { 852 int i; 853 if (popnum (&i) || if_depth == 0) 854 return OOPS; 855 if (!i) 856 { 857 scan_for = 'e'; 858 scan_depth = if_depth; 859 } 860 } 861 sp++; 862 break; 863 case 'e': 864 if (if_depth == 0) 865 return OOPS; 866 scan_for = ';'; 867 scan_depth = if_depth; 868 sp++; 869 break; 870 case ';': 871 if (if_depth-- == 0) 872 return OOPS; 873 sp++; 874 break; 875 case 'b': 876 if (--termcap < 1) 877 return OOPS; 878 sp++; 879 break; 880 case 'f': 881 if (!termcap++) 882 return OOPS; 883 sp++; 884 break; 885 } 886 break; 887 default: 888 if (scan_for) 889 sp++; 890 else 891 *dp++ = *sp++; 892 break; 893 } 894 } 895 va_end (tparm_args); 896 *dp = '\0'; 897 return buf; 898} 899