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