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 }