>From b5443f091c678733b01e467632d0837458560918 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sun, 30 Jan 2022 15:06:22 +0100 Subject: [PATCH 1/2] 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. --- ChangeLog | 11 + lib/termcap.h | 118 ++++++ lib/tparm.c | 897 ++++++++++++++++++++++++++++++++++++++++++++++ lib/tputs.c | 41 +++ m4/curses.m4 | 16 + m4/termcap.m4 | 222 ++++++++++++ modules/termcap | 29 ++ modules/termcap-h | 21 ++ 8 files changed, 1355 insertions(+) create mode 100644 lib/termcap.h create mode 100644 lib/tparm.c create mode 100644 lib/tputs.c create mode 100644 m4/curses.m4 create mode 100644 m4/termcap.m4 create mode 100644 modules/termcap create mode 100644 modules/termcap-h diff --git a/ChangeLog b/ChangeLog index 11ab41d52b..9834bf973a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2022-01-30 Bruno Haible + + 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. + 2022-01-29 Bruno Haible doc: Clarify MSVC support. 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 . */ + +/* Written by Bruno Haible , 2006. */ + +#ifndef _TERMCAP_H +#define _TERMCAP_H + +/* Including or 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 . */ + +/* 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 , , , + - OSF/1 curses in , , + - Solaris, AIX, HP-UX, IRIX curses in , + - 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 . */ + +/* Originally by Ross Ridge, Public Domain, 92/02/01 07:30:36 */ + +#include + +#include +#include +#include + +#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 . */ + +#include + +/* 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); +} diff --git a/m4/curses.m4 b/m4/curses.m4 new file mode 100644 index 0000000000..0a508c0f12 --- /dev/null +++ b/m4/curses.m4 @@ -0,0 +1,16 @@ +# curses.m4 serial 1 +dnl Copyright (C) 2008-2022 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +# Sets gl_curses_allowed to yes or no. +AC_DEFUN([gl_CURSES], +[ + AC_MSG_CHECKING([whether curses libraries may be used]) + AC_ARG_ENABLE(curses, + [ --disable-curses do not use libncurses, libtermcap even if they exist], + [gl_curses_allowed="$enableval"], + [gl_curses_allowed=yes]) + AC_MSG_RESULT([$gl_curses_allowed]) +]) diff --git a/m4/termcap.m4 b/m4/termcap.m4 new file mode 100644 index 0000000000..4817b5343c --- /dev/null +++ b/m4/termcap.m4 @@ -0,0 +1,222 @@ +# termcap.m4 serial 8 +dnl Copyright (C) 2000-2022 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([gl_TERMCAP], +[ + AC_REQUIRE([gl_TERMCAP_BODY]) + if test $gl_cv_termcap_tparam = no && test $gl_cv_termcap_tparm = no; then + AC_LIBOBJ([tparm]) + fi + case "$gl_cv_termcap" in + no*) + AC_LIBOBJ([tputs]) + ;; + esac +]) + +AC_DEFUN([gl_TERMCAP_BODY], +[ + dnl Some systems have tgetent(), tgetnum(), tgetstr(), tgetflag(), tparm(), + dnl tputs(), tgoto() in libc, some have it in libtermcap, some have it in + dnl libncurses. + dnl When both libtermcap and libncurses exist, we prefer the latter, + dnl because libtermcap is unsecure by design and obsolete since 1994. + dnl libcurses is useless: all platforms which have libcurses also have + dnl libtermcap, and libcurses is unusable on some old Unices. + dnl Some systems have the terminfo functions setupterm(), tigetnum(), + dnl tigetstr(), tigetflag() in the same library. + dnl Some systems, like BeOS, use GNU termcap, which has tparam() instead of + dnl tparm(). + dnl Some systems, like mingw, have nothing at all. + + dnl Some people want to avoid these libraries, in special situations such + dnl as when cross-compiling. + AC_REQUIRE([gl_CURSES]) + + dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + + if test "$gl_curses_allowed" != no; then + + dnl Search for libncurses and define LIBNCURSES, LTLIBNCURSES and INCNCURSES + dnl accordingly. + AC_LIB_LINKFLAGS_BODY([ncurses]) + + dnl Search for libtermcap and define LIBTERMCAP, LTLIBTERMCAP and INCTERMCAP + dnl accordingly. + AC_LIB_LINKFLAGS_BODY([termcap]) + + else + + LIBNCURSES= + LTLIBNCURSES= + INCNCURSES= + + LIBTERMCAP= + LTLIBTERMCAP= + INCTERMCAP= + + fi + + AC_CACHE_CHECK([where termcap library functions come from], [gl_cv_termcap], [ + gl_cv_termcap="not found, consider installing GNU ncurses" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[extern + #ifdef __cplusplus + "C" + #endif + int tgetent (char *, const char *); + ]], + [[return tgetent ((char *) 0, "xterm");]])], + [gl_cv_termcap=libc]) + if test "$gl_cv_termcap" != libc; then + gl_save_LIBS="$LIBS" + LIBS="$LIBS $LIBNCURSES" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[extern + #ifdef __cplusplus + "C" + #endif + int tgetent (char *, const char *); + ]], + [[return tgetent ((char *) 0, "xterm");]])], + [gl_cv_termcap=libncurses]) + LIBS="$gl_save_LIBS" + if test "$gl_cv_termcap" != libncurses; then + gl_save_LIBS="$LIBS" + LIBS="$LIBS $LIBTERMCAP" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[extern + #ifdef __cplusplus + "C" + #endif + int tgetent (char *, const char *); + ]], + [[return tgetent ((char *) 0, "xterm");]])], + [gl_cv_termcap=libtermcap]) + LIBS="$gl_save_LIBS" + fi + fi + ]) + case "$gl_cv_termcap" in + libc) + LIBTERMCAP= + LTLIBTERMCAP= + INCTERMCAP= + ;; + libncurses) + LIBTERMCAP="$LIBNCURSES" + LTLIBTERMCAP="$LTLIBNCURSES" + INCTERMCAP="$INCNCURSES" + ;; + libtermcap) + ;; + esac + case "$gl_cv_termcap" in + libc | libncurses | libtermcap) + AC_DEFINE([HAVE_TERMCAP], 1, + [Define if tgetent(), tgetnum(), tgetstr(), tgetflag() + are among the termcap library functions.]) + ;; + esac + AC_SUBST([LIBTERMCAP]) + AC_SUBST([LTLIBTERMCAP]) + AC_SUBST([INCTERMCAP]) + + dnl Test whether the terminfo functions are available from the same library. + AC_CACHE_CHECK([for terminfo functions], [gl_cv_func_terminfo], [ + gl_save_LIBS="$LIBS" + LIBS="$LIBS $LIBTERMCAP" + gl_save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $INCTERMCAP" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[extern + #ifdef __cplusplus + "C" + #endif + int setupterm (const char *, int, int *); + extern + #ifdef __cplusplus + "C" + #endif + int tigetnum (const char *); + extern + #ifdef __cplusplus + "C" + #endif + int tigetflag (const char *); + extern + #ifdef __cplusplus + "C" + #endif + const char * tigetstr (const char *); + ]], + [[return setupterm ("xterm", 0, (int *)0) + + tigetnum ("colors") + tigetflag ("hc") + * tigetstr ("oc");]])], + [gl_cv_func_terminfo=yes], [gl_cv_func_terminfo=no]) + CPPFLAGS="$gl_save_CPPFLAGS" + LIBS="$gl_save_LIBS" + ]) + if test $gl_cv_func_terminfo = yes; then + AC_DEFINE([HAVE_TERMINFO], 1, + [Define if setupterm(), tigetnum(), tigetstr(), tigetflag() + are among the termcap library functions.]) + fi + + dnl Test against the old GNU termcap, which provides a tparam() function + dnl instead of the classical tparm() function. + AC_CACHE_CHECK([for tparam], [gl_cv_termcap_tparam], [ + gl_save_LIBS="$LIBS" + LIBS="$LIBS $LIBTERMCAP" + gl_save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $INCTERMCAP" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[extern + #ifdef __cplusplus + "C" + #endif + char * tparam (const char *, void *, int, ...); + char buf; + ]], + [[return tparam ("\033\133%dm", &buf, 1, 8);]])], + [gl_cv_termcap_tparam=yes], [gl_cv_termcap_tparam=no]) + CPPFLAGS="$gl_save_CPPFLAGS" + LIBS="$gl_save_LIBS" + ]) + if test $gl_cv_termcap_tparam = yes; then + AC_DEFINE([HAVE_TPARAM], 1, + [Define if tparam() is among the termcap library functions.]) + else + dnl Test whether a tparm() function is provided. It is missing e.g. + dnl in NetBSD 3.0 libtermcap. + AC_CACHE_CHECK([for tparm], [gl_cv_termcap_tparm], [ + gl_save_LIBS="$LIBS" + LIBS="$LIBS $LIBTERMCAP" + gl_save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $INCTERMCAP" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[extern + #ifdef __cplusplus + "C" + #endif + char * tparm (const char *, ...); + ]], + [[return tparm ("\033\133%dm", 8);]])], + [gl_cv_termcap_tparm=yes], [gl_cv_termcap_tparm=no]) + CPPFLAGS="$gl_save_CPPFLAGS" + LIBS="$gl_save_LIBS" + ]) + fi +]) diff --git a/modules/termcap b/modules/termcap new file mode 100644 index 0000000000..647cde8107 --- /dev/null +++ b/modules/termcap @@ -0,0 +1,29 @@ +Description: +Information about terminal capabilities. + +Files: +m4/termcap.m4 +m4/curses.m4 +lib/tparm.c +lib/tputs.c + +Depends-on: +havelib +attribute +c-ctype + +configure.ac: +gl_TERMCAP + +Makefile.am: +if GL_COND_LIBTOOL +lib_LDFLAGS += $(LTLIBTERMCAP) +endif + +Include: + +License: +LGPL + +Maintainer: +Bruno Haible diff --git a/modules/termcap-h b/modules/termcap-h new file mode 100644 index 0000000000..ff5846bc50 --- /dev/null +++ b/modules/termcap-h @@ -0,0 +1,21 @@ +Description: +Information about terminal capabilities. + +Files: +lib/termcap.h + +Depends-on: +termcap + +configure.ac: + +Makefile.am: + +Include: +"termcap.h" + +License: +LGPL + +Maintainer: +Bruno Haible -- 2.25.1