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
tops(STACK * st)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
push(STACK * st,long i)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
pop(STACK * st)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 *
pop_char_p(STACK * st)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
init_stack(STACK * st)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
free_stack(STACK * st)163 free_stack(STACK *st)
164 {
165 free(st->stack);
166 }
167
168
169 char *
tparm_p0(char * instring)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 *
tparm_p1(char * instring,long l1)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 *
tparm_p2(char * instring,long l1,long l2)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 *
tparm_p3(char * instring,long l1,long l2,long l3)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 *
tparm_p4(char * instring,long l1,long l2,long l3,long l4)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 *
tparm_p7(char * instring,long l1,long l2,long l3,long l4,long l5,long l6,long l7)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 *
tparm(char * instring,long fp1,long fp2,long p3,long p4,long p5,long p6,long p7,long p8,long p9)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 *
_branchto(register char * cp,char to)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