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