Programs that use the module 'libtextstyle' and still want to be able to
build when no libtextstyle is installed need to use
  #if HAVE_LIBTEXTSTYLE
in many places.

The module 'libtextstyle-optional' greatly reduces the need to use that
much conditional code (assuming the program's developer accepts to use
ostream_t types in unconditional code). Akim Demaille confirmed that this
module is useful, so I am adding it.


2019-03-19  Bruno Haible  <br...@clisp.org>

        libtextstyle-optional: New module.
        * lib/textstyle.in.h: New file, based on libtextstyle's textstyle.h.
        * m4/libtextstyle-optional.m4: New file, based on m4/libtextstyle.m4.
        * modules/libtextstyle-optional: New file.

        libtextstyle-optional: Add tests.
        * tests/test-libtextstyle.c: New file, based on libtextstyle's
        adhoc-tests/hello.c.
        * tests/test-libtextstyle-default.css: New file, copied from
        libtextstyle's adhoc-tests/hello-default.css.
        * modules/libtextstyle-optional-tests: New file.

>From c23dae8c0231543a241bd54f6e5af0f348f70d99 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Wed, 20 Mar 2019 00:35:11 +0100
Subject: [PATCH 1/2] libtextstyle-optional: New module.

* lib/textstyle.in.h: New file, based on libtextstyle's textstyle.h.
* m4/libtextstyle-optional.m4: New file, based on m4/libtextstyle.m4.
* modules/libtextstyle-optional: New file.
---
 ChangeLog                     |   7 +
 lib/textstyle.in.h            | 377 ++++++++++++++++++++++++++++++++++++++++++
 m4/libtextstyle-optional.m4   |  30 ++++
 modules/libtextstyle-optional |  45 +++++
 4 files changed, 459 insertions(+)
 create mode 100644 lib/textstyle.in.h
 create mode 100644 m4/libtextstyle-optional.m4
 create mode 100644 modules/libtextstyle-optional

diff --git a/ChangeLog b/ChangeLog
index 8a4c5f3..af9d7f1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2019-03-19  Bruno Haible  <br...@clisp.org>
 
+	libtextstyle-optional: New module.
+	* lib/textstyle.in.h: New file, based on libtextstyle's textstyle.h.
+	* m4/libtextstyle-optional.m4: New file, based on m4/libtextstyle.m4.
+	* modules/libtextstyle-optional: New file.
+
+2019-03-19  Bruno Haible  <br...@clisp.org>
+
 	c-stack: Make signal handlers more reliable.
 	* lib/c-stack.c (progname): New variable.
 	(die): Use it.
diff --git a/lib/textstyle.in.h b/lib/textstyle.in.h
new file mode 100644
index 0000000..65fa670
--- /dev/null
+++ b/lib/textstyle.in.h
@@ -0,0 +1,377 @@
+/* Dummy replacement for part of the public API of the libtextstyle library.
+   Copyright (C) 2006-2007, 2019 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <br...@clisp.org>, 2019.  */
+
+/* This file is used as replacement when libtextstyle with its include file
+   <textstyle.h> is not found.
+   It supports the essential API and implements it in a way that does not
+   provide text styling.  That is, it produces plain text output via <stdio.h>
+   FILE objects.
+   Thus, it allows a package to be build with or without a dependency to
+   libtextstyle, with very few occurrences of '#if HAVE_LIBTEXTSTYLE'.
+
+   Restriction:
+   It assumes that freopen() is not being called on stdout and stderr.  */
+
+#ifndef _TEXTSTYLE_H
+#define _TEXTSTYLE_H
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if HAVE_TCDRAIN
+# include <termios.h>
+#endif
+
+/* ----------------------------- From ostream.h ----------------------------- */
+
+/* Describes the scope of a flush operation.  */
+typedef enum
+{
+  /* Flushes buffers in this ostream_t.
+     Use this value if you want to write to the underlying ostream_t.  */
+  FLUSH_THIS_STREAM = 0,
+  /* Flushes all buffers in the current process.
+     Use this value if you want to write to the same target through a
+     different file descriptor or a FILE stream.  */
+  FLUSH_THIS_PROCESS = 1,
+  /* Flushes buffers in the current process and attempts to flush the buffers
+     in the kernel.
+     Use this value so that some other process (or the kernel itself)
+     may write to the same target.  */
+  FLUSH_ALL = 2
+} ostream_flush_scope_t;
+
+
+/* An output stream is an object to which one can feed a sequence of bytes.  */
+
+typedef FILE * ostream_t;
+
+static inline void
+ostream_write_mem (ostream_t stream, const void *data, size_t len)
+{
+  if (len > 0)
+    fwrite (data, 1, len, stream);
+}
+
+static inline void
+ostream_flush (ostream_t stream, ostream_flush_scope_t scope)
+{
+  fflush (stream);
+  if (scope == FLUSH_ALL)
+    {
+      int fd = fileno (stream);
+      if (fd >= 0)
+        {
+          /* For streams connected to a disk file:  */
+          fsync (fd);
+          #if HAVE_TCDRAIN
+          /* For streams connected to a terminal:  */
+          {
+            int retval;
+
+            do
+              retval = tcdrain (fd);
+            while (retval < 0 && errno == EINTR);
+          }
+          #endif
+        }
+    }
+}
+
+static inline void
+ostream_free (ostream_t stream)
+{
+  if (stream == stdin || stream == stderr)
+    fflush (stream);
+  else
+    fclose (stream);
+}
+
+static inline void
+ostream_write_str (ostream_t stream, const char *string)
+{
+  ostream_write_mem (stream, string, strlen (string));
+}
+
+/* ------------------------- From styled-ostream.h ------------------------- */
+
+typedef ostream_t styled_ostream_t;
+
+#define styled_ostream_write_mem ostream_write_mem
+#define styled_ostream_flush ostream_flush
+#define styled_ostream_free ostream_free
+
+static inline void
+styled_ostream_begin_use_class (styled_ostream_t stream _GL_UNUSED,
+                                const char *classname _GL_UNUSED)
+{
+}
+
+static inline void
+styled_ostream_end_use_class (styled_ostream_t stream _GL_UNUSED,
+                              const char *classname _GL_UNUSED)
+{
+}
+
+static inline void
+styled_ostream_flush_to_current_style (styled_ostream_t stream _GL_UNUSED)
+{
+}
+
+/* -------------------------- From file-ostream.h -------------------------- */
+
+typedef ostream_t file_ostream_t;
+
+#define file_ostream_write_mem ostream_write_mem
+#define file_ostream_flush ostream_flush
+#define file_ostream_free ostream_free
+
+static inline file_ostream_t
+file_ostream_create (FILE *fp)
+{
+  return fp;
+}
+
+/* --------------------------- From fd-ostream.h --------------------------- */
+
+typedef ostream_t fd_ostream_t;
+
+#define fd_ostream_write_mem ostream_write_mem
+#define fd_ostream_flush ostream_flush
+#define fd_ostream_free ostream_free
+
+static inline fd_ostream_t
+fd_ostream_create (int fd, const char *filename _GL_UNUSED,
+                   bool buffered _GL_UNUSED)
+{
+  if (fd == 1)
+    return stdout;
+  else if (fd == 2)
+    return stderr;
+  else
+    return fdopen (fd, "w");
+}
+
+/* -------------------------- From term-ostream.h -------------------------- */
+
+typedef int term_color_t;
+enum
+{
+  COLOR_DEFAULT = -1  /* unknown */
+};
+
+typedef enum
+{
+  WEIGHT_NORMAL = 0,
+  WEIGHT_BOLD,
+  WEIGHT_DEFAULT = WEIGHT_NORMAL
+} term_weight_t;
+
+typedef enum
+{
+  POSTURE_NORMAL = 0,
+  POSTURE_ITALIC, /* same as oblique */
+  POSTURE_DEFAULT = POSTURE_NORMAL
+} term_posture_t;
+
+typedef enum
+{
+  UNDERLINE_OFF = 0,
+  UNDERLINE_ON,
+  UNDERLINE_DEFAULT = UNDERLINE_OFF
+} term_underline_t;
+
+typedef ostream_t term_ostream_t;
+
+#define term_ostream_write_mem ostream_write_mem
+#define term_ostream_flush ostream_flush
+#define term_ostream_free ostream_free
+
+static inline term_color_t
+term_ostream_get_color (term_ostream_t stream _GL_UNUSED)
+{
+  return COLOR_DEFAULT;
+}
+
+static inline void
+term_ostream_set_color (term_ostream_t stream _GL_UNUSED,
+                        term_color_t color _GL_UNUSED)
+{
+}
+
+static inline term_color_t
+term_ostream_get_bgcolor (term_ostream_t stream _GL_UNUSED)
+{
+  return COLOR_DEFAULT;
+}
+
+static inline void
+term_ostream_set_bgcolor (term_ostream_t stream _GL_UNUSED,
+                          term_color_t color _GL_UNUSED)
+{
+}
+
+static inline term_weight_t
+term_ostream_get_weight (term_ostream_t stream _GL_UNUSED)
+{
+  return WEIGHT_DEFAULT;
+}
+
+static inline void
+term_ostream_set_weight (term_ostream_t stream _GL_UNUSED,
+                         term_weight_t weight _GL_UNUSED)
+{
+}
+
+static inline term_posture_t
+term_ostream_get_posture (term_ostream_t stream _GL_UNUSED)
+{
+  return POSTURE_DEFAULT;
+}
+
+static inline void
+term_ostream_set_posture (term_ostream_t stream _GL_UNUSED,
+                          term_posture_t posture _GL_UNUSED)
+{
+}
+
+static inline term_underline_t
+term_ostream_get_underline (term_ostream_t stream _GL_UNUSED)
+{
+  return UNDERLINE_DEFAULT;
+}
+
+static inline void
+term_ostream_set_underline (term_ostream_t stream _GL_UNUSED,
+                            term_underline_t underline _GL_UNUSED)
+{
+}
+
+static inline void
+term_ostream_flush_to_current_style (term_ostream_t stream)
+{
+  fflush (stream);
+}
+
+typedef enum
+{
+  TTYCTL_AUTO = 0,  /* Automatic best-possible choice.  */
+  TTYCTL_NONE,      /* No control.
+                       Result: Garbled output can occur, and the terminal can
+                       be left in any state when the program is interrupted.  */
+  TTYCTL_PARTIAL,   /* Signal handling.
+                       Result: Garbled output can occur, but the terminal will
+                       be left in the default state when the program is
+                       interrupted.  */
+  TTYCTL_FULL       /* Signal handling and disabling echo and flush-upon-signal.
+                       Result: No garbled output, and the the terminal will
+                       be left in the default state when the program is
+                       interrupted.  */
+} ttyctl_t;
+
+static inline term_ostream_t
+term_ostream_create (int fd, const char *filename,
+                     ttyctl_t tty_control _GL_UNUSED)
+{
+  return fd_ostream_create (fd, filename, true);
+}
+
+/* ----------------------- From term-styled-ostream.h ----------------------- */
+
+typedef styled_ostream_t term_styled_ostream_t;
+
+#define term_styled_ostream_write_mem ostream_write_mem
+#define term_styled_ostream_flush ostream_flush
+#define term_styled_ostream_free ostream_free
+#define term_styled_ostream_begin_use_class styled_ostream_begin_use_class
+#define term_styled_ostream_end_use_class styled_ostream_end_use_class
+#define term_styled_ostream_flush_to_current_style styled_ostream_flush_to_current_style
+
+static inline term_styled_ostream_t
+term_styled_ostream_create (int fd, const char *filename,
+                            ttyctl_t tty_control _GL_UNUSED,
+                            const char *css_filename _GL_UNUSED)
+{
+  return fd_ostream_create (fd, filename, true);
+}
+
+/* ----------------------- From html-styled-ostream.h ----------------------- */
+
+typedef styled_ostream_t html_styled_ostream_t;
+
+static inline html_styled_ostream_t
+html_styled_ostream_create (ostream_t destination, const char *css_filename)
+{
+  abort ();
+  return NULL;
+}
+
+/* ------------------------------ From color.h ------------------------------ */
+
+#define color_test_mode false
+
+enum color_option { color_no, color_tty, color_yes, color_html };
+#define color_mode color_no
+
+#define style_file_name NULL
+
+static inline bool
+handle_color_option (const char *option _GL_UNUSED)
+{
+  return false;
+}
+
+static inline void
+handle_style_option (const char *option _GL_UNUSED)
+{
+}
+
+static inline void
+print_color_test (void)
+{
+  abort ();
+}
+
+static inline void
+style_file_prepare (const char *style_file_envvar _GL_UNUSED,
+                    const char *stylesdir_envvar _GL_UNUSED,
+                    const char *stylesdir_after_install _GL_UNUSED,
+                    const char *default_style_file _GL_UNUSED)
+{
+}
+
+/* ------------------------------ From misc.h ------------------------------ */
+
+static inline styled_ostream_t
+styled_ostream_create (int fd, const char *filename,
+                       ttyctl_t tty_control _GL_UNUSED,
+                       const char *css_filename _GL_UNUSED)
+{
+  return fd_ostream_create (fd, filename, true);
+}
+
+static inline void
+libtextstyle_set_failure_exit_code (int exit_code _GL_UNUSED)
+{
+}
+
+#endif /* _TEXTSTYLE_H */
diff --git a/m4/libtextstyle-optional.m4 b/m4/libtextstyle-optional.m4
new file mode 100644
index 0000000..16c8f16
--- /dev/null
+++ b/m4/libtextstyle-optional.m4
@@ -0,0 +1,30 @@
+# libtextstyle-optional.m4 serial 1
+dnl Copyright (C) 2019 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.
+
+dnl gl_LIBTEXTSTYLE_OPTIONAL
+dnl Searches for an installed libtextstyle or uses the included source code
+dnl parts that define only part of the API and does not do any styling.
+dnl If found, it sets and AC_SUBSTs HAVE_LIBTEXTSTYLE=yes and the LIBTEXTSTYLE
+dnl and LTLIBTEXTSTYLE variables, and augments the CPPFLAGS variable, and
+dnl #defines HAVE_LIBTEXTSTYLE to 1.
+dnl Otherwise, it sets and AC_SUBSTs HAVE_LIBTEXTSTYLE=no and LIBTEXTSTYLE and
+dnl LTLIBTEXTSTYLE to empty.
+
+AC_DEFUN([gl_LIBTEXTSTYLE_OPTIONAL],
+[
+  AC_REQUIRE([gl_LIBTEXTSTYLE])
+  if test $HAVE_LIBTEXTSTYLE = yes; then
+    TEXTSTYLE_H=
+  else
+    TEXTSTYLE_H=textstyle.h
+    AC_REQUIRE([AC_C_INLINE])
+    AC_CHECK_FUNCS_ONCE([tcdrain])
+  fi
+  AC_SUBST([TEXTSTYLE_H])
+  AM_CONDITIONAL([GL_GENERATE_TEXTSTYLE_H], [test -n "$TEXTSTYLE_H"])
+])
diff --git a/modules/libtextstyle-optional b/modules/libtextstyle-optional
new file mode 100644
index 0000000..f936986
--- /dev/null
+++ b/modules/libtextstyle-optional
@@ -0,0 +1,45 @@
+Description:
+Try to use system libtextstyle for output of styled text.
+
+Files:
+lib/textstyle.in.h
+m4/libtextstyle-optional.m4
+
+Depends-on:
+libtextstyle
+stdbool
+unistd
+fsync
+
+configure.ac:
+gl_LIBTEXTSTYLE_OPTIONAL
+
+Makefile.am:
+BUILT_SOURCES += $(TEXTSTYLE_H)
+
+# We need the following in order to create a dummy placeholder for
+# <textstyle.h> when the system doesn't have one.
+if GL_GENERATE_TEXTSTYLE_H
+textstyle.h: textstyle.in.h $(top_builddir)/config.status
+	$(AM_V_GEN)rm -f $@-t $@ && \
+	{ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \
+	  cat $(srcdir)/textstyle.in.h; \
+	} > $@-t && \
+	mv $@-t $@
+else
+textstyle.h: $(top_builddir)/config.status
+	rm -f $@
+endif
+MOSTLYCLEANFILES += textstyle.h textstyle.h-t
+
+Include:
+#include <textstyle.h>
+
+Link:
+$(LTLIBTEXTSTYLE) when linking with libtool, $(LIBTEXTSTYLE) otherwise
+
+License:
+GPL
+
+Maintainer:
+Bruno Haible, Akim Demaille
-- 
2.7.4

>From e4d8618dfd0c115f8b6076411e74d2df10e383af Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Wed, 20 Mar 2019 00:37:52 +0100
Subject: [PATCH 2/2] libtextstyle-optional: Add tests.

* tests/test-libtextstyle.c: New file, based on libtextstyle's
adhoc-tests/hello.c.
* tests/test-libtextstyle-default.css: New file, copied from
libtextstyle's adhoc-tests/hello-default.css.
* modules/libtextstyle-optional-tests: New file.
---
 ChangeLog                           |   7 +++
 modules/libtextstyle-optional-tests |  14 +++++
 tests/test-libtextstyle-default.css |   7 +++
 tests/test-libtextstyle.c           | 101 ++++++++++++++++++++++++++++++++++++
 4 files changed, 129 insertions(+)
 create mode 100644 modules/libtextstyle-optional-tests
 create mode 100644 tests/test-libtextstyle-default.css
 create mode 100644 tests/test-libtextstyle.c

diff --git a/ChangeLog b/ChangeLog
index af9d7f1..7f937aa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2019-03-19  Bruno Haible  <br...@clisp.org>
 
+	libtextstyle-optional: Add tests.
+	* tests/test-libtextstyle.c: New file, based on libtextstyle's
+	adhoc-tests/hello.c.
+	* tests/test-libtextstyle-default.css: New file, copied from
+	libtextstyle's adhoc-tests/hello-default.css.
+	* modules/libtextstyle-optional-tests: New file.
+
 	libtextstyle-optional: New module.
 	* lib/textstyle.in.h: New file, based on libtextstyle's textstyle.h.
 	* m4/libtextstyle-optional.m4: New file, based on m4/libtextstyle.m4.
diff --git a/modules/libtextstyle-optional-tests b/modules/libtextstyle-optional-tests
new file mode 100644
index 0000000..2722a18
--- /dev/null
+++ b/modules/libtextstyle-optional-tests
@@ -0,0 +1,14 @@
+Files:
+tests/test-libtextstyle.c
+tests/test-libtextstyle-default.css
+
+Depends-on:
+isatty
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-libtextstyle
+check_PROGRAMS += test-libtextstyle
+test_libtextstyle_CPPFLAGS = $(AM_CPPFLAGS) -DSRCDIR=\"$(srcdir)/\"
+test_libtextstyle_LDADD = $(LDADD) @LIBTEXTSTYLE@
diff --git a/tests/test-libtextstyle-default.css b/tests/test-libtextstyle-default.css
new file mode 100644
index 0000000..7eba906
--- /dev/null
+++ b/tests/test-libtextstyle-default.css
@@ -0,0 +1,7 @@
+/* This file is in the public domain.
+
+   Styling rules for the color-hello example.  */
+
+.name      { text-decoration : underline; }
+.boy-name  { background-color : rgb(123,201,249); }
+.girl-name { background-color : rgb(250,149,158); }
diff --git a/tests/test-libtextstyle.c b/tests/test-libtextstyle.c
new file mode 100644
index 0000000..970713f
--- /dev/null
+++ b/tests/test-libtextstyle.c
@@ -0,0 +1,101 @@
+/* Ad-hoc testing program for GNU libtextstyle.
+   Copyright (C) 2018-2019 Free Software Foundation, Inc.
+   Written by Bruno Haible <br...@clisp.org>, 2018.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include <textstyle.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int
+main (int argc, char *argv[])
+{
+  const char *program_name = argv[0];
+  int i;
+
+  /* Parse the command-line arguments.  */
+  for (i = 1; i < argc; i++)
+    {
+      const char *arg = argv[i];
+      if (strncmp (arg, "--color=", 8) == 0)
+        handle_color_option (arg + 8);
+      else if (strncmp (arg, "--style=", 8) == 0)
+        handle_style_option (arg + 8);
+      else if (arg[0] == '-')
+        {
+          fprintf (stderr, "%s: invalid argument: %s\n", program_name, arg);
+          exit (1);
+        }
+      else
+        /* Handle non-option arguments here.  */
+        ;
+    }
+
+  /* Handle the --color=test special argument.  */
+  if (color_test_mode)
+    {
+      print_color_test ();
+      exit (0);
+    }
+
+  #if HAVE_LIBTEXTSTYLE
+  if (color_mode == color_yes
+      || (color_mode == color_tty && isatty (STDOUT_FILENO))
+      || color_mode == color_html)
+    {
+      /* If no style file is explicitly specified, use the default in the
+         source directory.  */
+      if (style_file_name == NULL)
+        style_file_name = SRCDIR "test-libtextstyle-default.css";
+    }
+  else
+    /* No styling.  */
+    style_file_name = NULL;
+  #endif
+
+  /* Create a terminal output stream that uses this style file.  */
+  styled_ostream_t stream =
+    (color_mode == color_html
+     ? html_styled_ostream_create (file_ostream_create (stdout),
+                                   style_file_name)
+     : styled_ostream_create (STDOUT_FILENO, "(stdout)", TTYCTL_AUTO,
+                              style_file_name));
+
+  ostream_write_str (stream, "Hello ");
+
+  /* Associate the entire full name in CSS class 'name'.  */
+  styled_ostream_begin_use_class (stream, "name");
+
+  ostream_write_str (stream, "Dr. ");
+  styled_ostream_begin_use_class (stream, "boy-name");
+  ostream_write_str (stream, "Linus");
+  styled_ostream_end_use_class (stream, "boy-name");
+  ostream_write_str (stream, " Pauling");
+
+  /* Terminate the name.  */
+  styled_ostream_end_use_class (stream, "name");
+
+  ostream_write_str (stream, "!\n");
+
+  /* Flush and close the terminal stream.  */
+  styled_ostream_free (stream);
+
+  return 0;
+}
-- 
2.7.4

Reply via email to