1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1996-1997 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 /* Copyright (c) 1988 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 /* Copyright (c) 1979 Regents of the University of California */ 34 35 /*LINTLIBRARY*/ 36 37 #include "curses_inc.h" 38 #include "curshdr.h" 39 #include "term.h" 40 #include <string.h> 41 #include <setjmp.h> 42 #include <stdlib.h> 43 #include <stdio.h> 44 45 #ifndef _CHCTRL 46 #define _CHCTRL(c) ((c) & 037) 47 #endif /* _CHCTRL */ 48 49 char *_branchto(char *, char); 50 51 /* 52 * Routine to perform parameter substitution. 53 * instring is a string containing printf type escapes. 54 * The whole thing uses a stack, much like an HP 35. 55 * The following escapes are defined for substituting row/column: 56 * 57 * %[:[-+ #0]][0-9][.][0-9][dsoxX] 58 * print pop() as in printf(3), as defined in the local 59 * sprintf(3), except that a leading + or - must be preceded 60 * with a colon (:) to distinguish from the plus/minus operators. 61 * 62 * %c print pop() like %c in printf(3) 63 * %l pop() a string address and push its length. 64 * %P[a-z] set dynamic variable a-z 65 * %g[a-z] get dynamic variable a-z 66 * %P[A-Z] set static variable A-Z 67 * %g[A-Z] get static variable A-Z 68 * 69 * %p[1-0] push ith parm 70 * %'c' char constant c 71 * %{nn} integer constant nn 72 * 73 * %+ %- %* %/ %m arithmetic (%m is mod): push(pop() op pop()) 74 * %& %| %^ bit operations: push(pop() op pop()) 75 * %= %> %< logical operations: push(pop() op pop()) 76 * %A %O logical AND, OR push(pop() op pop()) 77 * %! %~ unary operations push(op pop()) 78 * %% output % 79 * %? expr %t thenpart %e elsepart %; 80 * if-then-else, %e elsepart is optional. 81 * else-if's are possible ala Algol 68: 82 * %? c1 %t %e c2 %t %e c3 %t %e c4 %t %e %; 83 * % followed by anything else 84 * is not defined, it may output the character, 85 * and it may not. This is done so that further 86 * enhancements to the format capabilities may 87 * be made without worrying about being upwardly 88 * compatible from buggy code. 89 * 90 * all other characters are ``self-inserting''. %% gets % output. 91 * 92 * The stack structure used here is based on an idea by Joseph Yao. 93 */ 94 95 #define MAX 10 96 #define MEM_ALLOC_FAIL 1 97 #define STACK_UNDERFLOW 2 98 99 typedef struct { 100 long top; 101 int stacksize; 102 long *stack; 103 104 }STACK; 105 106 static jmp_buf env; 107 108 static long 109 tops(STACK *st) 110 { 111 112 if (st->top < 0) { 113 longjmp(env, STACK_UNDERFLOW); 114 } 115 return (st->stack[st->top]); 116 } 117 118 static void 119 push(STACK *st, long i) 120 { 121 if (st->top >= (st->stacksize - 1)) { 122 st->stacksize += MAX; 123 if ((st->stack = (void *)realloc(st->stack, 124 (st->stacksize * sizeof (long)))) == NULL) { 125 longjmp(env, MEM_ALLOC_FAIL); 126 } 127 } 128 st->stack[++st->top] = (i); 129 } 130 131 static long 132 pop(STACK *st) 133 { 134 if (st->top < 0) { 135 longjmp(env, STACK_UNDERFLOW); 136 } 137 return (st->stack[st->top--]); 138 } 139 140 /* The following routine was added to make lint shut up about converting from 141 * a long to a char *. It is identical to the pop routine, except for the 142 * cast on the return statement. 143 */ 144 static char * 145 pop_char_p(STACK *st) 146 { 147 if (st->top < 0) { 148 longjmp(env, STACK_UNDERFLOW); 149 } 150 return ((char *)(st->stack[st->top--])); 151 } 152 153 static void 154 init_stack(STACK *st) 155 { 156 st->top = -1; 157 st->stacksize = MAX; 158 if ((st->stack = (void *)malloc(MAX * sizeof (long))) == NULL) { 159 longjmp(env, MEM_ALLOC_FAIL); 160 } 161 } 162 163 static void 164 free_stack(STACK *st) 165 { 166 free(st->stack); 167 } 168 169 170 char * 171 tparm_p0(char *instring) 172 { 173 long p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; 174 175 return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6], 176 p[7], p[8])); 177 } 178 179 char * 180 tparm_p1(char *instring, long l1) 181 { 182 long p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; 183 184 p[0] = l1; 185 186 return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6], 187 p[7], p[8])); 188 } 189 190 char * 191 tparm_p2(char *instring, long l1, long l2) 192 { 193 long p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; 194 195 p[0] = l1; 196 p[1] = l2; 197 198 return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6], 199 p[7], p[8])); 200 } 201 202 char * 203 tparm_p3(char *instring, long l1, long l2, long l3) 204 { 205 long p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; 206 207 p[0] = l1; 208 p[1] = l2; 209 p[2] = l3; 210 211 return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6], 212 p[7], p[8])); 213 } 214 215 char * 216 tparm_p4(char *instring, long l1, long l2, long l3, long l4) 217 { 218 long p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; 219 220 p[0] = l1; 221 p[1] = l2; 222 p[2] = l3; 223 p[3] = l4; 224 225 return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6], 226 p[7], p[8])); 227 } 228 229 char * 230 tparm_p7(char *instring, long l1, long l2, long l3, long l4, long l5, long l6, 231 long l7) 232 { 233 long p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; 234 235 p[0] = l1; 236 p[1] = l2; 237 p[2] = l3; 238 p[3] = l4; 239 p[4] = l5; 240 p[5] = l6; 241 p[6] = l7; 242 243 return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6], 244 p[7], p[8])); 245 } 246 247 /* VARARGS */ 248 char * 249 tparm(char *instring, long fp1, long fp2, long p3, long p4, 250 long p5, long p6, long p7, long p8, long p9) 251 { 252 static char result[512]; 253 static char added[100]; 254 long vars[26]; 255 STACK stk; 256 char *cp = instring; 257 char *outp = result; 258 char c; 259 long op; 260 long op2; 261 int sign; 262 int onrow = 0; 263 long p1 = fp1, p2 = fp2; /* copy in case < 2 actual parms */ 264 char *xp; 265 char formatbuffer[100]; 266 char *format; 267 int looping; 268 short *regs = cur_term->_regs; 269 int val; 270 271 272 if ((val = setjmp(env)) != 0) { 273 #ifdef DEBUG 274 switch (val) { 275 case MEM_ALLOC_FAIL: 276 fprintf(outf, "TPARM: Memory allocation" 277 " failure."); 278 break; 279 case STACK_UNDERFLOW: 280 fprintf(outf, "TPARM: Stack underflow."); 281 break; 282 } 283 #endif /* DEBUG */ 284 285 if (val == STACK_UNDERFLOW) 286 free_stack(&stk); 287 return (NULL); 288 } 289 290 init_stack(&stk); 291 push(&stk, 0); 292 293 if (instring == 0) { 294 #ifdef DEBUG 295 if (outf) 296 fprintf(outf, "TPARM: null arg\n"); 297 #endif /* DEBUG */ 298 free_stack(&stk); 299 return (NULL); 300 } 301 302 added[0] = 0; 303 304 while ((c = *cp++) != 0) { 305 if (c != '%') { 306 *outp++ = c; 307 continue; 308 } 309 op = tops(&stk); 310 switch (c = *cp++) { 311 /* PRINTING CASES */ 312 case ':': 313 case ' ': 314 case '#': 315 case '0': 316 case '1': 317 case '2': 318 case '3': 319 case '4': 320 case '5': 321 case '6': 322 case '7': 323 case '8': 324 case '9': 325 case '.': 326 case 'd': 327 case 's': 328 case 'o': 329 case 'x': 330 case 'X': 331 format = formatbuffer; 332 *format++ = '%'; 333 334 /* leading ':' to allow +/- in format */ 335 if (c == ':') 336 c = *cp++; 337 338 /* take care of flags, width and precision */ 339 looping = 1; 340 while (c && looping) 341 switch (c) { 342 case '-': 343 case '+': 344 case ' ': 345 case '#': 346 case '0': 347 case '1': 348 case '2': 349 case '3': 350 case '4': 351 case '5': 352 case '6': 353 case '7': 354 case '8': 355 case '9': 356 case '.': 357 *format++ = c; 358 c = *cp++; 359 break; 360 default: 361 looping = 0; 362 } 363 364 /* add in the conversion type */ 365 switch (c) { 366 case 'd': 367 case 's': 368 case 'o': 369 case 'x': 370 case 'X': 371 *format++ = c; 372 break; 373 default: 374 #ifdef DEBUG 375 if (outf) 376 fprintf(outf, "TPARM: invalid " 377 "conversion type\n"); 378 #endif /* DEBUG */ 379 free_stack(&stk); 380 return (NULL); 381 } 382 *format = '\0'; 383 384 /* 385 * Pass off the dirty work to sprintf. 386 * It's debatable whether we should just pull in 387 * the appropriate code here. I decided not to for 388 * now. 389 */ 390 if (c == 's') 391 (void) sprintf(outp, formatbuffer, 392 (char *) op); 393 else 394 (void) sprintf(outp, formatbuffer, op); 395 /* 396 * Advance outp past what sprintf just did. 397 * sprintf returns an indication of its length on some 398 * systems, others the first char, and there's 399 * no easy way to tell which. The Sys V on 400 * BSD emulations are particularly confusing. 401 */ 402 while (*outp) 403 outp++; 404 (void) pop(&stk); 405 406 continue; 407 408 case 'c': 409 /* 410 * This code is worth scratching your head at for a 411 * while. The idea is that various weird things can 412 * happen to nulls, EOT's, tabs, and newlines by the 413 * tty driver, arpanet, and so on, so we don't send 414 * them if we can help it. So we instead alter the 415 * place being addessed and then move the cursor 416 * locally using UP or RIGHT. 417 * 418 * This is a kludge, clearly. It loses if the 419 * parameterized string isn't addressing the cursor 420 * (but hopefully that is all that %c terminals do 421 * with parms). Also, since tab and newline happen 422 * to be next to each other in ASCII, if tab were 423 * included a loop would be needed. Finally, note 424 * that lots of other processing is done here, so 425 * this hack won't always work (e.g. the Ann Arbor 426 * 4080, which uses %B and then %c.) 427 */ 428 switch (op) { 429 /* 430 * Null. Problem is that our 431 * output is, by convention, null terminated. 432 */ 433 case 0: 434 op = 0200; /* Parity should */ 435 /* be ignored. */ 436 break; 437 /* 438 * Control D. Problem is that certain very 439 * ancient hardware hangs up on this, so the 440 * current(!) UNIX tty driver doesn't xmit 441 * control D's. 442 */ 443 case _CHCTRL('d'): 444 /* 445 * Newline. Problem is that UNIX will expand 446 * this to CRLF. 447 */ 448 case '\n': 449 xp = (onrow ? cursor_down : 450 cursor_right); 451 if (onrow && xp && op < lines-1 && 452 cursor_up) { 453 op += 2; 454 xp = cursor_up; 455 } 456 if (xp && instring == 457 cursor_address) { 458 (void) strcat(added, xp); 459 op--; 460 } 461 break; 462 /* 463 * Tab used to be in this group too, 464 * because UNIX might expand it to blanks. 465 * We now require that this tab mode be turned 466 * off by any program using this routine, 467 * or using termcap in general, since some 468 * terminals use tab for other stuff, like 469 * nondestructive space. (Filters like ul 470 * or vcrt will lose, since they can't stty.) 471 * Tab was taken out to get the Ann Arbor 472 * 4080 to work. 473 */ 474 } 475 476 /* LINTED */ 477 *outp++ = (char)op; 478 (void) pop(&stk); 479 break; 480 481 case 'l': 482 xp = pop_char_p(&stk); 483 push(&stk, strlen(xp)); 484 break; 485 486 case '%': 487 *outp++ = c; 488 break; 489 490 /* 491 * %i: shorthand for increment first two parms. 492 * Useful for terminals that start numbering from 493 * one instead of zero(like ANSI terminals). 494 */ 495 case 'i': 496 p1++; 497 p2++; 498 break; 499 500 /* %pi: push the ith parameter */ 501 case 'p': 502 switch (c = *cp++) { 503 case '1': 504 push(&stk, p1); 505 break; 506 case '2': 507 push(&stk, p2); 508 break; 509 case '3': 510 push(&stk, p3); 511 break; 512 case '4': 513 push(&stk, p4); 514 break; 515 case '5': 516 push(&stk, p5); 517 break; 518 case '6': 519 push(&stk, p6); 520 break; 521 case '7': 522 push(&stk, p7); 523 break; 524 case '8': 525 push(&stk, p8); 526 break; 527 case '9': 528 push(&stk, p9); 529 break; 530 default: 531 #ifdef DEBUG 532 if (outf) 533 fprintf(outf, "TPARM:" 534 " bad parm" 535 " number\n"); 536 #endif /* DEBUG */ 537 free_stack(&stk); 538 return (NULL); 539 } 540 onrow = (c == '1'); 541 break; 542 543 /* %Pi: pop from stack into variable i (a-z) */ 544 case 'P': 545 if (*cp >= 'a' && *cp <= 'z') { 546 vars[*cp++ - 'a'] = pop(&stk); 547 } else { 548 if (*cp >= 'A' && *cp <= 'Z') { 549 regs[*cp++ - 'A'] = 550 /* LINTED */ 551 (short) pop(&stk); 552 } 553 #ifdef DEBUG 554 else if (outf) { 555 fprintf(outf, "TPARM: bad" 556 " register name\n"); 557 } 558 #endif /* DEBUG */ 559 } 560 break; 561 562 /* %gi: push variable i (a-z) */ 563 case 'g': 564 if (*cp >= 'a' && *cp <= 'z') { 565 push(&stk, vars[*cp++ - 'a']); 566 } else { 567 if (*cp >= 'A' && *cp <= 'Z') { 568 push(&stk, regs[*cp++ - 'A']); 569 } 570 #ifdef DEBUG 571 else if (outf) { 572 fprintf(outf, "TPARM: bad" 573 " register name\n"); 574 575 } 576 #endif /* DEBUG */ 577 } 578 break; 579 580 /* %'c' : character constant */ 581 case '\'': 582 push(&stk, *cp++); 583 if (*cp++ != '\'') { 584 #ifdef DEBUG 585 if (outf) 586 fprintf(outf, "TPARM: missing" 587 " closing quote\n"); 588 #endif /* DEBUG */ 589 free_stack(&stk); 590 return (NULL); 591 } 592 break; 593 594 /* %{nn} : integer constant. */ 595 case '{': 596 op = 0; 597 sign = 1; 598 if (*cp == '-') { 599 sign = -1; 600 cp++; 601 } else 602 if (*cp == '+') 603 cp++; 604 while ((c = *cp++) >= '0' && c <= '9') { 605 op = 10 * op + c - '0'; 606 } 607 if (c != '}') { 608 #ifdef DEBUG 609 if (outf) 610 fprintf(outf, "TPARM: missing " 611 "closing brace\n"); 612 #endif /* DEBUG */ 613 free_stack(&stk); 614 return (NULL); 615 } 616 push(&stk, (sign * op)); 617 break; 618 619 /* binary operators */ 620 case '+': 621 op2 = pop(&stk); 622 op = pop(&stk); 623 push(&stk, (op + op2)); 624 break; 625 case '-': 626 op2 = pop(&stk); 627 op = pop(&stk); 628 push(&stk, (op - op2)); 629 break; 630 case '*': 631 op2 = pop(&stk); 632 op = pop(&stk); 633 push(&stk, (op * op2)); 634 break; 635 case '/': 636 op2 = pop(&stk); 637 op = pop(&stk); 638 push(&stk, (op / op2)); 639 break; 640 case 'm': 641 op2 = pop(&stk); 642 op = pop(&stk); 643 push(&stk, (op % op2)); 644 break; /* %m: mod */ 645 case '&': 646 op2 = pop(&stk); 647 op = pop(&stk); 648 push(&stk, (op & op2)); 649 break; 650 case '|': 651 op2 = pop(&stk); 652 op = pop(&stk); 653 push(&stk, (op | op2)); 654 break; 655 case '^': 656 op2 = pop(&stk); 657 op = pop(&stk); 658 push(&stk, (op ^ op2)); 659 break; 660 case '=': 661 op2 = pop(&stk); 662 op = pop(&stk); 663 push(&stk, (op == op2)); 664 break; 665 case '>': 666 op2 = pop(&stk); 667 op = pop(&stk); 668 push(&stk, (op > op2)); 669 break; 670 case '<': 671 op2 = pop(&stk); 672 op = pop(&stk); 673 push(&stk, (op < op2)); 674 break; 675 case 'A': 676 op2 = pop(&stk); 677 op = pop(&stk); 678 push(&stk, (op && op2)); 679 break; /* AND */ 680 case 'O': 681 op2 = pop(&stk); 682 op = pop(&stk); 683 push(&stk, (op || op2)); 684 break; /* OR */ 685 686 /* Unary operators. */ 687 case '!': 688 push(&stk, !pop(&stk)); 689 break; 690 case '~': 691 push(&stk, ~pop(&stk)); 692 break; 693 694 /* Sorry, no unary minus, because minus is binary. */ 695 696 /* 697 * If-then-else. Implemented by a low level hack of 698 * skipping forward until the match is found, counting 699 * nested if-then-elses. 700 */ 701 case '?': /* IF - just a marker */ 702 break; 703 704 case 't': /* THEN - branch if false */ 705 if (!pop(&stk)) 706 cp = _branchto(cp, 'e'); 707 break; 708 709 case 'e': /* ELSE - branch to ENDIF */ 710 cp = _branchto(cp, ';'); 711 break; 712 713 case ';': /* ENDIF - just a marker */ 714 break; 715 716 default: 717 #ifdef DEBUG 718 if (outf) 719 fprintf(outf, "TPARM: bad % " 720 "sequence\n"); 721 #endif /* DEBUG */ 722 free_stack(&stk); 723 return (NULL); 724 } 725 } 726 (void) strcpy(outp, added); 727 free_stack(&stk); 728 return (result); 729 } 730 731 char * 732 _branchto(register char *cp, char to) 733 { 734 register int level = 0; 735 register char c; 736 737 while (c = *cp++) { 738 if (c == '%') { 739 if ((c = *cp++) == to || c == ';') { 740 if (level == 0) { 741 return (cp); 742 } 743 } 744 if (c == '?') 745 level++; 746 if (c == ';') 747 level--; 748 } 749 } 750 #ifdef DEBUG 751 if (outf) 752 fprintf(outf, "TPARM: no matching ENDIF"); 753 #endif /* DEBUG */ 754 return (NULL); 755 }