summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2022-01-30 15:06:22 +0100
committerBruno Haible <bruno@clisp.org>2022-01-30 16:37:34 +0100
commitb5443f091c678733b01e467632d0837458560918 (patch)
tree288f69c460347705ca71eb043258900dfba35112 /lib
parent735f319cde4ab7e98294557181d682564241d846 (diff)
downloadgnulib-b5443f091c678733b01e467632d0837458560918.tar.gz
termcap, termcap-h: New modules.
* lib/termcap.h: New file, from GNU gettext. * lib/tparm.c: New file, from GNU gettext. * lib/tputs.c: New file, from GNU gettext. * m4/termcap.m4: New file, from GNU gettext. * m4/curses.m4: New file, from GNU gettext. * modules/termcap: New file, from GNU gettext. * modules/termcap-h: New file, from GNU gettext.
Diffstat (limited to 'lib')
-rw-r--r--lib/termcap.h118
-rw-r--r--lib/tparm.c897
-rw-r--r--lib/tputs.c41
3 files changed, 1056 insertions, 0 deletions
diff --git a/lib/termcap.h b/lib/termcap.h
new file mode 100644
index 0000000000..859c8994ce
--- /dev/null
+++ b/lib/termcap.h
@@ -0,0 +1,118 @@
+/* Information about terminal capabilities.
+ Copyright (C) 2006-2022 Free Software Foundation, Inc.
+
+ This file is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This file is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2006. */
+
+#ifndef _TERMCAP_H
+#define _TERMCAP_H
+
+/* Including <curses.h> or <term.h> is dangerous, because it also declares
+ a lot of junk, such as variables PC, UP, and other. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if HAVE_TERMCAP
+
+/* Gets the capability information for terminal type TYPE.
+ BP must point to a buffer, at least 2048 bytes large.
+ Returns 1 if successful, 0 if TYPE is unknown, -1 on other error. */
+extern int tgetent (char *bp, const char *type);
+
+/* Retrieves the value of a numerical capability.
+ Returns -1 if it is not available. */
+extern int tgetnum (const char *id);
+
+/* Retrieves the value of a boolean capability.
+ Returns 1 if it available, 0 otherwise. */
+extern int tgetflag (const char *id);
+
+/* Retrieves the value of a string capability.
+ Returns NULL if it is not available.
+ Also, if AREA != NULL, stores it at *AREA and advances *AREA. */
+extern const char * tgetstr (const char *id, char **area);
+
+#endif
+
+#if HAVE_TERMINFO
+
+/* Gets the capability information for terminal type TYPE and prepares FD.
+ Returns 0 if successful, -1 upon error. If ERRP is non-NULL, also returns
+ an error indicator in *ERRP; otherwise an error is signalled. */
+extern int setupterm (const char *type, int fd, int *errp);
+
+/* Retrieves the value of a numerical capability.
+ Returns -1 if it is not available, -2 if ID is invalid. */
+extern int tigetnum (const char *id);
+
+/* Retrieves the value of a boolean capability.
+ Returns 1 if it available, 0 if not available, -1 if ID is invalid. */
+extern int tigetflag (const char *id);
+
+/* Retrieves the value of a string capability.
+ Returns NULL if it is not available, (char *)(-1) if ID is invalid. */
+extern const char * tigetstr (const char *id);
+
+#endif
+
+#if HAVE_TPARAM
+
+/* API provided by GNU termcap in <termcap.h>. */
+
+/* Instantiates a string capability with format strings.
+ BUF must be a buffer having room for BUFSIZE bytes.
+ The return value is either equal to BUF or freshly malloc()ed. */
+extern char * tparam (const char *str, void *buf, int bufsize, ...);
+
+#else
+
+/* API provided by
+ - GNU ncurses in <term.h>, <curses.h>, <ncurses.h>,
+ - OSF/1 curses in <term.h>, <curses.h>,
+ - Solaris, AIX, HP-UX, IRIX curses in <term.h>,
+ - gnulib's replacement. */
+
+/* Instantiates a string capability with format strings.
+ The return value is statically allocated and must not be freed. */
+extern char * tparm (const char *str, ...);
+
+#endif
+
+#if HAVE_TERMCAP || HAVE_TERMINFO
+
+/* Retrieves a string that causes cursor positioning to (column, row).
+ This function is necessary because the string returned by tgetstr ("cm")
+ is in a special format. */
+extern const char * tgoto (const char *cm, int column, int row);
+
+#endif
+
+/* Retrieves the value of a string capability.
+ OUTCHARFUN is called in turn for each 'char' of the result.
+ This function is necessary because string capabilities can contain
+ padding commands. */
+extern void tputs (const char *cp, int affcnt, int (*outcharfun) (int));
+
+/* The ncurses functions for color handling (see ncurses/base/lib_color.c)
+ are overkill: Most terminal emulators support only a fixed, small number
+ of colors. */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TERMCAP_H */
diff --git a/lib/tparm.c b/lib/tparm.c
new file mode 100644
index 0000000000..1c625ad5df
--- /dev/null
+++ b/lib/tparm.c
@@ -0,0 +1,897 @@
+/* Substitution of parameters in strings from terminal descriptions.
+ Copyright (C) 2006-2022 Free Software Foundation, Inc.
+
+ This file is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This file is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Originally by Ross Ridge, Public Domain, 92/02/01 07:30:36 */
+
+#include <config.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "attribute.h"
+#include "c-ctype.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 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 (int arg)
+{
+ if (pos == MAX_PUSHED)
+ return 1;
+ S[pos].type = ARG;
+ S[pos++].argnum = arg;
+ return 0;
+}
+
+static int
+pushnum (int num)
+{
+ if (pos == MAX_PUSHED)
+ return 1;
+ S[pos].type = NUM;
+ S[pos++].value = num;
+ return 0;
+}
+
+static int
+getarg (int argnum, int type, void *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 (char **str)
+{
+ if (pos-- == 0)
+ return 1;
+ if (S[pos].type != ARG)
+ return 1;
+ return getarg (S[pos].argnum, STRING, str);
+}
+
+static int
+popnum (int *num)
+{
+ if (pos-- == 0)
+ return 1;
+ switch (S[pos].type)
+ {
+ case ARG:
+ return getarg (S[pos].argnum, INTEGER, num);
+ case NUM:
+ *num = S[pos].value;
+ return 0;
+ }
+ return 1;
+}
+
+static int
+cvtchar (const char *sp, char *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;
+ }
+}
+
+/* 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 definition 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 int termcap;
+ static char OOPS[] = "OOPS";
+ static char buf[MAX_LINE];
+ const char *sp;
+ char *dp;
+ char *fmt;
+ char scan_for;
+ int scan_depth;
+ int if_depth;
+ char fmt_buf[MAX_LINE];
+ char sbuf[MAX_LINE];
+
+ va_start (tparm_args, str);
+
+ sp = str;
+ dp = buf;
+ scan_for = 0;
+ scan_depth = 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)
+ {
+ int i, j;
+ if (popnum (&j) || popnum (&i))
+ return OOPS;
+ i += j;
+ if (pushnum (i))
+ return OOPS;
+ sp++;
+ break;
+ }
+ FALLTHROUGH;
+ case 'C':
+ if (*sp == 'C')
+ {
+ int i;
+ 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;
+ {
+ int i;
+ if (getarg (termcap - 1, INTEGER, &i))
+ return OOPS;
+ if (*++sp == '\0')
+ return OOPS;
+ if ((sp[1] == 'p' || sp[1] == 'c')
+ && sp[2] != '\0' && fmt == NULL)
+ {
+ /* GNU arithmetic parameter, what they really need is
+ terminfo. */
+ int val;
+ int lc;
+ if (sp[1] == 'p'
+ && getarg (termcap - 1 + sp[2] - '@', INTEGER, &val))
+ return OOPS;
+ if (sp[1] == 'c')
+ {
+ char c;
+ lc = cvtchar (sp + 2, &c) + 2;
+ /* Mask out 8th bit so \200 can be used for \0 as per
+ GNU docs. */
+ 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... */
+ {
+ char c;
+ lc = cvtchar (sp, &c);
+ val = c + i;
+ }
+ break;
+ }
+ arg_list[termcap - 1].integer = val;
+ sp += lc;
+ break;
+ }
+ {
+ char c;
+ sp += cvtchar (sp, &c);
+ arg_list[termcap - 1].integer = c + i;
+ }
+ }
+ if (fmt == NULL)
+ break;
+ sp--;
+ FALLTHROUGH;
+ case '-':
+ if (!termcap)
+ {
+ int i, j;
+ 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 == '-'))
+ {
+ int i;
+ if (getarg (termcap - 1, INTEGER, &i))
+ return OOPS;
+ if (*++sp == '\0')
+ return OOPS;
+ {
+ char c;
+ 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;
+ }
+ {
+ char conv_char = fmt[strlen (fmt) - 1];
+ if (conv_char == 's')
+ {
+ char *s;
+ if (popstring (&s))
+ return OOPS;
+ sprintf (sbuf, fmt, s);
+ }
+ else
+ {
+ int i;
+ 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':
+ {
+ int i;
+ 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':
+ {
+ int 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':
+ {
+ int i;
+ if (!termcap || getarg (1, INTEGER, &i))
+ return OOPS;
+ }
+ arg_list[0].integer ^= 0140;
+ arg_list[1].integer ^= 0140;
+ sp++;
+ break;
+ case '>':
+ if (!termcap)
+ {
+ int i, j;
+ if (popnum (&j) || popnum (&i))
+ return OOPS;
+ i = (i > j);
+ if (pushnum (i))
+ return OOPS;
+ sp++;
+ break;
+ }
+ {
+ int i;
+ if (getarg (termcap-1, INTEGER, &i))
+ return OOPS;
+ {
+ char c;
+ 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':
+ {
+ int i;
+ if (!termcap || getarg (termcap-1, INTEGER, &i))
+ return OOPS;
+ arg_list[termcap-1].integer = 16 * (i / 10) + i % 10;
+ }
+ sp++;
+ break;
+ case 'D':
+ {
+ int i;
+ 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;
+ {
+ int i = (*sp == '0' ? 9 : *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;
+ {
+ int 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;
+ {
+ int 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;
+ {
+ char c;
+ sp += cvtchar (sp, &c);
+ if (pushnum (c) || *sp++ != '\'')
+ return OOPS;
+ }
+ termcap = 0;
+ break;
+ case '{':
+ if (termcap > 1)
+ return OOPS;
+ {
+ int i;
+ i = 0;
+ sp++;
+ while (c_isdigit (*sp))
+ i = 10 * i + *sp++ - '0';
+ if (*sp++ != '}' || pushnum (i))
+ return OOPS;
+ }
+ termcap = 0;
+ break;
+ case 'l':
+ {
+ int i;
+ char *s;
+ if (termcap || popstring (&s))
+ return OOPS;
+ i = strlen (s);
+ if (pushnum (i))
+ return OOPS;
+ }
+ sp++;
+ break;
+ case '*':
+ {
+ int i, j;
+ if (termcap || popnum (&j) || popnum (&i))
+ return OOPS;
+ i *= j;
+ if (pushnum (i))
+ return OOPS;
+ }
+ sp++;
+ break;
+ case '/':
+ {
+ int i, j;
+ if (termcap || popnum (&j) || popnum (&i))
+ return OOPS;
+ i /= j;
+ if (pushnum (i))
+ return OOPS;
+ }
+ sp++;
+ break;
+ case 'm':
+ if (termcap)
+ {
+ int i;
+ if (getarg (1, INTEGER, &i))
+ return OOPS;
+ arg_list[0].integer ^= 0177;
+ arg_list[1].integer ^= 0177;
+ sp++;
+ break;
+ }
+ {
+ int i, j;
+ if (popnum (&j) || popnum (&i))
+ return OOPS;
+ i %= j;
+ if (pushnum (i))
+ return OOPS;
+ }
+ sp++;
+ break;
+ case '&':
+ {
+ int i, j;
+ if (popnum (&j) || popnum (&i))
+ return OOPS;
+ i &= j;
+ if (pushnum (i))
+ return OOPS;
+ }
+ sp++;
+ break;
+ case '|':
+ {
+ int i, j;
+ if (popnum (&j) || popnum (&i))
+ return OOPS;
+ i |= j;
+ if (pushnum (i))
+ return OOPS;
+ }
+ sp++;
+ break;
+ case '^':
+ {
+ int i, j;
+ if (popnum (&j) || popnum (&i))
+ return OOPS;
+ i ^= j;
+ if (pushnum (i))
+ return OOPS;
+ }
+ sp++;
+ break;
+ case '=':
+ {
+ int i, j;
+ if (popnum (&j) || popnum (&i))
+ return OOPS;
+ i = (i == j);
+ if (pushnum (i))
+ return OOPS;
+ }
+ sp++;
+ break;
+ case '<':
+ {
+ int i, j;
+ if (popnum (&j) || popnum (&i))
+ return OOPS;
+ i = (i < j);
+ if (pushnum (i))
+ return OOPS;
+ }
+ sp++;
+ break;
+ case 'A':
+ {
+ int i, j;
+ if (popnum (&j) || popnum (&i))
+ return OOPS;
+ i = (i && j);
+ if (pushnum (i))
+ return OOPS;
+ }
+ sp++;
+ break;
+ case 'O':
+ {
+ int i, j;
+ if (popnum (&j) || popnum (&i))
+ return OOPS;
+ i = (i || j);
+ if (pushnum (i))
+ return OOPS;
+ }
+ sp++;
+ break;
+ case '!':
+ {
+ int i;
+ if (popnum (&i))
+ return OOPS;
+ i = !i;
+ if (pushnum (i))
+ return OOPS;
+ }
+ sp++;
+ break;
+ case '~':
+ {
+ int i;
+ 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':
+ {
+ int i;
+ 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;
+}
diff --git a/lib/tputs.c b/lib/tputs.c
new file mode 100644
index 0000000000..e4d6854bf1
--- /dev/null
+++ b/lib/tputs.c
@@ -0,0 +1,41 @@
+/* Output of string from terminal descriptions.
+ Copyright (C) 2006-2022 Free Software Foundation, Inc.
+
+ This file is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This file is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification in termcap.h or terminfo.h. */
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void tputs (const char *cp, int affcnt, int (*outcharfun) (int));
+
+#include "c-ctype.h"
+
+void
+tputs (const char *cp, int affcnt, int (*outcharfun) (int))
+{
+ while (c_isdigit (*cp))
+ cp++;
+ if (*cp == '.')
+ {
+ cp++;
+ while (c_isdigit (*cp))
+ cp++;
+ }
+ for (; *cp != '\0'; cp++)
+ outcharfun (*cp);
+}