]> Savannah Git Hosting - gnulib.git/commitdiff
termcap, termcap-h: New modules.
authorBruno Haible <bruno@clisp.org>
Sun, 30 Jan 2022 14:06:22 +0000 (15:06 +0100)
committerBruno Haible <bruno@clisp.org>
Sun, 30 Jan 2022 15:37:34 +0000 (16:37 +0100)
* 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
lib/termcap.h [new file with mode: 0644]
lib/tparm.c [new file with mode: 0644]
lib/tputs.c [new file with mode: 0644]
m4/curses.m4 [new file with mode: 0644]
m4/termcap.m4 [new file with mode: 0644]
modules/termcap [new file with mode: 0644]
modules/termcap-h [new file with mode: 0644]

index 11ab41d52bcc93031392562971467f70e478b77f..9834bf973a98b674f4b82155db0090e08bdf00e4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2022-01-30  Bruno Haible  <bruno@clisp.org>
+
+       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  <bruno@clisp.org>
 
        doc: Clarify MSVC support.
diff --git a/lib/termcap.h b/lib/termcap.h
new file mode 100644 (file)
index 0000000..859c899
--- /dev/null
@@ -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 (file)
index 0000000..1c625ad
--- /dev/null
@@ -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 (file)
index 0000000..e4d6854
--- /dev/null
@@ -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);
+}
diff --git a/m4/curses.m4 b/m4/curses.m4
new file mode 100644 (file)
index 0000000..0a508c0
--- /dev/null
@@ -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 (file)
index 0000000..4817b53
--- /dev/null
@@ -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 (file)
index 0000000..647cde8
--- /dev/null
@@ -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 (file)
index 0000000..ff5846b
--- /dev/null
@@ -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