diff options
| author | Andrey A. Chernov <ache@FreeBSD.org> | 1994-12-04 02:41:41 +0000 | 
|---|---|---|
| committer | Andrey A. Chernov <ache@FreeBSD.org> | 1994-12-04 02:41:41 +0000 | 
| commit | fc98213f95f58cb136eac6f1d46e4ab8fc63081a (patch) | |
| tree | af6b69f6fad639697d85c70fe0583e1e7de8fd08 /lib/libtermcap | |
| parent | cdbbd9cfaf692faf43b9e5e11cbc0a3235e9f2d6 (diff) | |
Notes
Diffstat (limited to 'lib/libtermcap')
| -rw-r--r-- | lib/libtermcap/Makefile | 10 | ||||
| -rw-r--r-- | lib/libtermcap/termcap.3 | 22 | ||||
| -rw-r--r-- | lib/libtermcap/termcap.h | 51 | ||||
| -rw-r--r-- | lib/libtermcap/tparm.c | 752 | 
4 files changed, 826 insertions, 9 deletions
diff --git a/lib/libtermcap/Makefile b/lib/libtermcap/Makefile index fbfa7f5c008c..cd0ba3840274 100644 --- a/lib/libtermcap/Makefile +++ b/lib/libtermcap/Makefile @@ -2,11 +2,12 @@  LIB=	termcap  CFLAGS+=-DCM_N -DCM_GT -DCM_B -DCM_D -SRCS=	termcap.c tgoto.c tputs.c +SRCS=   termcap.c tgoto.c tputs.c tparm.c  MAN3=	termcap.3  MLINKS=	termcap.3 tgetent.3 termcap.3 tgetflag.3 termcap.3 tgetnum.3 \ -	termcap.3 tgetstr.3 termcap.3 tgoto.3 termcap.3 tputs.3 +	termcap.3 tgetstr.3 termcap.3 tgoto.3 termcap.3 tputs.3 \ +	termcap.3 tparm.3  LINKS=	${LIBDIR}/libtermcap.a ${LIBDIR}/libtermlib.a  .if !defined(NOPIC)  LINKS+= ${SHLIBDIR}/libtermcap.so.${SHLIB_MAJOR}.${SHLIB_MINOR} \ @@ -16,4 +17,9 @@ LINKS+= ${SHLIBDIR}/libtermcap.so.${SHLIB_MAJOR}.${SHLIB_MINOR} \  LINKS+=	${LIBDIR}/libtermcap_p.a ${LIBDIR}/libtermlib_p.a  .endif +beforeinstall: +	-cd ${.CURDIR}; cmp -s termcap.h ${DESTDIR}/usr/include/termcap.h || \ +	    install -c -o ${BINOWN} -g ${BINGRP} -m 444 termcap.h \ +	    ${DESTDIR}/usr/include +  .include <bsd.lib.mk> diff --git a/lib/libtermcap/termcap.3 b/lib/libtermcap/termcap.3 index 08173cda342b..29d271951631 100644 --- a/lib/libtermcap/termcap.3 +++ b/lib/libtermcap/termcap.3 @@ -40,21 +40,29 @@  .Nm tgetflag ,  .Nm tgetstr ,  .Nm tgoto , -.Nm tputs +.Nm tputs , +.Nm tparm  .Nd terminal independent operation routines  .Sh SYNOPSIS +.Fd #include <termcap.h>  .Vt char PC;  .Vt char *BC;  .Vt char *UP;  .Vt short ospeed; -.Fn tgetent "char *bp" "char *name" -.Fn tgetnum "char *id" -.Fn tgetflag "char *id" +.Ft int +.Fn tgetent "char *bp" "const char *name" +.Ft int +.Fn tgetnum "const char *id" +.Ft int +.Fn tgetflag "const char *id"  .Ft char * -.Fn tgetstr "char *id" "char **area" +.Fn tgetstr "const char *id" "char **area"  .Ft char * -.Fn tgoto "char *cm" destcol destline -.Fn tputs "register char *cp" "int affcnt" "int (*outc)()" +.Fn tgoto "const char *cm" "int destcol" "int destline" +.Ft int +.Fn tputs "const char *cp" "int affcnt" "int (*outc)(int)" +.Ft char * +.Fn tparm "const char *cp" "..."  .Sh DESCRIPTION  These functions extract and use capabilities from a terminal capability data  base, usually diff --git a/lib/libtermcap/termcap.h b/lib/libtermcap/termcap.h new file mode 100644 index 000000000000..54848cb97236 --- /dev/null +++ b/lib/libtermcap/termcap.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 1994 by Andrew A. Chernov, Moscow, Russia. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id */ + +#ifndef _TERMCAP_H_ +#define _TERMCAP_H_ + +#include <sys/cdefs.h> + +__BEGIN_DECLS + +extern char PC, *UP, *BC; +extern short ospeed; + +extern int tgetent __P((char *, const char *)); +extern int tgetflag __P((const char *)); +extern int tgetnum __P((const char *)); +extern char *tgetstr __P((const char *, char **)); + +extern int tputs __P((const char *, int, int (*)(int))); + +extern char *tgoto __P((const char *, int, int)); +extern char *tparm __P((const char *, ...)); + +__END_DECLS + +#endif /* _TERMCAP_H_ */ diff --git a/lib/libtermcap/tparm.c b/lib/libtermcap/tparm.c new file mode 100644 index 000000000000..96cbe3d19c7a --- /dev/null +++ b/lib/libtermcap/tparm.c @@ -0,0 +1,752 @@ +/* + * tparm.c + * + * By Ross Ridge + * Public Domain + * 92/02/01 07:30:36 + * + */ + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#ifdef USE_SCCS_IDS +static const char SCCSid[] = "@(#) mytinfo tparm.c 3.2 92/02/01 public domain, By Ross Ridge"; +#endif + +#ifndef MAX_PUSHED +#define MAX_PUSHED	32 +#endif + +#define ARG	1 +#define NUM	2 + +#define INTEGER	1 +#define STRING	2 + +#define MAX_LINE 640 + +typedef void* anyptr; + +typedef struct stack_str { +	int	type; +	int	argnum; +	int	value; +} stack; + +static stack S[MAX_PUSHED]; +static stack vars['z'-'a'+1]; +static int pos = 0; + +static struct arg_str { +	int type; +	int	integer; +	char	*string; +} arg_list[10]; + +static int argcnt; + +static va_list tparm_args; + +static int +pusharg(arg) +int arg; { +	if (pos == MAX_PUSHED) +		return 1; +	S[pos].type = ARG; +	S[pos++].argnum = arg; +	return 0; +} + +static int +pushnum(num) +int num; { +	if (pos == MAX_PUSHED) +		return 1; +	S[pos].type = NUM; +	S[pos++].value = num; +	return 0; +} + +/* VARARGS2 */ +static int +getarg(argnum, type, p) +int argnum, type; +anyptr p; { +	while (argcnt < argnum) { +		arg_list[argcnt].type = INTEGER; +		arg_list[argcnt++].integer = (int) va_arg(tparm_args, int); +	} +	if (argcnt > argnum) { +		if (arg_list[argnum].type != type) +			return 1; +		else if (type == STRING) +			*(char **)p = arg_list[argnum].string; +		else  +			*(int *)p = arg_list[argnum].integer; +	} else { +		arg_list[argcnt].type = type; +		if (type == STRING) +			*(char **)p = arg_list[argcnt++].string +				= (char *) va_arg(tparm_args, char *); +		else +			*(int *)p = arg_list[argcnt++].integer = (int) va_arg(tparm_args, int); +	} +	return 0; +} + + +static int +popstring(str) +char **str; { +	if (pos-- == 0) +		return 1; +	if (S[pos].type != ARG) +		return 1; +	return(getarg(S[pos].argnum, STRING, (anyptr) str)); +} + +static int +popnum(num) +int *num; { +	if (pos-- == 0) +		return 1; +	switch (S[pos].type) { +	case ARG: +		return (getarg(S[pos].argnum, INTEGER, (anyptr) num)); +	case NUM: +		*num = S[pos].value; +		return 0; +	} +	return 1; +} + +static int +cvtchar(sp, c) +register char *sp, *c; { +	switch(*sp) { +	case '\\': +		switch(*++sp) { +		case '\'': +		case '$': +		case '\\': +		case '%': +			*c = *sp; +			return 2; +		case '\0': +			*c = '\\'; +			return 1; +		case '0': +			if (sp[1] == '0' && sp[2] == '0') { +				*c = '\0'; +				return 4; +			} +			*c = '\200'; /* '\0' ???? */ +			return 2; +		default: +			*c = *sp; +			return 2; +		} +	default: +		*c = *sp; +		return 1; +	} +} + +static int termcap; + +/* sigh... this has got to be the ugliest code I've ever written. +   Trying to handle everything has its cost, I guess. + +   It actually isn't to hard to figure out if a given % code is supposed +   to be interpeted with its termcap or terminfo meaning since almost +   all terminfo codes are invalid unless something has been pushed on +   the stack and termcap strings will never push things on the stack +   (%p isn't used by termcap). So where we have a choice we make the +   decision by wether or not somthing has been pushed on the stack. +   The static variable termcap keeps track of this; it starts out set +   to 1 and is incremented as each argument processed by a termcap % code, +   however if something is pushed on the stack it's set to 0 and the +   rest of the % codes are interpeted as terminfo % codes. Another way +   of putting it is that if termcap equals one we haven't decided either +   way yet, if it equals zero we're looking for terminfo codes, and if +   its greater than 1 we're looking for termcap codes. + +   Terminfo % codes: + +	%%	output a '%' +	%[[:][-+# ][width][.precision]][doxXs] +		output pop according to the printf format +	%c	output pop as a char +	%'c'	push character constant c. +	%{n}	push decimal constant n. +	%p[1-9] push paramter [1-9] +	%g[a-z] push variable [a-z] +	%P[a-z] put pop in variable [a-z] +	%l	push the length of pop (a string) +	%+	add pop to pop and push the result +	%-	subtract pop from pop and push the result +	%*	multiply pop and pop and push the result +	%&	bitwise and pop and pop and push the result +	%|	bitwise or pop and pop and push the result +	%^	bitwise xor pop and pop and push the result +	%~	push the bitwise not of pop +	%=	compare if pop and pop are equal and push the result +	%>	compare if pop is less than pop and push the result +	%<	compare if pop is greater than pop and push the result +	%A	logical and pop and pop and push the result +	%O	logical or pop and pop and push the result +	%!	push the logical not of pop +	%? condition %t if_true [%e if_false] %; +		if condtion evaulates as true then evaluate if_true, +		else evaluate if_false. elseif's can be done: +%? cond %t true [%e cond2 %t true2] ... [%e condN %t trueN] [%e false] %; +	%i	add one to parameters 1 and 2. (ANSI) + +  Termcap Codes: + +	%%	output a % +	%.	output parameter as a character +	%d	output parameter as a decimal number +	%2	output parameter in printf format %02d +	%3	output parameter in printf format %03d +	%+x	add the character x to parameter and output it as a character +(UW)	%-x	subtract parameter FROM the character x and output it as a char +(UW)	%ax	add the character x to parameter +(GNU)	%a[+*-/=][cp]x +		GNU arithmetic.  +(UW)	%sx	subtract parameter FROM the character x +	%>xy	if parameter > character x then add character y to parameter +	%B	convert to BCD (parameter = (parameter/10)*16 + parameter%16) +	%D	Delta Data encode (parameter = parameter - 2*(paramter%16)) +	%i	increment the first two parameters by one +	%n	xor the first two parameters by 0140 +(GNU)	%m	xor the first two parameters by 0177 +	%r	swap the first two parameters +(GNU)	%b	backup to previous parameter +(GNU)	%f	skip this parameter + +  Note the two definitions of %a, the GNU defintion is used if the characters +  after the 'a' are valid, otherwise the UW definition is used. + +  (GNU) used by GNU Emacs termcap libraries +  (UW) used by the University of Waterloo (MFCF) termcap libraries + +*/ + +char *tparm(const char *str, ...) { +	static char OOPS[] = "OOPS"; +	static char buf[MAX_LINE]; +	register const char *sp; +	register char *dp; +	register char *fmt; +	char conv_char; +	char scan_for; +	int scan_depth = 0, if_depth; +	static int i, j; +	static char *s, c; +	char fmt_buf[MAX_LINE]; +	char sbuf[MAX_LINE]; + +	va_start(tparm_args, str); + +	sp = str; +	dp = buf; +	scan_for = 0; +	if_depth = 0; +	argcnt = 0; +	pos = 0; +	termcap = 1; +	while(*sp != '\0') { +		switch(*sp) { +		case '\\': +			if (scan_for) { +				if (*++sp != '\0') +					sp++; +				break; +			} +			*dp++ = *sp++; +			if (*sp != '\0') +				*dp++ = *sp++; +			break; +		case '%': +			sp++; +			if (scan_for) { +				if (*sp == scan_for && if_depth == scan_depth) { +					if (scan_for == ';') +						if_depth--; +					scan_for = 0; +				} else if (*sp == '?') +					if_depth++; +				else if (*sp == ';') { +					if (if_depth == 0) +						return OOPS; +					else +						if_depth--; +				} +				sp++; +				break; +			} +			fmt = NULL; +			switch(*sp) { +			case '%': +				*dp++ = *sp++; +				break; +			case '+': +				if (!termcap) { +					if (popnum(&j) || popnum(&i)) +						return OOPS; +					i += j; +					if (pushnum(i)) +						return OOPS; +					sp++; +					break; +				} +				;/* FALLTHROUGH */ +			case 'C': +				if (*sp == 'C') {  +					if (getarg(termcap - 1, INTEGER, &i)) +						return OOPS; +					if (i >= 96) { +						i /= 96; +						if (i == '$') +							*dp++ = '\\'; +						*dp++ = i; +					} +				} +				fmt = "%c"; +				/* FALLTHROUGH */ +			case 'a': +				if (!termcap) +					return OOPS; +				if (getarg(termcap - 1, INTEGER, (anyptr) &i)) +					return OOPS; +				if (*++sp == '\0') +					return OOPS; +				if ((sp[1] == 'p' || sp[1] == 'c') +			            && sp[2] != '\0' && fmt == NULL) { +					/* GNU aritmitic parameter, what they +					   realy need is terminfo.	      */ +					int val, lc; +					if (sp[1] == 'p' +					    && getarg(termcap - 1 + sp[2] - '@', +						      INTEGER, (anyptr) &val)) +						return OOPS; +					if (sp[1] == 'c') { +						lc = cvtchar(sp + 2, &c) + 2; +					/* Mask out 8th bit so \200 can be +					   used for \0 as per GNU doc's    */ +						val = c & 0177; +					} else +						lc = 2; +					switch(sp[0]) { +					case '=': +						break; +					case '+': +						val = i + val; +						break; +					case '-': +						val = i - val; +						break; +					case '*': +						val = i * val; +						break; +					case '/': +						val = i / val; +						break; +					default: +					/* Not really GNU's %a after all... */ +						lc = cvtchar(sp, &c); +						val = c + i; +						break; +					} +					arg_list[termcap - 1].integer = val; +					sp += lc; +					break; +				} +				sp += cvtchar(sp, &c); +				arg_list[termcap - 1].integer = c + i; +				if (fmt == NULL) +					break; +				sp--; +				/* FALLTHROUGH */ +			case '-': +				if (!termcap) { +					if (popnum(&j) || popnum(&i)) +						return OOPS; +					i -= j; +					if (pushnum(i)) +						return OOPS; +					sp++; +					break; +				} +				fmt = "%c"; +				/* FALLTHROUGH */ +			case 's': +				if (termcap && (fmt == NULL || *sp == '-')) { +					if (getarg(termcap - 1, INTEGER, &i)) +						return OOPS; +					if (*++sp == '\0') +						return OOPS; +					sp += cvtchar(sp, &c); +					arg_list[termcap - 1].integer = c - i; +					if (fmt == NULL) +						break; +					sp--; +				} +				if (!termcap) +					return OOPS; +				;/* FALLTHROUGH */ +			case '.': +				if (termcap && fmt == NULL)  +					fmt = "%c"; +				;/* FALLTHROUGH */ +			case 'd': +				if (termcap && fmt == NULL)  +					fmt = "%d"; +				;/* FALLTHROUGH */ +			case '2': +				if (termcap && fmt == NULL) +					fmt = "%02d"; +				;/* FALLTHROUGH */ +			case '3': +				if (termcap && fmt == NULL)  +					fmt = "%03d"; +				;/* FALLTHROUGH */ +			case ':': case ' ': case '#': case 'u': +			case 'x': case 'X': case 'o': case 'c': +			case '0': case '1': case '4': case '5': +			case '6': case '7': case '8': case '9': +				if (fmt == NULL) { +					if (termcap) +						return OOPS; +					if (*sp == ':') +						sp++; +					fmt = fmt_buf; +					*fmt++ = '%'; +					while(*sp != 's' && *sp != 'x' && *sp != 'X' && *sp != 'd' && *sp != 'o' && *sp != 'c' && *sp != 'u') { +						if (*sp == '\0') +							return OOPS; +						*fmt++ = *sp++; +					} +					*fmt++ = *sp; +					*fmt = '\0'; +					fmt = fmt_buf; +				} +				conv_char = fmt[strlen(fmt) - 1]; +				if (conv_char == 's') { +					if (popstring(&s)) +						return OOPS; +					sprintf(sbuf, fmt, s); +				} else { +					if (termcap) { +						if (getarg(termcap++ - 1, +							   INTEGER, &i)) +							return OOPS; +					} else +						if (popnum(&i)) +							return OOPS; +					if (i == 0 && conv_char == 'c') +						strcpy(sbuf, "\000"); +					else +						sprintf(sbuf, fmt, i); +				} +				sp++; +				fmt = sbuf; +				while(*fmt != '\0') { +					if (*fmt == '$')  +						*dp++ = '\\'; +					*dp++ = *fmt++; +				} +				break; +			case 'r': +				if (!termcap || getarg(1, INTEGER, &i)) +					return OOPS; +				arg_list[1].integer = arg_list[0].integer; +				arg_list[0].integer = i; +				sp++; +				break; +			case 'i': +				if (getarg(1, INTEGER, &i) +				    || arg_list[0].type != INTEGER) +					return OOPS; +				arg_list[1].integer++; +				arg_list[0].integer++; +				sp++; +				break; +			case 'n': +				if (!termcap || getarg(1, INTEGER, &i)) +					return OOPS; +				arg_list[0].integer ^= 0140; +				arg_list[1].integer ^= 0140; +				sp++; +				break; +			case '>': +				if (!termcap) { +					if (popnum(&j) || popnum(&i)) +						return OOPS; +					i = (i > j); +					if (pushnum(i)) +						return OOPS; +					sp++; +					break; +				} +				if (getarg(termcap-1, INTEGER, &i)) +					return OOPS; +				sp += cvtchar(sp, &c); +				if (i > c) { +					sp += cvtchar(sp, &c); +					arg_list[termcap-1].integer += c; +				} else +					sp += cvtchar(sp, &c); +				sp++; +				break; +			case 'B': +				if (!termcap || getarg(termcap-1, INTEGER, &i)) +					return OOPS; +				arg_list[termcap-1].integer = 16*(i/10)+i%10; +				sp++; +				break; +			case 'D': +				if (!termcap || getarg(termcap-1, INTEGER, &i)) +					return OOPS; +				arg_list[termcap-1].integer = i - 2 * (i % 16); +				sp++; +				break; +			case 'p': +				if (termcap > 1) +					return OOPS; +				if (*++sp == '\0') +					return OOPS; +				if (*sp == '0') +					i = 9; +				else +					i = *sp - '1'; +				if (i < 0 || i > 9) +					return OOPS; +				if (pusharg(i)) +					return OOPS; +				termcap = 0; +				sp++; +				break; +			case 'P': +				if (termcap || *++sp == '\0') +					return OOPS; +				i = *sp++ - 'a'; +				if (i < 0 || i > 25) +					return OOPS; +				if (pos-- == 0) +					return OOPS; +				switch(vars[i].type = S[pos].type) { +				case ARG: +					vars[i].argnum = S[pos].argnum; +					break; +				case NUM: +					vars[i].value = S[pos].value; +					break; +				} +				break; +			case 'g': +				if (termcap || *++sp == '\0') +					return OOPS; +				i = *sp++ - 'a'; +				if (i < 0 || i > 25) +					return OOPS; +				switch(vars[i].type) { +				case ARG: +					if (pusharg(vars[i].argnum)) +						return OOPS; +					break; +				case NUM: +					if (pushnum(vars[i].value)) +						return OOPS; +					break; +				} +				break; +			case '\'': +				if (termcap > 1) +					return OOPS; +				if (*++sp == '\0') +					return OOPS; +				sp += cvtchar(sp, &c); +				if (pushnum(c) || *sp++ != '\'') +					return OOPS; +				termcap = 0; +				break; +			case '{': +				if (termcap > 1) +					return OOPS; +				i = 0; +				sp++; +				while(isdigit(*sp)) +					i = 10 * i + *sp++ - '0'; +				if (*sp++ != '}' || pushnum(i)) +					return OOPS; +				termcap = 0; +				break; +			case 'l': +				if (termcap || popstring(&s)) +					return OOPS; +				i = strlen(s); +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case '*': +				if (termcap || popnum(&j) || popnum(&i)) +					return OOPS; +				i *= j; +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case '/': +				if (termcap || popnum(&j) || popnum(&i)) +					return OOPS; +				i /= j; +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case 'm': +				if (termcap) { +					if (getarg(1, INTEGER, &i)) +						return OOPS; +					arg_list[0].integer ^= 0177; +					arg_list[1].integer ^= 0177; +					sp++; +					break; +				} +				if (popnum(&j) || popnum(&i)) +					return OOPS; +				i %= j; +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case '&': +				if (popnum(&j) || popnum(&i)) +					return OOPS; +				i &= j; +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case '|': +				if (popnum(&j) || popnum(&i)) +					return OOPS; +				i |= j; +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case '^': +				if (popnum(&j) || popnum(&i)) +					return OOPS; +				i ^= j; +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case '=': +				if (popnum(&j) || popnum(&i)) +					return OOPS; +				i = (i == j); +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case '<': +				if (popnum(&j) || popnum(&i)) +					return OOPS; +				i = (i < j); +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case 'A': +				if (popnum(&j) || popnum(&i)) +					return OOPS; +				i = (i && j); +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case 'O': +				if (popnum(&j) || popnum(&i)) +					return OOPS; +				i = (i || j); +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case '!': +				if (popnum(&i)) +					return OOPS; +				i = !i; +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case '~': +				if (popnum(&i)) +					return OOPS; +				i = ~i; +				if (pushnum(i)) +					return OOPS; +				sp++; +				break; +			case '?': +				if (termcap > 1) +					return OOPS; +				termcap = 0; +				if_depth++; +				sp++; +				break; +			case 't': +				if (popnum(&i) || if_depth == 0) +					return OOPS; +				if (!i) { +					scan_for = 'e'; +					scan_depth = if_depth; +				} +				sp++; +				break; +			case 'e': +				if (if_depth == 0) +					return OOPS; +				scan_for = ';'; +				scan_depth = if_depth; +				sp++; +				break; +			case ';': +				if (if_depth-- == 0) +					return OOPS; +				sp++; +				break; +			case 'b': +				if (--termcap < 1) +					return OOPS; +				sp++; +				break; +			case 'f': +				if (!termcap++) +					return OOPS; +				sp++; +				break; +			} +			break; +		default: +			if (scan_for) +				sp++; +			else +				*dp++ = *sp++; +			break; +		} +	} +	va_end(tparm_args); +	*dp = '\0'; +	return buf; +}  | 
