2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
2N/A * All rights reserved.
2N/A */
2N/A
2N/A/* Copyright (c) 1988 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/* Copyright (c) 1979 Regents of the University of California */
2N/A
2N/A/*LINTLIBRARY*/
2N/A
2N/A#include "curses_inc.h"
2N/A#include "curshdr.h"
2N/A#include "term.h"
2N/A#include <string.h>
2N/A#include <setjmp.h>
2N/A#include <stdlib.h>
2N/A#include <stdio.h>
2N/A
2N/A#ifndef _CHCTRL
2N/A#define _CHCTRL(c) ((c) & 037)
2N/A#endif /* _CHCTRL */
2N/A
2N/Achar *_branchto(char *, char);
2N/A
2N/A/*
2N/A * Routine to perform parameter substitution.
2N/A * instring is a string containing printf type escapes.
2N/A * The whole thing uses a stack, much like an HP 35.
2N/A * The following escapes are defined for substituting row/column:
2N/A *
2N/A * %[:[-+ #0]][0-9][.][0-9][dsoxX]
2N/A * print pop() as in printf(3), as defined in the local
2N/A * sprintf(3), except that a leading + or - must be preceded
2N/A * with a colon (:) to distinguish from the plus/minus operators.
2N/A *
2N/A * %c print pop() like %c in printf(3)
2N/A * %l pop() a string address and push its length.
2N/A * %P[a-z] set dynamic variable a-z
2N/A * %g[a-z] get dynamic variable a-z
2N/A * %P[A-Z] set static variable A-Z
2N/A * %g[A-Z] get static variable A-Z
2N/A *
2N/A * %p[1-0] push ith parm
2N/A * %'c' char constant c
2N/A * %{nn} integer constant nn
2N/A *
2N/A * %+ %- %* %/ %m arithmetic (%m is mod): push(pop() op pop())
2N/A * %& %| %^ bit operations: push(pop() op pop())
2N/A * %= %> %< logical operations: push(pop() op pop())
2N/A * %A %O logical AND, OR push(pop() op pop())
2N/A * %! %~ unary operations push(op pop())
2N/A * %% output %
2N/A * %? expr %t thenpart %e elsepart %;
2N/A * if-then-else, %e elsepart is optional.
2N/A * else-if's are possible ala Algol 68:
2N/A * %? c1 %t %e c2 %t %e c3 %t %e c4 %t %e %;
2N/A * % followed by anything else
2N/A * is not defined, it may output the character,
2N/A * and it may not. This is done so that further
2N/A * enhancements to the format capabilities may
2N/A * be made without worrying about being upwardly
2N/A * compatible from buggy code.
2N/A *
2N/A * all other characters are ``self-inserting''. %% gets % output.
2N/A *
2N/A * The stack structure used here is based on an idea by Joseph Yao.
2N/A */
2N/A
2N/A#define MAX 10
2N/A#define MEM_ALLOC_FAIL 1
2N/A#define STACK_UNDERFLOW 2
2N/A
2N/Atypedef struct {
2N/A long top;
2N/A int stacksize;
2N/A long *stack;
2N/A
2N/A}STACK;
2N/A
2N/Astatic jmp_buf env;
2N/A
2N/Astatic long
2N/Atops(STACK *st)
2N/A{
2N/A
2N/A if (st->top < 0) {
2N/A longjmp(env, STACK_UNDERFLOW);
2N/A }
2N/A return (st->stack[st->top]);
2N/A}
2N/A
2N/Astatic void
2N/Apush(STACK *st, long i)
2N/A{
2N/A if (st->top >= (st->stacksize - 1)) {
2N/A st->stacksize += MAX;
2N/A if ((st->stack = (void *)realloc(st->stack,
2N/A (st->stacksize * sizeof (long)))) == NULL) {
2N/A longjmp(env, MEM_ALLOC_FAIL);
2N/A }
2N/A }
2N/A st->stack[++st->top] = (i);
2N/A}
2N/A
2N/Astatic long
2N/Apop(STACK *st)
2N/A{
2N/A if (st->top < 0) {
2N/A longjmp(env, STACK_UNDERFLOW);
2N/A }
2N/A return (st->stack[st->top--]);
2N/A}
2N/A
2N/A/* The following routine was added to make lint shut up about converting from
2N/A * a long to a char *. It is identical to the pop routine, except for the
2N/A * cast on the return statement.
2N/A */
2N/Astatic char *
2N/Apop_char_p(STACK *st)
2N/A{
2N/A if (st->top < 0) {
2N/A longjmp(env, STACK_UNDERFLOW);
2N/A }
2N/A return ((char *)(st->stack[st->top--]));
2N/A}
2N/A
2N/Astatic void
2N/Ainit_stack(STACK *st)
2N/A{
2N/A st->top = -1;
2N/A st->stacksize = MAX;
2N/A if ((st->stack = (void *)malloc(MAX * sizeof (long))) == NULL) {
2N/A longjmp(env, MEM_ALLOC_FAIL);
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Afree_stack(STACK *st)
2N/A{
2N/A free(st->stack);
2N/A}
2N/A
2N/A
2N/Achar *
2N/Atparm_p0(char *instring)
2N/A{
2N/A long p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
2N/A
2N/A return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6],
2N/A p[7], p[8]));
2N/A}
2N/A
2N/Achar *
2N/Atparm_p1(char *instring, long l1)
2N/A{
2N/A long p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
2N/A
2N/A p[0] = l1;
2N/A
2N/A return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6],
2N/A p[7], p[8]));
2N/A}
2N/A
2N/Achar *
2N/Atparm_p2(char *instring, long l1, long l2)
2N/A{
2N/A long p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
2N/A
2N/A p[0] = l1;
2N/A p[1] = l2;
2N/A
2N/A return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6],
2N/A p[7], p[8]));
2N/A}
2N/A
2N/Achar *
2N/Atparm_p3(char *instring, long l1, long l2, long l3)
2N/A{
2N/A long p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
2N/A
2N/A p[0] = l1;
2N/A p[1] = l2;
2N/A p[2] = l3;
2N/A
2N/A return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6],
2N/A p[7], p[8]));
2N/A}
2N/A
2N/Achar *
2N/Atparm_p4(char *instring, long l1, long l2, long l3, long l4)
2N/A{
2N/A long p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
2N/A
2N/A p[0] = l1;
2N/A p[1] = l2;
2N/A p[2] = l3;
2N/A p[3] = l4;
2N/A
2N/A return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6],
2N/A p[7], p[8]));
2N/A}
2N/A
2N/Achar *
2N/Atparm_p7(char *instring, long l1, long l2, long l3, long l4, long l5, long l6,
2N/A long l7)
2N/A{
2N/A long p[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
2N/A
2N/A p[0] = l1;
2N/A p[1] = l2;
2N/A p[2] = l3;
2N/A p[3] = l4;
2N/A p[4] = l5;
2N/A p[5] = l6;
2N/A p[6] = l7;
2N/A
2N/A return (tparm(instring, p[0], p[1], p[2], p[3], p[4], p[5], p[6],
2N/A p[7], p[8]));
2N/A}
2N/A
2N/A/* VARARGS */
2N/Achar *
2N/Atparm(char *instring, long fp1, long fp2, long p3, long p4,
2N/A long p5, long p6, long p7, long p8, long p9)
2N/A{
2N/A static char result[512];
2N/A static char added[100];
2N/A long vars[26];
2N/A STACK stk;
2N/A char *cp = instring;
2N/A char *outp = result;
2N/A char c;
2N/A long op;
2N/A long op2;
2N/A int sign;
2N/A int onrow = 0;
2N/A long p1 = fp1, p2 = fp2; /* copy in case < 2 actual parms */
2N/A char *xp;
2N/A char formatbuffer[100];
2N/A char *format;
2N/A int looping;
2N/A short *regs = cur_term->_regs;
2N/A int val;
2N/A
2N/A
2N/A if ((val = setjmp(env)) != 0) {
2N/A#ifdef DEBUG
2N/A switch (val) {
2N/A case MEM_ALLOC_FAIL:
2N/A fprintf(outf, "TPARM: Memory allocation"
2N/A " failure.");
2N/A break;
2N/A case STACK_UNDERFLOW:
2N/A fprintf(outf, "TPARM: Stack underflow.");
2N/A break;
2N/A }
2N/A#endif /* DEBUG */
2N/A
2N/A if (val == STACK_UNDERFLOW)
2N/A free_stack(&stk);
2N/A return (NULL);
2N/A }
2N/A
2N/A init_stack(&stk);
2N/A push(&stk, 0);
2N/A
2N/A if (instring == 0) {
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf, "TPARM: null arg\n");
2N/A#endif /* DEBUG */
2N/A free_stack(&stk);
2N/A return (NULL);
2N/A }
2N/A
2N/A added[0] = 0;
2N/A
2N/A while ((c = *cp++) != 0) {
2N/A if (c != '%') {
2N/A *outp++ = c;
2N/A continue;
2N/A }
2N/A op = tops(&stk);
2N/A switch (c = *cp++) {
2N/A /* PRINTING CASES */
2N/A case ':':
2N/A case ' ':
2N/A case '#':
2N/A case '0':
2N/A case '1':
2N/A case '2':
2N/A case '3':
2N/A case '4':
2N/A case '5':
2N/A case '6':
2N/A case '7':
2N/A case '8':
2N/A case '9':
2N/A case '.':
2N/A case 'd':
2N/A case 's':
2N/A case 'o':
2N/A case 'x':
2N/A case 'X':
2N/A format = formatbuffer;
2N/A *format++ = '%';
2N/A
2N/A /* leading ':' to allow +/- in format */
2N/A if (c == ':')
2N/A c = *cp++;
2N/A
2N/A /* take care of flags, width and precision */
2N/A looping = 1;
2N/A while (c && looping)
2N/A switch (c) {
2N/A case '-':
2N/A case '+':
2N/A case ' ':
2N/A case '#':
2N/A case '0':
2N/A case '1':
2N/A case '2':
2N/A case '3':
2N/A case '4':
2N/A case '5':
2N/A case '6':
2N/A case '7':
2N/A case '8':
2N/A case '9':
2N/A case '.':
2N/A *format++ = c;
2N/A c = *cp++;
2N/A break;
2N/A default:
2N/A looping = 0;
2N/A }
2N/A
2N/A /* add in the conversion type */
2N/A switch (c) {
2N/A case 'd':
2N/A case 's':
2N/A case 'o':
2N/A case 'x':
2N/A case 'X':
2N/A *format++ = c;
2N/A break;
2N/A default:
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf, "TPARM: invalid "
2N/A "conversion type\n");
2N/A#endif /* DEBUG */
2N/A free_stack(&stk);
2N/A return (NULL);
2N/A }
2N/A *format = '\0';
2N/A
2N/A /*
2N/A * Pass off the dirty work to sprintf.
2N/A * It's debatable whether we should just pull in
2N/A * the appropriate code here. I decided not to for
2N/A * now.
2N/A */
2N/A if (c == 's')
2N/A (void) sprintf(outp, formatbuffer,
2N/A (char *) op);
2N/A else
2N/A (void) sprintf(outp, formatbuffer, op);
2N/A /*
2N/A * Advance outp past what sprintf just did.
2N/A * sprintf returns an indication of its length on some
2N/A * systems, others the first char, and there's
2N/A * no easy way to tell which. The Sys V on
2N/A * BSD emulations are particularly confusing.
2N/A */
2N/A while (*outp)
2N/A outp++;
2N/A (void) pop(&stk);
2N/A
2N/A continue;
2N/A
2N/A case 'c':
2N/A /*
2N/A * This code is worth scratching your head at for a
2N/A * while. The idea is that various weird things can
2N/A * happen to nulls, EOT's, tabs, and newlines by the
2N/A * tty driver, arpanet, and so on, so we don't send
2N/A * them if we can help it. So we instead alter the
2N/A * place being addessed and then move the cursor
2N/A * locally using UP or RIGHT.
2N/A *
2N/A * This is a kludge, clearly. It loses if the
2N/A * parameterized string isn't addressing the cursor
2N/A * (but hopefully that is all that %c terminals do
2N/A * with parms). Also, since tab and newline happen
2N/A * to be next to each other in ASCII, if tab were
2N/A * included a loop would be needed. Finally, note
2N/A * that lots of other processing is done here, so
2N/A * this hack won't always work (e.g. the Ann Arbor
2N/A * 4080, which uses %B and then %c.)
2N/A */
2N/A switch (op) {
2N/A /*
2N/A * Null. Problem is that our
2N/A * output is, by convention, null terminated.
2N/A */
2N/A case 0:
2N/A op = 0200; /* Parity should */
2N/A /* be ignored. */
2N/A break;
2N/A /*
2N/A * Control D. Problem is that certain very
2N/A * ancient hardware hangs up on this, so the
2N/A * current(!) UNIX tty driver doesn't xmit
2N/A * control D's.
2N/A */
2N/A case _CHCTRL('d'):
2N/A /*
2N/A * Newline. Problem is that UNIX will expand
2N/A * this to CRLF.
2N/A */
2N/A case '\n':
2N/A xp = (onrow ? cursor_down :
2N/A cursor_right);
2N/A if (onrow && xp && op < lines-1 &&
2N/A cursor_up) {
2N/A op += 2;
2N/A xp = cursor_up;
2N/A }
2N/A if (xp && instring ==
2N/A cursor_address) {
2N/A (void) strcat(added, xp);
2N/A op--;
2N/A }
2N/A break;
2N/A /*
2N/A * Tab used to be in this group too,
2N/A * because UNIX might expand it to blanks.
2N/A * We now require that this tab mode be turned
2N/A * off by any program using this routine,
2N/A * or using termcap in general, since some
2N/A * terminals use tab for other stuff, like
2N/A * nondestructive space. (Filters like ul
2N/A * or vcrt will lose, since they can't stty.)
2N/A * Tab was taken out to get the Ann Arbor
2N/A * 4080 to work.
2N/A */
2N/A }
2N/A
2N/A /* LINTED */
2N/A *outp++ = (char)op;
2N/A (void) pop(&stk);
2N/A break;
2N/A
2N/A case 'l':
2N/A xp = pop_char_p(&stk);
2N/A push(&stk, strlen(xp));
2N/A break;
2N/A
2N/A case '%':
2N/A *outp++ = c;
2N/A break;
2N/A
2N/A /*
2N/A * %i: shorthand for increment first two parms.
2N/A * Useful for terminals that start numbering from
2N/A * one instead of zero(like ANSI terminals).
2N/A */
2N/A case 'i':
2N/A p1++;
2N/A p2++;
2N/A break;
2N/A
2N/A /* %pi: push the ith parameter */
2N/A case 'p':
2N/A switch (c = *cp++) {
2N/A case '1':
2N/A push(&stk, p1);
2N/A break;
2N/A case '2':
2N/A push(&stk, p2);
2N/A break;
2N/A case '3':
2N/A push(&stk, p3);
2N/A break;
2N/A case '4':
2N/A push(&stk, p4);
2N/A break;
2N/A case '5':
2N/A push(&stk, p5);
2N/A break;
2N/A case '6':
2N/A push(&stk, p6);
2N/A break;
2N/A case '7':
2N/A push(&stk, p7);
2N/A break;
2N/A case '8':
2N/A push(&stk, p8);
2N/A break;
2N/A case '9':
2N/A push(&stk, p9);
2N/A break;
2N/A default:
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf, "TPARM:"
2N/A " bad parm"
2N/A " number\n");
2N/A#endif /* DEBUG */
2N/A free_stack(&stk);
2N/A return (NULL);
2N/A }
2N/A onrow = (c == '1');
2N/A break;
2N/A
2N/A /* %Pi: pop from stack into variable i (a-z) */
2N/A case 'P':
2N/A if (*cp >= 'a' && *cp <= 'z') {
2N/A vars[*cp++ - 'a'] = pop(&stk);
2N/A } else {
2N/A if (*cp >= 'A' && *cp <= 'Z') {
2N/A regs[*cp++ - 'A'] =
2N/A /* LINTED */
2N/A (short) pop(&stk);
2N/A }
2N/A#ifdef DEBUG
2N/A else if (outf) {
2N/A fprintf(outf, "TPARM: bad"
2N/A " register name\n");
2N/A }
2N/A#endif /* DEBUG */
2N/A }
2N/A break;
2N/A
2N/A /* %gi: push variable i (a-z) */
2N/A case 'g':
2N/A if (*cp >= 'a' && *cp <= 'z') {
2N/A push(&stk, vars[*cp++ - 'a']);
2N/A } else {
2N/A if (*cp >= 'A' && *cp <= 'Z') {
2N/A push(&stk, regs[*cp++ - 'A']);
2N/A }
2N/A#ifdef DEBUG
2N/A else if (outf) {
2N/A fprintf(outf, "TPARM: bad"
2N/A " register name\n");
2N/A
2N/A }
2N/A#endif /* DEBUG */
2N/A }
2N/A break;
2N/A
2N/A /* %'c' : character constant */
2N/A case '\'':
2N/A push(&stk, *cp++);
2N/A if (*cp++ != '\'') {
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf, "TPARM: missing"
2N/A " closing quote\n");
2N/A#endif /* DEBUG */
2N/A free_stack(&stk);
2N/A return (NULL);
2N/A }
2N/A break;
2N/A
2N/A /* %{nn} : integer constant. */
2N/A case '{':
2N/A op = 0;
2N/A sign = 1;
2N/A if (*cp == '-') {
2N/A sign = -1;
2N/A cp++;
2N/A } else
2N/A if (*cp == '+')
2N/A cp++;
2N/A while ((c = *cp++) >= '0' && c <= '9') {
2N/A op = 10 * op + c - '0';
2N/A }
2N/A if (c != '}') {
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf, "TPARM: missing "
2N/A "closing brace\n");
2N/A#endif /* DEBUG */
2N/A free_stack(&stk);
2N/A return (NULL);
2N/A }
2N/A push(&stk, (sign * op));
2N/A break;
2N/A
2N/A /* binary operators */
2N/A case '+':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op + op2));
2N/A break;
2N/A case '-':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op - op2));
2N/A break;
2N/A case '*':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op * op2));
2N/A break;
2N/A case '/':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op / op2));
2N/A break;
2N/A case 'm':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op % op2));
2N/A break; /* %m: mod */
2N/A case '&':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op & op2));
2N/A break;
2N/A case '|':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op | op2));
2N/A break;
2N/A case '^':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op ^ op2));
2N/A break;
2N/A case '=':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op == op2));
2N/A break;
2N/A case '>':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op > op2));
2N/A break;
2N/A case '<':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op < op2));
2N/A break;
2N/A case 'A':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op && op2));
2N/A break; /* AND */
2N/A case 'O':
2N/A op2 = pop(&stk);
2N/A op = pop(&stk);
2N/A push(&stk, (op || op2));
2N/A break; /* OR */
2N/A
2N/A /* Unary operators. */
2N/A case '!':
2N/A push(&stk, !pop(&stk));
2N/A break;
2N/A case '~':
2N/A push(&stk, ~pop(&stk));
2N/A break;
2N/A
2N/A /* Sorry, no unary minus, because minus is binary. */
2N/A
2N/A /*
2N/A * If-then-else. Implemented by a low level hack of
2N/A * skipping forward until the match is found, counting
2N/A * nested if-then-elses.
2N/A */
2N/A case '?': /* IF - just a marker */
2N/A break;
2N/A
2N/A case 't': /* THEN - branch if false */
2N/A if (!pop(&stk))
2N/A cp = _branchto(cp, 'e');
2N/A break;
2N/A
2N/A case 'e': /* ELSE - branch to ENDIF */
2N/A cp = _branchto(cp, ';');
2N/A break;
2N/A
2N/A case ';': /* ENDIF - just a marker */
2N/A break;
2N/A
2N/A default:
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf, "TPARM: bad % "
2N/A "sequence\n");
2N/A#endif /* DEBUG */
2N/A free_stack(&stk);
2N/A return (NULL);
2N/A }
2N/A }
2N/A (void) strcpy(outp, added);
2N/A free_stack(&stk);
2N/A return (result);
2N/A}
2N/A
2N/Achar *
2N/A_branchto(register char *cp, char to)
2N/A{
2N/A register int level = 0;
2N/A register char c;
2N/A
2N/A while (c = *cp++) {
2N/A if (c == '%') {
2N/A if ((c = *cp++) == to || c == ';') {
2N/A if (level == 0) {
2N/A return (cp);
2N/A }
2N/A }
2N/A if (c == '?')
2N/A level++;
2N/A if (c == ';')
2N/A level--;
2N/A }
2N/A }
2N/A#ifdef DEBUG
2N/A if (outf)
2N/A fprintf(outf, "TPARM: no matching ENDIF");
2N/A#endif /* DEBUG */
2N/A return (NULL);
2N/A}