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 /* The following routine was added to make lint shut up about converting from
 139  * a long to a char *.  It is identical to the pop routine, except for the
 140  * cast on the return statement.
 141  */
 142 static char *
 143 pop_char_p(STACK *st)
 144 {
 145         if (st->top < 0) {
 146                 longjmp(env, STACK_UNDERFLOW);
 147         }
 148         return ((char *)(st->stack[st->top--]));
 149 }
 150 
 151 static void
 152 init_stack(STACK *st)
 153 {
 154         st->top = -1;
 155         st->stacksize = MAX;
 156         if ((st->stack = (void *)malloc(MAX * sizeof (long))) == NULL) {
 157                 longjmp(env, MEM_ALLOC_FAIL);
 158         }
 159 }
 160 
 161 static void
 162 free_stack(STACK *st)
 163 {
 164         free(st->stack);
 165 }
 166 
 167 
 168 char    *
 169 tparm_p0(char *instring)
 170 {
 171         long    p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
 172 
 173         return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6],
 174                         p[7], p[8]));
 175 }
 176 
 177 char    *
 178 tparm_p1(char *instring, long l1)
 179 {
 180         long    p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
 181 
 182         p[0] = l1;
 183 
 184         return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6],
 185                         p[7], p[8]));
 186 }
 187 
 188 char    *
 189 tparm_p2(char *instring, long l1, long l2)
 190 {
 191         long    p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
 192 
 193         p[0] = l1;
 194         p[1] = l2;
 195 
 196         return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6],
 197                         p[7], p[8]));
 198 }
 199 
 200 char    *
 201 tparm_p3(char *instring, long l1, long l2, long l3)
 202 {
 203         long    p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
 204 
 205         p[0] = l1;
 206         p[1] = l2;
 207         p[2] = l3;
 208 
 209         return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6],
 210                         p[7], p[8]));
 211 }
 212 
 213 char    *
 214 tparm_p4(char *instring, long l1, long l2, long l3, long l4)
 215 {
 216         long    p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
 217 
 218         p[0] = l1;
 219         p[1] = l2;
 220         p[2] = l3;
 221         p[3] = l4;
 222 
 223         return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6],
 224                         p[7], p[8]));
 225 }
 226 
 227 char    *
 228 tparm_p7(char *instring, long l1, long l2, long l3, long l4, long l5, long l6,
 229         long l7)
 230 {
 231         long    p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
 232 
 233         p[0] = l1;
 234         p[1] = l2;
 235         p[2] = l3;
 236         p[3] = l4;
 237         p[4] = l5;
 238         p[5] = l6;
 239         p[6] = l7;
 240 
 241         return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6],
 242                         p[7], p[8]));
 243 }
 244 
 245 /* VARARGS */
 246 char    *
 247 tparm(char *instring, long fp1, long fp2, long p3, long p4,
 248         long p5, long p6, long p7, long p8, long p9)
 249 {
 250         static  char    result[512];
 251         static  char    added[100];
 252         long            vars[26];
 253         STACK           stk;
 254         char            *cp = instring;
 255         char            *outp = result;
 256         char            c;
 257         long            op;
 258         long            op2;
 259         int             sign;
 260         int             onrow = 0;
 261         volatile long   p1 = fp1, p2 = fp2; /* copy in case < 2 actual parms */
 262         char            *xp;
 263         char            formatbuffer[100];
 264         char            *format;
 265         int             looping;
 266         short           *regs = cur_term->_regs;
 267         int             val;
 268 
 269 
 270         if ((val = setjmp(env)) != 0) {
 271 #ifdef DEBUG
 272                 switch (val) {
 273                         case MEM_ALLOC_FAIL:
 274                                 fprintf(outf, "TPARM: Memory allocation"
 275                                     " failure.");
 276                                 break;
 277                         case STACK_UNDERFLOW:
 278                                 fprintf(outf, "TPARM: Stack underflow.");
 279                                 break;
 280                 }
 281 #endif  /* DEBUG */
 282 
 283                 if (val == STACK_UNDERFLOW)
 284                         free_stack(&stk);
 285                 return (NULL);
 286         }
 287 
 288         init_stack(&stk);
 289         push(&stk, 0);
 290 
 291         if (instring == 0) {
 292 #ifdef  DEBUG
 293                 if (outf)
 294                         fprintf(outf, "TPARM: null arg\n");
 295 #endif  /* DEBUG */
 296                 free_stack(&stk);
 297                 return (NULL);
 298         }
 299 
 300         added[0] = 0;
 301 
 302         while ((c = *cp++) != 0) {
 303                 if (c != '%') {
 304                         *outp++ = c;
 305                         continue;
 306                 }
 307                 op = tops(&stk);
 308                 switch (c = *cp++) {
 309                         /* PRINTING CASES */
 310                         case ':':
 311                         case ' ':
 312                         case '#':
 313                         case '0':
 314                         case '1':
 315                         case '2':
 316                         case '3':
 317                         case '4':
 318                         case '5':
 319                         case '6':
 320                         case '7':
 321                         case '8':
 322                         case '9':
 323                         case '.':
 324                         case 'd':
 325                         case 's':
 326                         case 'o':
 327                         case 'x':
 328                         case 'X':
 329                                 format = formatbuffer;
 330                                 *format++ = '%';
 331 
 332                         /* leading ':' to allow +/- in format */
 333                         if (c == ':')
 334                                 c = *cp++;
 335 
 336                         /* take care of flags, width and precision */
 337                         looping = 1;
 338                         while (c && looping)
 339                                 switch (c) {
 340                                         case '-':
 341                                         case '+':
 342                                         case ' ':
 343                                         case '#':
 344                                         case '0':
 345                                         case '1':
 346                                         case '2':
 347                                         case '3':
 348                                         case '4':
 349                                         case '5':
 350                                         case '6':
 351                                         case '7':
 352                                         case '8':
 353                                         case '9':
 354                                         case '.':
 355                                                 *format++ = c;
 356                                                 c = *cp++;
 357                                                 break;
 358                                         default:
 359                                                 looping = 0;
 360                                 }
 361 
 362                         /* add in the conversion type */
 363                         switch (c) {
 364                                 case 'd':
 365                                 case 's':
 366                                 case 'o':
 367                                 case 'x':
 368                                 case 'X':
 369                                         *format++ = c;
 370                                         break;
 371                                 default:
 372 #ifdef  DEBUG
 373                                 if (outf)
 374                                         fprintf(outf, "TPARM: invalid "
 375                                             "conversion type\n");
 376 #endif  /* DEBUG */
 377                                 free_stack(&stk);
 378                                 return (NULL);
 379                         }
 380                         *format = '\0';
 381 
 382                         /*
 383                          * Pass off the dirty work to sprintf.
 384                          * It's debatable whether we should just pull in
 385                          * the appropriate code here. I decided not to for
 386                          * now.
 387                          */
 388                         if (c == 's')
 389                                 (void) sprintf(outp, formatbuffer,
 390                                     (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 }