]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
New module 'term-ostream': Output stream for attributed text, producing ANSI
authorBruno Haible <bruno@clisp.org>
Tue, 7 Nov 2006 14:49:18 +0000 (14:49 +0000)
committerBruno Haible <bruno@clisp.org>
Tue, 23 Jun 2009 10:14:20 +0000 (12:14 +0200)
escape sequences.

gnulib-local/lib/term-ostream.oo.c [new file with mode: 0644]
gnulib-local/lib/term-ostream.oo.h [new file with mode: 0644]
gnulib-local/m4/term-ostream.m4 [new file with mode: 0644]
gnulib-local/modules/term-ostream [new file with mode: 0644]

diff --git a/gnulib-local/lib/term-ostream.oo.c b/gnulib-local/lib/term-ostream.oo.c
new file mode 100644 (file)
index 0000000..5482ce7
--- /dev/null
@@ -0,0 +1,700 @@
+/* Output stream for attributed text, producing ANSI escape sequences.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+   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 2, 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, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "term-ostream.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "exit.h"
+#include "fatal-signal.h"
+#include "full-write.h"
+#include "xalloc.h"
+#include "xsize.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* 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
+
+/* Gets the capability information for terminal type TYPE.
+   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);
+
+/* Instantiates a string capability with format strings.
+   The return value is statically allocated and must not be freed.  */
+extern char * tparm (const char *str, ...);
+
+/* 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);
+
+/* 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
+
+
+/* ANSI C and ISO C99 6.7.2.1.(4) forbid use of bit fields for types other
+   than 'int' or 'unsigned int'.
+   On the other hand, C++ forbids conversion between enum types and integer
+   types without an explicit cast.  */
+#ifdef __cplusplus
+# define BITFIELD_TYPE(orig_type,integer_type) orig_type
+#else
+# define BITFIELD_TYPE(orig_type,integer_type) integer_type
+#endif
+
+/* Attributes that can be set on a character.  */
+typedef struct
+{
+  BITFIELD_TYPE(term_color_t,     signed int)   color     : 4;
+  BITFIELD_TYPE(term_color_t,     signed int)   bgcolor   : 4;
+  BITFIELD_TYPE(term_weight_t,    unsigned int) weight    : 1;
+  BITFIELD_TYPE(term_posture_t,   unsigned int) posture   : 1;
+  BITFIELD_TYPE(term_underline_t, unsigned int) underline : 1;
+} attributes_t;
+
+struct term_ostream : struct ostream
+{
+fields:
+  /* The file descriptor used for output.  Note that ncurses termcap emulation
+     uses the baud rate information from file descriptor 1 (stdout) if it is
+     a tty, or from file descriptor 2 (stderr) otherwise.  */
+  int fd;
+  char *filename;
+  /* Values from the terminal type's terminfo/termcap description.
+     See terminfo(5) for details.  */
+                               /* terminfo  termcap */
+  int max_colors;              /* colors    Co */
+  int no_color_video;          /* ncv       NC */
+  char *set_a_foreground;      /* setaf     AF */
+  char *set_foreground;                /* setf      Sf */
+  char *set_a_background;      /* setab     AB */
+  char *set_background;                /* setb      Sb */
+  char *orig_pair;             /* op        op */
+  char *enter_bold_mode;       /* bold      md */
+  char *enter_italics_mode;    /* sitm      ZH */
+  char *exit_italics_mode;     /* ritm      ZR */
+  char *enter_underline_mode;  /* smul      us */
+  char *exit_underline_mode;   /* rmul      ue */
+  char *exit_attribute_mode;   /* sgr0      me */
+  /* Inferred values.  */
+  bool supports_foreground;
+  bool supports_background;
+  bool supports_weight;
+  bool supports_posture;
+  bool supports_underline;
+  /* Variable state.  */
+  char *buffer;                        /* Buffer for the current line.  */
+  attributes_t *attrbuffer;    /* Buffer for the simplified attributes; same
+                                  length as buffer.  */
+  size_t buflen;               /* Number of bytes stored so far.  */
+  size_t allocated;            /* Allocated size of the buffer.  */
+  attributes_t curr_attr;      /* Current attributes.  */
+  attributes_t simp_attr;      /* Simplified current attributes.  */
+};
+
+/* Simplify attributes, according to the terminal's capabilities.  */
+static attributes_t
+simplify_attributes (term_ostream_t stream, attributes_t attr)
+{
+  if ((attr.color != COLOR_DEFAULT || attr.bgcolor != COLOR_DEFAULT)
+      && stream->no_color_video > 0)
+    {
+      /* When colors and attributes can not be represented simultaneously,
+        we give preference to the color.  */
+      if (stream->no_color_video & 2)
+       /* Colors conflict with underlining.  */
+       attr.underline = UNDERLINE_OFF;
+      if (stream->no_color_video & 32)
+       /* Colors conflict with bold weight.  */
+       attr.weight = WEIGHT_NORMAL;
+    }
+  if (!stream->supports_foreground)
+    attr.color = COLOR_DEFAULT;
+  if (!stream->supports_background)
+    attr.bgcolor = COLOR_DEFAULT;
+  if (!stream->supports_weight)
+    attr.weight = WEIGHT_DEFAULT;
+  if (!stream->supports_posture)
+    attr.posture = POSTURE_DEFAULT;
+  if (!stream->supports_underline)
+    attr.underline = UNDERLINE_DEFAULT;
+  return attr;
+}
+
+/* While a line is being output, we need to be careful to restore the
+   terminal's settings in case of a fatal signal or an exit() call.  */
+
+/* File descriptor to which out_char shall output escape sequences.  */
+static int out_fd = -1;
+
+/* Filename of out_fd.  */
+static const char *out_filename;
+
+/* Output a single char to out_fd.  Ignore errors.  */
+static int
+out_char_unchecked (int c)
+{
+  char bytes[1];
+
+  bytes[0] = (char)c;
+  full_write (out_fd, bytes, 1);
+  return 0;
+}
+
+/* State that informs the exit handler what to do.  */
+static const char *restore_colors;
+static const char *restore_weight;
+static const char *restore_posture;
+static const char *restore_underline;
+
+/* The exit handler.  */
+static void
+restore (void)
+{
+  /* Only do something while some output was interrupted.  */
+  if (out_fd >= 0)
+    {
+      if (restore_colors != NULL)
+       tputs (restore_colors, 1, out_char_unchecked);
+      if (restore_weight != NULL)
+       tputs (restore_weight, 1, out_char_unchecked);
+      if (restore_posture != NULL)
+       tputs (restore_posture, 1, out_char_unchecked);
+      if (restore_underline != NULL)
+       tputs (restore_underline, 1, out_char_unchecked);
+    }
+}
+
+/* Compare two sets of attributes for equality.  */
+static inline bool
+equal_attributes (attributes_t attr1, attributes_t attr2)
+{
+  return (attr1.color == attr2.color
+         && attr1.bgcolor == attr2.bgcolor
+         && attr1.weight == attr2.weight
+         && attr1.posture == attr2.posture
+         && attr1.underline == attr2.underline);
+}
+
+/* Convert a color in RGB encoding to BGR encoding.  */
+static inline int
+color_bgr (term_color_t color)
+{
+  return ((color & 4) >> 2) | (color & 2) | ((color & 1) << 2);
+}
+
+/* Output a single char to out_fd.  */
+static int
+out_char (int c)
+{
+  char bytes[1];
+
+  bytes[0] = (char)c;
+  /* We have to write directly to the file descriptor, not to a buffer with
+     the same destination, because of the padding and sleeping that tputs()
+     does.  */
+  if (full_write (out_fd, bytes, 1) < 1)
+    error (EXIT_FAILURE, errno, _("error writing to %s"), out_filename);
+  return 0;
+}
+
+/* Output escape sequences to switch from OLD_ATTR to NEW_ATTR.  */
+static void
+out_attr_change (term_ostream_t stream,
+                attributes_t old_attr, attributes_t new_attr)
+{
+  bool cleared_attributes;
+
+  /* We don't know the default colors of the terminal.  The only way to switch
+     back to a default color is to use stream->orig_pair.  */
+  if ((new_attr.color == COLOR_DEFAULT && old_attr.color != COLOR_DEFAULT)
+      || (new_attr.bgcolor == COLOR_DEFAULT && old_attr.bgcolor != COLOR_DEFAULT))
+    {
+      assert (stream->supports_foreground || stream->supports_background);
+      tputs (stream->orig_pair, 1, out_char);
+      old_attr.color = COLOR_DEFAULT;
+      old_attr.bgcolor = COLOR_DEFAULT;
+    }
+  if (new_attr.color != old_attr.color)
+    {
+      assert (stream->supports_foreground);
+      assert (new_attr.color != COLOR_DEFAULT);
+      if (stream->set_a_foreground != NULL)
+       tputs (tparm (stream->set_a_foreground, new_attr.color), 1, out_char);
+      else
+       tputs (tparm (stream->set_foreground, color_bgr (new_attr.color)),
+              1, out_char);
+    }
+  if (new_attr.bgcolor != old_attr.bgcolor)
+    {
+      assert (stream->supports_background);
+      assert (new_attr.bgcolor != COLOR_DEFAULT);
+      if (stream->set_a_background != NULL)
+       tputs (tparm (stream->set_a_background, new_attr.bgcolor), 1, out_char);
+      else
+       tputs (tparm (stream->set_background, color_bgr (new_attr.bgcolor)),
+              1, out_char);
+    }
+
+  cleared_attributes = false;
+  if (new_attr.weight != old_attr.weight)
+    {
+      assert (stream->supports_weight);
+      if (new_attr.weight == WEIGHT_BOLD)
+       tputs (stream->enter_bold_mode, 1, out_char);
+      else
+       {
+         /* The simplest way to clear bold mode is exit_attribute_mode.
+            With xterm, you can also do it with "Esc [ 0 m", but this escape
+            sequence is not contained in the terminfo description.  */
+         tputs (stream->exit_attribute_mode, 1, out_char);
+         /* We don't know whether exit_attribute_mode clears also the
+            italics or underline mode.  */
+         cleared_attributes = true;
+       }
+    }
+  if (new_attr.posture != old_attr.posture
+      || (cleared_attributes && new_attr.posture != POSTURE_DEFAULT))
+    {
+      assert (stream->supports_posture);
+      if (new_attr.posture == POSTURE_ITALIC)
+       tputs (stream->enter_italics_mode, 1, out_char);
+      else
+       {
+         if (stream->exit_italics_mode != NULL)
+           tputs (stream->exit_italics_mode, 1, out_char);
+         else if (!cleared_attributes)
+           tputs (stream->exit_attribute_mode, 1, out_char);
+       }
+    }
+  if (new_attr.underline != old_attr.underline
+      || (cleared_attributes && new_attr.underline != UNDERLINE_DEFAULT))
+    {
+      assert (stream->supports_underline);
+      if (new_attr.underline == UNDERLINE_ON)
+       tputs (stream->enter_underline_mode, 1, out_char);
+      else
+       {
+         if (stream->exit_underline_mode != NULL)
+           tputs (stream->exit_underline_mode, 1, out_char);
+         else if (!cleared_attributes)
+           tputs (stream->exit_attribute_mode, 1, out_char);
+       }
+    }
+}
+
+/* Output the buffered line atomically.
+   The terminal is assumed to have the default state (regarding colors and
+   attributes) before this call.  It is left in default state after this
+   call (regardless of stream->curr_attr).  */
+static void
+output_buffer (term_ostream_t stream)
+{
+  attributes_t default_attr;
+  attributes_t attr;
+  const char *cp;
+  const attributes_t *ap;
+  size_t len;
+  size_t n;
+
+  default_attr.color = COLOR_DEFAULT;
+  default_attr.bgcolor = COLOR_DEFAULT;
+  default_attr.weight = WEIGHT_DEFAULT;
+  default_attr.posture = POSTURE_DEFAULT;
+  default_attr.underline = UNDERLINE_DEFAULT;
+
+  attr = default_attr;
+
+  cp = stream->buffer;
+  ap = stream->attrbuffer;
+  len = stream->buflen;
+
+  /* See how much we can output without blocking signals.  */
+  for (n = 0; n < len && equal_attributes (ap[n], attr); n++)
+    ;
+  if (n > 0)
+    {
+      if (full_write (stream->fd, cp, n) < n)
+       error (EXIT_FAILURE, errno, _("error writing to %s"), stream->filename);
+      cp += n;
+      ap += n;
+      len -= n;
+    }
+  if (len > 0)
+    {
+      /* Block fatal signals, so that a SIGINT or similar doesn't interrupt
+        us without the possibility of restoring the terminal's state.  */
+      block_fatal_signals ();
+
+      /* Enable the exit handler for restoring the terminal's state.  */
+      restore_colors =
+       (stream->supports_foreground || stream->supports_background
+        ? stream->orig_pair
+        : NULL);
+      restore_weight =
+       (stream->supports_weight ? stream->exit_attribute_mode : NULL);
+      restore_posture =
+       (stream->supports_posture
+        ? (stream->exit_italics_mode != NULL
+           ? stream->exit_italics_mode
+           : stream->exit_attribute_mode)
+        : NULL);
+      restore_underline =
+       (stream->supports_underline
+        ? (stream->exit_underline_mode != NULL
+           ? stream->exit_underline_mode
+           : stream->exit_attribute_mode)
+        : NULL);
+      out_fd = stream->fd;
+      out_filename = stream->filename;
+
+      while (len > 0)
+       {
+         /* Activate the attributes in *ap.  */
+         out_attr_change (stream, attr, *ap);
+         attr = *ap;
+         /* See how many characters we can output without further attribute
+            changes.  */
+         for (n = 1; n < len && equal_attributes (ap[n], attr); n++)
+           ;
+         if (full_write (stream->fd, cp, n) < n)
+           error (EXIT_FAILURE, errno, _("error writing to %s"),
+                  stream->filename);
+         cp += n;
+         ap += n;
+         len -= n;
+       }
+
+      /* Switch back to the default attributes.  */
+      out_attr_change (stream, attr, default_attr);
+
+      /* Disable the exit handler.  */
+      out_fd = -1;
+      out_filename = NULL;
+
+      /* Unblock fatal signals.  */
+      unblock_fatal_signals ();
+    }
+  stream->buflen = 0;
+}
+
+/* Implementation of ostream_t methods.  */
+
+static void
+term_ostream::write_mem (term_ostream_t stream, const void *data, size_t len)
+{
+  const char *cp = (const char *) data;
+  while (len > 0)
+    {
+      /* Look for the next newline.  */
+      const char *newline = (const char *) memchr (cp, '\n', len);
+      size_t n = (newline != NULL ? newline - cp : len);
+
+      /* Copy n bytes into the buffer.  */
+      if (n > stream->allocated - stream->buflen)
+       {
+         size_t new_allocated =
+           xmax (xsum (stream->buflen, n),
+                 xsum (stream->allocated, stream->allocated));
+         if (size_overflow_p (new_allocated))
+           error (EXIT_FAILURE, 0,
+                  _("%s: too much output, buffer size overflow"),
+                  "term_ostream");
+         stream->buffer = (char *) xrealloc (stream->buffer, new_allocated);
+         stream->attrbuffer =
+           (attributes_t *)
+           xrealloc (stream->attrbuffer,
+                     new_allocated * sizeof (attributes_t));
+         stream->allocated = new_allocated;
+       }
+      memcpy (stream->buffer + stream->buflen, cp, n);
+      {
+       attributes_t attr = stream->simp_attr;
+       attributes_t *ap = stream->attrbuffer + stream->buflen;
+       attributes_t *ap_end = ap + n;
+       for (; ap < ap_end; ap++)
+         *ap = attr;
+      }
+      stream->buflen += n;
+
+      if (newline != NULL)
+       {
+         output_buffer (stream);
+         if (full_write (stream->fd, "\n", 1) < 1)
+           error (EXIT_FAILURE, errno, _("error writing to %s"),
+                  stream->filename);
+         cp += n + 1; /* cp = newline + 1; */
+         len -= n + 1;
+       }
+      else
+       break;
+    }
+}
+
+static void
+term_ostream::flush (term_ostream_t stream)
+{
+  output_buffer (stream);
+}
+
+static void
+term_ostream::free (term_ostream_t stream)
+{
+  term_ostream_flush (stream);
+  free (stream->filename);
+  if (stream->set_a_foreground != NULL)
+    free (stream->set_a_foreground);
+  if (stream->set_foreground != NULL)
+    free (stream->set_foreground);
+  if (stream->set_a_background != NULL)
+    free (stream->set_a_background);
+  if (stream->set_background != NULL)
+    free (stream->set_background);
+  if (stream->orig_pair != NULL)
+    free (stream->orig_pair);
+  if (stream->enter_bold_mode != NULL)
+    free (stream->enter_bold_mode);
+  if (stream->enter_italics_mode != NULL)
+    free (stream->enter_italics_mode);
+  if (stream->exit_italics_mode != NULL)
+    free (stream->exit_italics_mode);
+  if (stream->enter_underline_mode != NULL)
+    free (stream->enter_underline_mode);
+  if (stream->exit_underline_mode != NULL)
+    free (stream->exit_underline_mode);
+  if (stream->exit_attribute_mode != NULL)
+    free (stream->exit_attribute_mode);
+  free (stream->buffer);
+  free (stream);
+}
+
+/* Implementation of term_ostream_t methods.  */
+
+term_color_t
+term_ostream::get_color (term_ostream_t stream)
+{
+  return stream->curr_attr.color;
+}
+
+void
+term_ostream::set_color (term_ostream_t stream, term_color_t color)
+{
+  stream->curr_attr.color = color;
+  stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
+}
+
+term_color_t
+term_ostream::get_bgcolor (term_ostream_t stream)
+{
+  return stream->curr_attr.bgcolor;
+}
+
+void
+term_ostream::set_bgcolor (term_ostream_t stream, term_color_t color)
+{
+  stream->curr_attr.bgcolor = color;
+  stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
+}
+
+term_weight_t
+term_ostream::get_weight (term_ostream_t stream)
+{
+  return stream->curr_attr.weight;
+}
+
+void
+term_ostream::set_weight (term_ostream_t stream, term_weight_t weight)
+{
+  stream->curr_attr.weight = weight;
+  stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
+}
+
+term_posture_t
+term_ostream::get_posture (term_ostream_t stream)
+{
+  return stream->curr_attr.posture;
+}
+
+void
+term_ostream::set_posture (term_ostream_t stream, term_posture_t posture)
+{
+  stream->curr_attr.posture = posture;
+  stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
+}
+
+term_underline_t
+term_ostream::get_underline (term_ostream_t stream)
+{
+  return stream->curr_attr.underline;
+}
+
+void
+term_ostream::set_underline (term_ostream_t stream, term_underline_t underline)
+{
+  stream->curr_attr.underline = underline;
+  stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
+}
+
+/* Constructor.  */
+
+static inline char *
+xstrdup0 (const char *str)
+{
+  return (str != NULL ? xstrdup (str) : NULL);
+}
+
+term_ostream_t
+term_ostream_create (int fd, const char *filename)
+{
+  term_ostream_t stream = XMALLOC (struct term_ostream_representation);
+  const char *term;
+
+  stream->base.vtable = &term_ostream_vtable;
+  stream->fd = fd;
+  stream->filename = xstrdup (filename);
+
+  /* Defaults.  */
+  stream->max_colors = -1;
+  stream->no_color_video = -1;
+  stream->set_a_foreground = NULL;
+  stream->set_foreground = NULL;
+  stream->set_a_background = NULL;
+  stream->set_background = NULL;
+  stream->orig_pair = NULL;
+  stream->enter_bold_mode = NULL;
+  stream->enter_italics_mode = NULL;
+  stream->exit_italics_mode = NULL;
+  stream->enter_underline_mode = NULL;
+  stream->exit_underline_mode = NULL;
+  stream->exit_attribute_mode = NULL;
+
+  /* Retrieve the terminal type.  */
+  term = getenv ("TERM");
+  if (term != NULL && term[0] != '\0')
+    {
+      struct { char buf[1024]; char canary[4]; } termcapbuf;
+      int retval;
+  
+      /* Call tgetent, being defensive against buffer overflow.  */
+      memcpy (termcapbuf.canary, "CnRy", 4);
+      retval = tgetent (termcapbuf.buf, term);
+      if (memcmp (termcapbuf.canary, "CnRy", 4) != 0)
+       /* Buffer overflow!  */
+       abort ();
+
+      /* Retrieve particular values depending on the terminal type.  */
+      stream->max_colors = tgetnum ("Co");
+      stream->no_color_video = tgetnum ("nc");
+      stream->set_a_foreground = xstrdup0 (tgetstr ("AF", NULL));
+      stream->set_foreground = xstrdup0 (tgetstr ("Sf", NULL));
+      stream->set_a_background = xstrdup0 (tgetstr ("AB", NULL));
+      stream->set_background = xstrdup0 (tgetstr ("Sb", NULL));
+      stream->orig_pair = xstrdup0 (tgetstr ("op", NULL));
+      stream->enter_bold_mode = xstrdup0 (tgetstr ("md", NULL));
+      stream->enter_italics_mode = xstrdup0 (tgetstr ("ZH", NULL));
+      stream->exit_italics_mode = xstrdup0 (tgetstr ("ZR", NULL));
+      stream->enter_underline_mode = xstrdup0 (tgetstr ("us", NULL));
+      stream->exit_underline_mode = xstrdup0 (tgetstr ("ue", NULL));
+      stream->exit_attribute_mode = xstrdup0 (tgetstr ("me", NULL));
+    }
+
+  /* Infer the capabilities.  */
+  stream->supports_foreground =
+    (stream->max_colors >= 8
+     && (stream->set_a_foreground != NULL || stream->set_foreground != NULL)
+     && stream->orig_pair != NULL);
+  stream->supports_background =
+    (stream->max_colors >= 8
+     && (stream->set_a_background != NULL || stream->set_background != NULL)
+     && stream->orig_pair != NULL);
+  stream->supports_weight =
+    (stream->enter_bold_mode != NULL && stream->exit_attribute_mode != NULL);
+  stream->supports_posture =
+    (stream->enter_italics_mode != NULL
+     && (stream->exit_italics_mode != NULL
+        || stream->exit_attribute_mode != NULL));
+  stream->supports_underline =
+    (stream->enter_underline_mode != NULL
+     && (stream->exit_underline_mode != NULL
+        || stream->exit_attribute_mode != NULL));
+
+  /* Initialize the buffer.  */
+  stream->allocated = 120;
+  stream->buffer = XNMALLOC (stream->allocated, char);
+  stream->attrbuffer = XNMALLOC (stream->allocated, attributes_t);
+  stream->buflen = 0;
+
+  /* Initialize the current attributes.  */
+  stream->curr_attr.color = COLOR_DEFAULT;
+  stream->curr_attr.bgcolor = COLOR_DEFAULT;
+  stream->curr_attr.weight = WEIGHT_DEFAULT;
+  stream->curr_attr.posture = POSTURE_DEFAULT;
+  stream->curr_attr.underline = UNDERLINE_DEFAULT;
+  stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
+
+  /* Register an exit handler.  */
+  {
+    static bool registered = false;
+    if (!registered)
+      {
+       atexit (restore);
+       registered = true;
+      }
+  }
+
+  return stream;
+}
diff --git a/gnulib-local/lib/term-ostream.oo.h b/gnulib-local/lib/term-ostream.oo.h
new file mode 100644 (file)
index 0000000..38dddfb
--- /dev/null
@@ -0,0 +1,110 @@
+/* Output stream for attributed text, producing ANSI escape sequences.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+   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 2, 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, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#ifndef _TERM_OSTREAM_H
+#define _TERM_OSTREAM_H
+
+#include "ostream.h"
+
+
+/* Querying and setting of text attributes.
+   The stream has a notion of the current text attributes; they apply
+   implicitly to all following output.  The attributes are automatically
+   reset when the stream is closed.
+   Note: Not all terminal types can actually render all attributes adequately.
+   For example, xterm cannot render POSTURE_ITALIC nor the combination of
+   WEIGHT_BOLD and UNDERLINE_ON.  */
+
+typedef enum
+{                     /* RGB components */
+  COLOR_BLACK = 0,    /* 000 */
+  COLOR_BLUE,         /* 001 */
+  COLOR_GREEN,        /* 010 */
+  COLOR_CYAN,         /* 011 */
+  COLOR_RED,          /* 100 */
+  COLOR_MAGENTA,      /* 101 */
+  COLOR_YELLOW,       /* 110 */
+  COLOR_WHITE,        /* 111 */
+  COLOR_DEFAULT = -1  /* unknown */
+} term_color_t;
+
+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;
+
+struct term_ostream : struct ostream
+{
+methods:
+
+  /* Get/set the text color.  */
+  term_color_t get_color (term_ostream_t stream);
+  void         set_color (term_ostream_t stream, term_color_t color);
+
+  /* Get/set the background color.  */
+  term_color_t get_bgcolor (term_ostream_t stream);
+  void         set_bgcolor (term_ostream_t stream, term_color_t color);
+
+  /* Get/set the font weight.  */
+  term_weight_t get_weight (term_ostream_t stream);
+  void          set_weight (term_ostream_t stream, term_weight_t weight);
+
+  /* Get/set the font posture.  */
+  term_posture_t get_posture (term_ostream_t stream);
+  void           set_posture (term_ostream_t stream, term_posture_t posture);
+
+  /* Get/set the text underline decoration.  */
+  term_underline_t get_underline (term_ostream_t stream);
+  void             set_underline (term_ostream_t stream,
+                                 term_underline_t underline);
+};
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Create an output stream referring to the file descriptor FD.
+   FILENAME is used only for error messages.
+   The resulting stream will be line-buffered.
+   Note that the resulting stream must be closed before FD can be closed.  */
+extern term_ostream_t term_ostream_create (int fd, const char *filename);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TERM_OSTREAM_H */
diff --git a/gnulib-local/m4/term-ostream.m4 b/gnulib-local/m4/term-ostream.m4
new file mode 100644 (file)
index 0000000..0fd8691
--- /dev/null
@@ -0,0 +1,10 @@
+# term-ostream.m4 serial 1
+dnl Copyright (C) 2006 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.
+
+AC_DEFUN([gl_TERM_OSTREAM],
+[
+  AC_REQUIRE([AC_C_INLINE])
+])
diff --git a/gnulib-local/modules/term-ostream b/gnulib-local/modules/term-ostream
new file mode 100644 (file)
index 0000000..24fdf68
--- /dev/null
@@ -0,0 +1,38 @@
+Description:
+Output stream for attributed text, producing ANSI escape sequences.
+
+Files:
+lib/term-ostream.oo.h
+lib/term-ostream.oo.c
+m4/term-ostream.m4
+
+Depends-on:
+ostream
+error
+exit
+fatal-signal
+full-write
+gettext-h
+termcap
+xalloc
+xsize
+
+configure.ac:
+gl_TERM_OSTREAM
+
+Makefile.am:
+lib_SOURCES += term-ostream.c
+term-ostream.h term-ostream.c : $(top_srcdir)/build-aux/moopp term-ostream.oo.h term-ostream.oo.c ostream.oo.h
+       $(top_srcdir)/build-aux/moopp $(srcdir)/term-ostream.oo.c $(srcdir)/term-ostream.oo.h $(srcdir)/ostream.oo.h
+BUILT_SOURCES += term-ostream.h term-ostream.c term_ostream.priv.h term_ostream.vt.h
+CLEANFILES += term-ostream.h term-ostream.c term_ostream.priv.h term_ostream.vt.h
+
+Include:
+"term-ostream.h"
+
+License:
+GPL
+
+Maintainer:
+Bruno Haible
+