]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Add styling support to the PO output routines.
authorBruno Haible <bruno@clisp.org>
Fri, 15 Dec 2006 12:57:56 +0000 (12:57 +0000)
committerBruno Haible <bruno@clisp.org>
Tue, 23 Jun 2009 10:14:29 +0000 (12:14 +0200)
ChangeLog
autogen.sh
gettext-tools/src/ChangeLog
gettext-tools/src/FILES
gettext-tools/src/Makefile.am
gettext-tools/src/write-catalog.c
gettext-tools/src/write-catalog.h
gettext-tools/src/write-po.c
gettext-tools/src/write-properties.c
gettext-tools/src/write-stringtable.c

index 5f470b484dcec3e8b177defc7f29f5b5adff0db2..378b6afa45a03fbe6420e4865aaa1873eb532379 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2006-12-01  Bruno Haible  <bruno@clisp.org>
+
+       * autogen.sh (GNULIB_MODULES_TOOLS_FOR_SRC): Add fd-ostream,
+       styled-ostream, html-styled-ostream, term-styled-ostream.
+
 2006-12-04  Bruno Haible  <bruno@clisp.org>
 
        * DEPENDENCIES: New file.
index 62704e3de5bf299c0bd5a84591cc391590fd05f4..e89ba7ed8bf3fff4b385892993ebfa9a9060cadb 100755 (executable)
@@ -99,6 +99,7 @@ if test -n "$GNULIB_TOOL"; then
   error-progname
   execute
   exit
+  fd-ostream
   file-ostream
   findprog
   fnmatch-posix
@@ -110,6 +111,7 @@ if test -n "$GNULIB_TOOL"; then
   getopt
   gettext-h
   hash
+  html-styled-ostream
   iconv
   javacomp
   javaexec
@@ -136,6 +138,8 @@ if test -n "$GNULIB_TOOL"; then
   strpbrk
   strtol
   strtoul
+  styled-ostream
+  term-styled-ostream
   ucs4-utf8
   unistd
   unlocked-io
index 14f84e7b651023807d95b13fc08c546d360a6dfd..e59a22d33787b02d3325f95fdd5a02229c66cbcf 100644 (file)
@@ -1,3 +1,44 @@
+2006-12-01  Bruno Haible  <bruno@clisp.org>
+
+       Add styling support to the PO output routines.
+       * color.h: New file.
+       * color.c: New file.
+       * write-catalog.h (struct catalog_output_format): Add field
+       'supports_color'.
+       * write-catalog.c: Include fcntl.h, unistd.h, styled-ostream.h,
+       term-styled-ostream.h, html-styled-ostream.h, fd-ostream.h, color.h,
+       po-charset.h, msgl-iconv.h.
+       (STDOUT_FILENO): New macro.
+       (ENABLE_COLOR): New macro.
+       (msgdomain_list_print): Use a styled_ostream_t that uses the
+       style_file_name.
+       * write-po.c: Include format.h, styled-ostream.h.
+       (is_stylable, begin_css_class, end_css_class): New functions or macros.
+       (class_header, class_translated, class_untranslated, class_fuzzy,
+       class_obsolete, class_comment, class_translator_comment,
+       class_extracted_comment, class_reference_comment, class_reference,
+       class_flag_comment, class_flag, class_fuzzy_flag,
+       class_previous_comment, class_previous, class_msgid, class_msgstr,
+       class_keyword, class_string, class_text, class_escape_sequence,
+       class_format_directive, class_invalid_format_directive): New variables.
+       (ATTR_ESCAPE_SEQUENCE, ATTR_FORMAT_DIRECTIVE,
+       ATTR_INVALID_FORMAT_DIRECTIVE): New enum items.
+       (message_print_comment, message_print_comment_dot,
+       message_print_comment_filepos, message_print_comment_flags): Call
+       begin/end_css_class.
+       (memset_small): New function.
+       (wrap): Add css_class argument. Determine the extent of format
+       string directives. Call begin/end_css_class.
+       (print_blank_line): Call begin/end_css_class.
+       (message_print, message_print_obsolete): Likewise. Pass a css_class
+       argument to 'wrap'.
+       (msgdomain_list_print_po): Call begin/end_css_class.
+       (output_format_po): Update.
+       * write-properties.c (output_format_properties): Update.
+       * write-stringtable.c (output_format_stringtable): Update.
+       * Makefile.am (noinst_HEADERS): Add color.h.
+       (libgettextsrc_la_SOURCES): Add color.c.
+
 2006-11-26  Bruno Haible  <bruno@clisp.org>
 
        * write-po.c (make_format_description_string,
index 9340ade0bd220a6787bfd9ad56466d5fe2ae4ce9..49cf3faa3d5f92c90749540bd13e30b9c4338cff 100644 (file)
@@ -43,6 +43,9 @@ po-xerror.c
                 Error handling during writing and reading of PO files.
 
 +-------------- Writing PO files
+| color.h
+| color.c
+|               Color and style handling.
 | write-catalog.h
 | write-catalog.c
 |               Output of a list-of-messages.
index 1cc0ca1bc6c3796ff1a5b0c342b791b6b86aba35..98c4c100a6a1abd1186c832623bf8e61a0a5a671 100644 (file)
@@ -38,7 +38,7 @@ noinst_HEADERS = pos.h message.h po-error.h po-xerror.h po-gram.h po-charset.h \
 po-lex.h open-catalog.h read-catalog-abstract.h read-catalog.h \
 read-po.h read-properties.h read-stringtable.h \
 str-list.h \
-write-catalog.h write-po.h write-properties.h write-stringtable.h \
+color.h write-catalog.h write-po.h write-properties.h write-stringtable.h \
 dir-list.h file-list.h po-gram-gen.h po-gram-gen2.h \
 msgl-charset.h msgl-equal.h msgl-iconv.h msgl-ascii.h msgl-cat.h \
 msgl-english.h msgl-check.h msgl-fsearch.h msgfmt.h msgunfmt.h \
@@ -118,7 +118,7 @@ format-php.c format-gcc-internal.c format-qt.c format-boost.c
 # libgettextsrc contains all code that is needed by at least two programs.
 libgettextsrc_la_SOURCES = \
 $(COMMON_SOURCE) read-catalog.c \
-write-catalog.c write-properties.c write-stringtable.c write-po.c \
+color.c write-catalog.c write-properties.c write-stringtable.c write-po.c \
 msgl-ascii.c msgl-iconv.c msgl-equal.c msgl-cat.c msgl-english.c msgl-check.c \
 file-list.c msgl-charset.c po-time.c plural-exp.c plural-eval.c plural-table.c \
 $(FORMAT_SOURCE)
index 4e12272d7c897805baee62bc68f3c14aa8ac3171..6c66918b34fa2da337df29c8c8a1e65de1c16346 100644 (file)
 #include "write-catalog.h"
 
 #include <errno.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include <unistd.h>
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif
+
 #include "ostream.h"
 #include "file-ostream.h"
 #include "fwriteerror.h"
 /* Our regular abbreviation.  */
 #define _(str) gettext (str)
 
+/* When compiled in src, enable color support.
+   When compiled in libgettextpo, don't enable color support.  */
+#ifdef GETTEXTDATADIR
+
+# define ENABLE_COLOR 1
+
+# include "styled-ostream.h"
+# include "term-styled-ostream.h"
+# include "html-styled-ostream.h"
+# include "fd-ostream.h"
+
+# include "color.h"
+# include "po-charset.h"
+# include "msgl-iconv.h"
+
+#endif
+
 
 /* =========== Some parameters for use by 'msgdomain_list_print'. ========== */
 
@@ -72,8 +95,7 @@ msgdomain_list_print (msgdomain_list_ty *mdlp, const char *filename,
                      catalog_output_format_ty output_syntax,
                      bool force, bool debug)
 {
-  FILE *fp;
-  file_ostream_t stream;
+  bool to_stdout;
 
   /* We will not write anything if, for every domain, we have no message
      or only the header entry.  */
@@ -185,41 +207,118 @@ message catalog has plural form translations, but the output format does not sup
        }
     }
 
-  /* Open the output file.  */
-  if (filename != NULL && strcmp (filename, "-") != 0
-      && strcmp (filename, "/dev/stdout") != 0)
+  to_stdout = (filename == NULL || strcmp (filename, "-") == 0
+              || strcmp (filename, "/dev/stdout") == 0);
+
+#if ENABLE_COLOR
+  if (output_syntax->supports_color
+      && (color_mode == color_yes
+         || (color_mode == color_tty && to_stdout && isatty (STDOUT_FILENO))))
     {
-      fp = fopen (filename, "w");
-      if (fp == NULL)
+      int fd;
+      ostream_t stream;
+
+      /* Open the output file.  */
+      if (!to_stdout)
+       {
+         fd = open (filename, O_WRONLY | O_CREAT);
+         if (fd < 0)
+           {
+             const char *errno_description = strerror (errno);
+             po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+                        xasprintf ("%s: %s",
+                                   xasprintf (_("cannot create output file \"%s\""),
+                                              filename),
+                                   errno_description));
+           }
+       }
+      else
+       {
+         fd = STDOUT_FILENO;
+         filename = _("standard output");
+       }
+
+      style_file_prepare ();
+      stream = term_styled_ostream_create (fd, filename, style_file_name);
+      if (stream == NULL)
+       stream = fd_ostream_create (fd, filename, true);
+      output_syntax->print (mdlp, stream, page_width, debug);
+      ostream_free (stream);
+
+      /* Make sure nothing went wrong.  */
+      if (close (fd) < 0)
        {
          const char *errno_description = strerror (errno);
          po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
                     xasprintf ("%s: %s",
-                               xasprintf (_("cannot create output file \"%s\""),
+                               xasprintf (_("error while writing \"%s\" file"),
                                           filename),
                                errno_description));
        }
     }
   else
+#endif
     {
-      fp = stdout;
-      /* xgettext:no-c-format */
-      filename = _("standard output");
-    }
+      FILE *fp;
+      file_ostream_t stream;
 
-  stream = file_ostream_create (fp);
-  output_syntax->print (mdlp, stream, page_width, debug);
-  ostream_free (stream);
+      /* Open the output file.  */
+      if (!to_stdout)
+       {
+         fp = fopen (filename, "w");
+         if (fp == NULL)
+           {
+             const char *errno_description = strerror (errno);
+             po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+                        xasprintf ("%s: %s",
+                                   xasprintf (_("cannot create output file \"%s\""),
+                                              filename),
+                                   errno_description));
+           }
+       }
+      else
+       {
+         fp = stdout;
+         filename = _("standard output");
+       }
 
-  /* Make sure nothing went wrong.  */
-  if (fwriteerror (fp))
-    {
-      const char *errno_description = strerror (errno);
-      po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
-                xasprintf ("%s: %s",
-                           xasprintf (_("error while writing \"%s\" file"),
-                                      filename),
-                           errno_description));
+      stream = file_ostream_create (fp);
+
+#if ENABLE_COLOR
+      if (output_syntax->supports_color && color_mode == color_html)
+       {
+         html_styled_ostream_t html_stream;
+
+         /* Convert mdlp to UTF-8 encoding.  */
+         if (mdlp->encoding != po_charset_utf8)
+           {
+             mdlp = msgdomain_list_copy (mdlp, 0);
+             mdlp = iconv_msgdomain_list (mdlp, po_charset_utf8, false, NULL);
+           }
+
+         style_file_prepare ();
+         html_stream = html_styled_ostream_create (stream, style_file_name);
+         output_syntax->print (mdlp, html_stream, page_width, debug);
+         ostream_free (html_stream);
+       }
+      else
+#endif
+       {
+         output_syntax->print (mdlp, stream, page_width, debug);
+       }
+
+      ostream_free (stream);
+
+      /* Make sure nothing went wrong.  */
+      if (fwriteerror (fp))
+       {
+         const char *errno_description = strerror (errno);
+         po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+                    xasprintf ("%s: %s",
+                               xasprintf (_("error while writing \"%s\" file"),
+                                          filename),
+                               errno_description));
+       }
     }
 }
 
index 923302900382496549005a111c347fd3d90ca0a8..4d2e2dee49b6a2b260f16b85d3dbdf8cc56083ab 100644 (file)
@@ -39,6 +39,9 @@ struct catalog_output_format
      encoding.  */
   bool requires_utf8;
 
+  /* Whether the print function supports styled output.  */
+  bool supports_color;
+
   /* Whether the format supports multiple domains in a single file.  */
   bool supports_multiple_domains;
 
index 801040a94f61ca67561de3277b7f68f4b8c26d44..733375e7e565c062137a886dfb90b469fa991683 100644 (file)
@@ -37,6 +37,7 @@
 
 #include "c-ctype.h"
 #include "po-charset.h"
+#include "format.h"
 #include "linebreak.h"
 #include "msgl-ascii.h"
 #include "write-catalog.h"
@@ -44,6 +45,9 @@
 #include "xallocsa.h"
 #include "c-strstr.h"
 #include "ostream.h"
+#ifdef GETTEXTDATADIR
+# include "styled-ostream.h"
+#endif
 #include "xvasprintf.h"
 #include "po-xerror.h"
 #include "gettext.h"
@@ -140,6 +144,88 @@ make_c_width_description_string (enum is_wrap do_wrap)
 }
 
 
+/* ========================== Styling primitives. ========================== */
+
+
+/* When compiled in src, enable styling support.
+   When compiled in libgettextpo, don't enable styling support.  */
+#ifdef GETTEXTDATADIR
+
+/* Return true if the stream is an instance of styled_ostream_t.  */
+static inline bool
+is_stylable (ostream_t stream)
+{
+  return IS_INSTANCE (stream, ostream, styled_ostream);
+}
+
+/* Start a run of text belonging to a given CSS class.  */
+static void
+begin_css_class (ostream_t stream, const char *classname)
+{
+  if (is_stylable (stream))
+    styled_ostream_begin_use_class ((styled_ostream_t) stream, classname);
+}
+
+/* End a run of text belonging to a given CSS class.  */
+static void
+end_css_class (ostream_t stream, const char *classname)
+{
+  if (is_stylable (stream))
+    styled_ostream_end_use_class ((styled_ostream_t) stream, classname);
+}
+
+#else
+
+#define is_stylable(stream) false
+#define begin_css_class(stream,classname) /* empty */
+#define end_css_class(stream,classname) /* empty */
+
+#endif
+
+/* CSS classes at message level.  */
+static const char class_header[] = "header";
+static const char class_translated[] = "translated";
+static const char class_untranslated[] = "untranslated";
+static const char class_fuzzy[] = "fuzzy";
+static const char class_obsolete[] = "obsolete";
+
+/* CSS classes describing the parts of a message.  */
+static const char class_comment[] = "comment";
+static const char class_translator_comment[] = "translator-comment";
+static const char class_extracted_comment[] = "extracted-comment";
+static const char class_reference_comment[] = "reference-comment";
+static const char class_reference[] = "reference";
+static const char class_flag_comment[] = "flag-comment";
+static const char class_flag[] = "flag";
+static const char class_fuzzy_flag[] = "fuzzy-flag";
+static const char class_previous_comment[] = "previous-comment";
+static const char class_previous[] = "previous";
+static const char class_msgid[] = "msgid";
+static const char class_msgstr[] = "msgstr";
+static const char class_keyword[] = "keyword";
+static const char class_string[] = "string";
+
+/* CSS classes for the contents of strings.  */
+static const char class_text[] = "text";
+static const char class_escape_sequence[] = "escape-sequence";
+static const char class_format_directive[] = "format-directive";
+static const char class_invalid_format_directive[] = "invalid-format-directive";
+#if 0
+static const char class_added[] = "added";
+static const char class_changed[] = "changed";
+static const char class_removed[] = "removed";
+#endif
+
+/* Per-character attributes.  */
+enum
+{
+  ATTR_ESCAPE_SEQUENCE          = 1 << 0,
+  /* The following two are exclusive.  */
+  ATTR_FORMAT_DIRECTIVE         = 1 << 1,
+  ATTR_INVALID_FORMAT_DIRECTIVE = 1 << 2
+};
+
+
 /* ================ Output parts of a message, as comments. ================ */
 
 
@@ -152,6 +238,8 @@ message_print_comment (const message_ty *mp, ostream_t stream)
     {
       size_t j;
 
+      begin_css_class (stream, class_translator_comment);
+
       for (j = 0; j < mp->comment->nitems; ++j)
        {
          const char *s = mp->comment->item[j];
@@ -176,6 +264,8 @@ message_print_comment (const message_ty *mp, ostream_t stream)
            }
          while (s != NULL);
        }
+
+      end_css_class (stream, class_translator_comment);
     }
 }
 
@@ -189,6 +279,8 @@ message_print_comment_dot (const message_ty *mp, ostream_t stream)
     {
       size_t j;
 
+      begin_css_class (stream, class_extracted_comment);
+
       for (j = 0; j < mp->comment_dot->nitems; ++j)
        {
          const char *s = mp->comment_dot->item[j];
@@ -198,6 +290,8 @@ message_print_comment_dot (const message_ty *mp, ostream_t stream)
          ostream_write_str (stream, s);
          ostream_write_str (stream, "\n");
        }
+
+      end_css_class (stream, class_extracted_comment);
     }
 }
 
@@ -210,6 +304,8 @@ message_print_comment_filepos (const message_ty *mp, ostream_t stream,
 {
   if (mp->filepos_count != 0)
     {
+      begin_css_class (stream, class_reference_comment);
+
       if (uniforum)
        {
          size_t j;
@@ -222,11 +318,15 @@ message_print_comment_filepos (const message_ty *mp, ostream_t stream,
 
              while (cp[0] == '.' && cp[1] == '/')
                cp += 2;
+             ostream_write_str (stream, "# ");
+             begin_css_class (stream, class_reference);
              /* There are two Sun formats to choose from: SunOS and
                 Solaris.  Use the Solaris form here.  */
-             str = xasprintf ("# File: %s, line: %ld\n",
+             str = xasprintf ("File: %s, line: %ld",
                               cp, (long) pp->line_number);
              ostream_write_str (stream, str);
+             end_css_class (stream, class_reference);
+             ostream_write_str (stream, "\n");
              free (str);
            }
        }
@@ -260,12 +360,16 @@ message_print_comment_filepos (const message_ty *mp, ostream_t stream,
                  column = 2;
                }
              ostream_write_str (stream, " ");
+             begin_css_class (stream, class_reference);
              ostream_write_str (stream, cp);
              ostream_write_str (stream, buffer);
+             end_css_class (stream, class_reference);
              column += len;
            }
          ostream_write_str (stream, "\n");
        }
+
+      end_css_class (stream, class_reference_comment);
     }
 }
 
@@ -282,6 +386,8 @@ message_print_comment_flags (const message_ty *mp, ostream_t stream, bool debug)
       bool first_flag = true;
       size_t i;
 
+      begin_css_class (stream, class_flag_comment);
+
       ostream_write_str (stream, "#,");
 
       /* We don't print the fuzzy flag if the msgstr is empty.  This
@@ -289,7 +395,12 @@ message_print_comment_flags (const message_ty *mp, ostream_t stream, bool debug)
         output.  */
       if (mp->is_fuzzy && mp->msgstr[0] != '\0')
        {
-         ostream_write_str (stream, " fuzzy");
+         ostream_write_str (stream, " ");
+         begin_css_class (stream, class_flag);
+         begin_css_class (stream, class_fuzzy_flag);
+         ostream_write_str (stream, "fuzzy");
+         end_css_class (stream, class_fuzzy_flag);
+         end_css_class (stream, class_flag);
          first_flag = false;
        }
 
@@ -300,10 +411,12 @@ message_print_comment_flags (const message_ty *mp, ostream_t stream, bool debug)
              ostream_write_str (stream, ",");
 
            ostream_write_str (stream, " ");
+           begin_css_class (stream, class_flag);
            ostream_write_str (stream,
                               make_format_description_string (mp->is_format[i],
                                                               format_language[i],
                                                               debug));
+           end_css_class (stream, class_flag);
            first_flag = false;
          }
 
@@ -313,12 +426,16 @@ message_print_comment_flags (const message_ty *mp, ostream_t stream, bool debug)
            ostream_write_str (stream, ",");
 
          ostream_write_str (stream, " ");
+         begin_css_class (stream, class_flag);
          ostream_write_str (stream,
                             make_c_width_description_string (mp->do_wrap));
+         end_css_class (stream, class_flag);
          first_flag = false;
        }
 
       ostream_write_str (stream, "\n");
+
+      end_css_class (stream, class_flag_comment);
     }
 }
 
@@ -382,14 +499,30 @@ memcpy_small (void *dst, const void *src, size_t n)
 }
 
 
+/* A version of memset optimized for the case n <= 1.  */
+static inline void
+memset_small (void *dst, char c, size_t n)
+{
+  if (n > 0)
+    {
+      char *p = (char *) dst;
+
+      *p = c;
+      if (--n > 0)
+       do *++p = c; while (--n > 0);
+    }
+}
+
+
 static void
 wrap (const message_ty *mp, ostream_t stream,
-      const char *line_prefix, int extra_indent,
+      const char *line_prefix, int extra_indent, const char *css_class,
       const char *name, const char *value,
       enum is_wrap do_wrap, size_t page_width,
       const char *charset)
 {
   const char *canon_charset;
+  char *fmtdir;
   const char *s;
   bool first_line;
 #if HAVE_ICONV
@@ -449,6 +582,58 @@ wrap (const message_ty *mp, ostream_t stream,
   if (canon_charset == NULL)
     canon_charset = po_charset_ascii;
 
+  /* Determine the extent of format string directives.  */
+  fmtdir = NULL;
+  if (is_stylable (stream) && value[0] != '\0')
+    {
+      bool is_msgstr =
+       (strlen (name) >= 6 && memcmp (name, "msgstr", 6) == 0);
+       /* or equivalent: = (css_class == class_msgstr) */
+      size_t i;
+
+      for (i = 0; i < NFORMATS; i++)
+       if (possible_format_p (mp->is_format[i]))
+         {
+           size_t len = strlen (value);
+           struct formatstring_parser *parser = formatstring_parsers[i];
+           char *invalid_reason = NULL;
+           void *descr;
+           char *fdp;
+           char *fd_end;
+
+           fmtdir = XCALLOC (len, char);
+           descr = parser->parse (value, is_msgstr, fmtdir, &invalid_reason);
+           if (descr != NULL)
+             parser->free (descr);
+
+           /* Locate the FMTDIR_* bits and transform the array to an array
+              of attributes.  */
+           fd_end = fmtdir + len;
+           for (fdp = fmtdir; fdp < fd_end; fdp++)
+             if (*fdp & FMTDIR_START)
+               {
+                 char *fdq;
+                 for (fdq = fdp; fdq < fd_end; fdq++)
+                   if (*fdq & (FMTDIR_END | FMTDIR_ERROR))
+                     break;
+                 if (!(fdq < fd_end))
+                   /* The ->parse method has determined the start of a
+                      formatstring directive but not stored a bit indicating
+                      its end. It is a bug in the ->parse method.  */
+                   abort ();
+                 if (*fdq & FMTDIR_ERROR)
+                   memset (fdp, ATTR_INVALID_FORMAT_DIRECTIVE, fdq - fdp + 1);
+                 else
+                   memset (fdp, ATTR_FORMAT_DIRECTIVE, fdq - fdp + 1);
+                 fdp = fdq;
+               }
+             else
+               *fdp = 0;
+
+           break;
+         }
+    }
+
   /* Loop over the '\n' delimited portions of value.  */
   s = value;
   first_line = true;
@@ -464,9 +649,11 @@ wrap (const message_ty *mp, ostream_t stream,
       size_t portion_len;
       char *portion;
       char *overrides;
+      char *attributes;
       char *linebreaks;
       char *pp;
       char *op;
+      char *ap;
       int startcol, startcol_after_break, width;
       size_t i;
 
@@ -548,9 +735,11 @@ wrap (const message_ty *mp, ostream_t stream,
       portion = XNMALLOC (portion_len, char);
       overrides = XNMALLOC (portion_len, char);
       memset (overrides, UC_BREAK_UNDEFINED, portion_len);
-      for (ep = s, pp = portion, op = overrides; ep < es; ep++)
+      attributes = XNMALLOC (portion_len, char);
+      for (ep = s, pp = portion, op = overrides, ap = attributes; ep < es; ep++)
        {
          char c = *ep;
+         char attr = (fmtdir != NULL ? fmtdir[ep - value] : 0);
          if (is_escape (c))
            {
              switch (c)
@@ -568,6 +757,8 @@ wrap (const message_ty *mp, ostream_t stream,
              *pp++ = c;
              op++;
              *op++ = UC_BREAK_PROHIBITED;
+             *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+             *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
              /* We warn about any use of escape sequences beside
                 '\n' and '\t'.  */
              if (c != 'n' && c != 't')
@@ -591,6 +782,10 @@ internationalized messages should not contain the `\\%c' escape sequence"),
              *op++ = UC_BREAK_PROHIBITED;
              *op++ = UC_BREAK_PROHIBITED;
              *op++ = UC_BREAK_PROHIBITED;
+             *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+             *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+             *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+             *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
            }
          else if (c == '\\' || c == '"')
            {
@@ -598,6 +793,8 @@ internationalized messages should not contain the `\\%c' escape sequence"),
              *pp++ = c;
              op++;
              *op++ = UC_BREAK_PROHIBITED;
+             *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+             *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
            }
          else
            {
@@ -643,6 +840,8 @@ internationalized messages should not contain the `\\%c' escape sequence"),
                  memcpy_small (pp, ep, insize);
                  pp += insize;
                  op += insize;
+                 memset_small (ap, attr, insize);
+                 ap += insize;
                  ep += insize - 1;
                }
              else
@@ -658,11 +857,14 @@ internationalized messages should not contain the `\\%c' escape sequence"),
                      ep += 1;
                      *pp++ = *ep;
                      op += 2;
+                     *ap++ = attr;
+                     *ap++ = attr;
                    }
                  else
                    {
                      *pp++ = c;
                      op++;
+                     *ap++ = attr;
                    }
                }
            }
@@ -724,8 +926,16 @@ internationalized messages should not contain the `\\%c' escape sequence"),
        {
          if (line_prefix != NULL)
            ostream_write_str (stream, line_prefix);
+         begin_css_class (stream, css_class);
+         begin_css_class (stream, class_keyword);
          ostream_write_str (stream, name);
-         ostream_write_str (stream, " \"\"\n");
+         end_css_class (stream, class_keyword);
+         ostream_write_str (stream, " ");
+         begin_css_class (stream, class_string);
+         ostream_write_str (stream, "\"\"");
+         end_css_class (stream, class_string);
+         end_css_class (stream, css_class);
+         ostream_write_str (stream, "\n");
          first_line = false;
          /* Recompute startcol and linebreaks.  */
          goto recompute;
@@ -742,10 +952,13 @@ internationalized messages should not contain the `\\%c' escape sequence"),
            ostream_write_str (stream, line_prefix);
            currcol = strlen (line_prefix);
          }
+       begin_css_class (stream, css_class);
        if (first_line)
          {
+           begin_css_class (stream, class_keyword);
            ostream_write_str (stream, name);
            currcol += strlen (name);
+           end_css_class (stream, class_keyword);
            if (indent)
              {
                if (extra_indent > 0)
@@ -775,33 +988,129 @@ internationalized messages should not contain the `\\%c' escape sequence"),
       }
 
       /* Print the portion itself, with linebreaks where necessary.  */
-      ostream_write_str (stream, "\"");
-      for (i = 0; i < portion_len; i++)
-       {
-         if (linebreaks[i] == UC_BREAK_POSSIBLE)
-           {
-             int currcol;
+      {
+       char currattr = 0;
 
-             ostream_write_str (stream, "\"\n");
-             currcol = 0;
-             /* INDENT-S.  */
-             if (line_prefix != NULL)
-               {
-                 ostream_write_str (stream, line_prefix);
-                 currcol = strlen (line_prefix);
-               }
-             if (indent)
-               {
-                 ostream_write_mem (stream, "        ", 8 - (currcol & 7));
-                 currcol = (currcol + 8) & ~7;
-               }
-             ostream_write_str (stream, "\"");
-           }
-         ostream_write_mem (stream, &portion[i], 1);
-       }
-      ostream_write_str (stream, "\"\n");
+       begin_css_class (stream, class_string);
+       ostream_write_str (stream, "\"");
+       begin_css_class (stream, class_text);
+
+       for (i = 0; i < portion_len; i++)
+         {
+           if (linebreaks[i] == UC_BREAK_POSSIBLE)
+             {
+               int currcol;
+
+               /* Change currattr so that it becomes 0.  */
+               if (currattr & ATTR_ESCAPE_SEQUENCE)
+                 {
+                   end_css_class (stream, class_escape_sequence);
+                   currattr &= ~ATTR_ESCAPE_SEQUENCE;
+                 }
+               if (currattr & ATTR_FORMAT_DIRECTIVE)
+                 {
+                   end_css_class (stream, class_format_directive);
+                   currattr &= ~ATTR_FORMAT_DIRECTIVE;
+                 }
+               else if (currattr & ATTR_INVALID_FORMAT_DIRECTIVE)
+                 {
+                   end_css_class (stream, class_invalid_format_directive);
+                   currattr &= ~ATTR_INVALID_FORMAT_DIRECTIVE;
+                 }
+               if (!(currattr == 0))
+                 abort ();
+
+               end_css_class (stream, class_text);
+               ostream_write_str (stream, "\"");
+               end_css_class (stream, class_string);
+               end_css_class (stream, css_class);
+               ostream_write_str (stream, "\n");
+               currcol = 0;
+               /* INDENT-S.  */
+               if (line_prefix != NULL)
+                 {
+                   ostream_write_str (stream, line_prefix);
+                   currcol = strlen (line_prefix);
+                 }
+               begin_css_class (stream, css_class);
+               if (indent)
+                 {
+                   ostream_write_mem (stream, "        ", 8 - (currcol & 7));
+                   currcol = (currcol + 8) & ~7;
+                 }
+               begin_css_class (stream, class_string);
+               ostream_write_str (stream, "\"");
+               begin_css_class (stream, class_text);
+             }
+           /* Change currattr so that it matches attributes[i].  */
+           if (attributes[i] != currattr)
+             {
+               /* class_escape_sequence occurs inside class_format_directive
+                  and class_invalid_format_directive, so clear it first.  */
+               if (currattr & ATTR_ESCAPE_SEQUENCE)
+                 {
+                   end_css_class (stream, class_escape_sequence);
+                   currattr &= ~ATTR_ESCAPE_SEQUENCE;
+                 }
+               if (~attributes[i] & currattr & ATTR_FORMAT_DIRECTIVE)
+                 {
+                   end_css_class (stream, class_format_directive);
+                   currattr &= ~ATTR_FORMAT_DIRECTIVE;
+                 }
+               else if (~attributes[i] & currattr & ATTR_INVALID_FORMAT_DIRECTIVE)
+                 {
+                   end_css_class (stream, class_invalid_format_directive);
+                   currattr &= ~ATTR_INVALID_FORMAT_DIRECTIVE;
+                 }
+               if (attributes[i] & ~currattr & ATTR_FORMAT_DIRECTIVE)
+                 {
+                   begin_css_class (stream, class_format_directive);
+                   currattr |= ATTR_FORMAT_DIRECTIVE;
+                 }
+               else if (attributes[i] & ~currattr & ATTR_INVALID_FORMAT_DIRECTIVE)
+                 {
+                   begin_css_class (stream, class_invalid_format_directive);
+                   currattr |= ATTR_INVALID_FORMAT_DIRECTIVE;
+                 }
+               /* class_escape_sequence occurs inside class_format_directive
+                  and class_invalid_format_directive, so set it last.  */
+               if (attributes[i] & ~currattr & ATTR_ESCAPE_SEQUENCE)
+                 {
+                   begin_css_class (stream, class_escape_sequence);
+                   currattr |= ATTR_ESCAPE_SEQUENCE;
+                 }
+             }
+           ostream_write_mem (stream, &portion[i], 1);
+         }
+
+       /* Change currattr so that it becomes 0.  */
+       if (currattr & ATTR_ESCAPE_SEQUENCE)
+         {
+           end_css_class (stream, class_escape_sequence);
+           currattr &= ~ATTR_ESCAPE_SEQUENCE;
+         }
+       if (currattr & ATTR_FORMAT_DIRECTIVE)
+         {
+           end_css_class (stream, class_format_directive);
+           currattr &= ~ATTR_FORMAT_DIRECTIVE;
+         }
+       else if (currattr & ATTR_INVALID_FORMAT_DIRECTIVE)
+         {
+           end_css_class (stream, class_invalid_format_directive);
+           currattr &= ~ATTR_INVALID_FORMAT_DIRECTIVE;
+         }
+       if (!(currattr == 0))
+         abort ();
+
+       end_css_class (stream, class_text);
+       ostream_write_str (stream, "\"");
+       end_css_class (stream, class_string);
+       end_css_class (stream, css_class);
+       ostream_write_str (stream, "\n");
+      }
 
       free (linebreaks);
+      free (attributes);
       free (overrides);
       free (portion);
 
@@ -810,6 +1119,9 @@ internationalized messages should not contain the `\\%c' escape sequence"),
     }
   while (*s);
 
+  if (fmtdir != NULL)
+    free (fmtdir);
+
 #if HAVE_ICONV
   if (conv != (iconv_t)(-1))
     iconv_close (conv);
@@ -821,7 +1133,11 @@ static void
 print_blank_line (ostream_t stream)
 {
   if (uniforum)
-    ostream_write_str (stream, "#\n");
+    {
+      begin_css_class (stream, class_comment);
+      ostream_write_str (stream, "#\n");
+      end_css_class (stream, class_comment);
+    }
   else
     ostream_write_str (stream, "\n");
 }
@@ -842,6 +1158,17 @@ message_print (const message_ty *mp, ostream_t stream,
                     || mp->comment->item[0][0] != '\0'))
     print_blank_line (stream);
 
+  if (is_header (mp))
+    begin_css_class (stream, class_header);
+  else if (mp->msgstr[0] == '\0')
+    begin_css_class (stream, class_untranslated);
+  else if (mp->is_fuzzy)
+    begin_css_class (stream, class_fuzzy);
+  else
+    begin_css_class (stream, class_translated);
+
+  begin_css_class (stream, class_comment);
+
   /* Print translator comment if available.  */
   message_print_comment (mp, stream);
 
@@ -858,20 +1185,24 @@ message_print (const message_ty *mp, ostream_t stream,
 
   /* Print the previous msgid.  This helps the translator when the msgid has
      only slightly changed.  */
+  begin_css_class (stream, class_previous_comment);
   if (mp->prev_msgctxt != NULL)
-    wrap (mp, stream, "#| ", 0, "msgctxt", mp->prev_msgctxt, mp->do_wrap,
-         page_width, charset);
+    wrap (mp, stream, "#| ", 0, class_previous, "msgctxt", mp->prev_msgctxt,
+         mp->do_wrap, page_width, charset);
   if (mp->prev_msgid != NULL)
-    wrap (mp, stream, "#| ", 0, "msgid", mp->prev_msgid, mp->do_wrap,
-         page_width, charset);
-  if (mp->prev_msgid_plural != NULL)
-    wrap (mp, stream, "#| ", 0, "msgid_plural", mp->prev_msgid_plural,
+    wrap (mp, stream, "#| ", 0, class_previous, "msgid", mp->prev_msgid,
          mp->do_wrap, page_width, charset);
+  if (mp->prev_msgid_plural != NULL)
+    wrap (mp, stream, "#| ", 0, class_previous, "msgid_plural",
+         mp->prev_msgid_plural, mp->do_wrap, page_width, charset);
+  end_css_class (stream, class_previous_comment);
   extra_indent = (mp->prev_msgctxt != NULL || mp->prev_msgid != NULL
                  || mp->prev_msgid_plural != NULL
                  ? 3
                  : 0);
 
+  end_css_class (stream, class_comment);
+
   /* Print each of the message components.  Wrap them nicely so they
      are as readable as possible.  If there is no recorded msgstr for
      this domain, emit an empty string.  */
@@ -900,17 +1231,17 @@ different from yours. Consider using a pure ASCII msgid instead.\n\
       free (warning_message);
     }
   if (mp->msgctxt != NULL)
-    wrap (mp, stream, NULL, extra_indent, "msgctxt", mp->msgctxt, mp->do_wrap,
-         page_width, charset);
-  wrap (mp, stream, NULL, extra_indent, "msgid", mp->msgid, mp->do_wrap,
-       page_width, charset);
-  if (mp->msgid_plural != NULL)
-    wrap (mp, stream, NULL, extra_indent, "msgid_plural", mp->msgid_plural,
+    wrap (mp, stream, NULL, extra_indent, class_msgid, "msgctxt", mp->msgctxt,
          mp->do_wrap, page_width, charset);
+  wrap (mp, stream, NULL, extra_indent, class_msgid, "msgid", mp->msgid,
+       mp->do_wrap, page_width, charset);
+  if (mp->msgid_plural != NULL)
+    wrap (mp, stream, NULL, extra_indent, class_msgid, "msgid_plural",
+         mp->msgid_plural, mp->do_wrap, page_width, charset);
 
   if (mp->msgid_plural == NULL)
-    wrap (mp, stream, NULL, extra_indent, "msgstr", mp->msgstr, mp->do_wrap,
-         page_width, charset);
+    wrap (mp, stream, NULL, extra_indent, class_msgstr, "msgstr", mp->msgstr,
+         mp->do_wrap, page_width, charset);
   else
     {
       char prefix_buf[20];
@@ -922,10 +1253,19 @@ different from yours. Consider using a pure ASCII msgid instead.\n\
           p += strlen (p) + 1, i++)
        {
          sprintf (prefix_buf, "msgstr[%u]", i);
-         wrap (mp, stream, NULL, extra_indent, prefix_buf, p, mp->do_wrap,
-               page_width, charset);
+         wrap (mp, stream, NULL, extra_indent, class_msgstr, prefix_buf, p,
+               mp->do_wrap, page_width, charset);
        }
     }
+
+  if (is_header (mp))
+    end_css_class (stream, class_header);
+  else if (mp->msgstr[0] == '\0')
+    end_css_class (stream, class_untranslated);
+  else if (mp->is_fuzzy)
+    end_css_class (stream, class_fuzzy);
+  else
+    end_css_class (stream, class_translated);
 }
 
 
@@ -944,6 +1284,10 @@ message_print_obsolete (const message_ty *mp, ostream_t stream,
   if (blank_line)
     print_blank_line (stream);
 
+  begin_css_class (stream, class_obsolete);
+
+  begin_css_class (stream, class_comment);
+
   /* Print translator comment if available.  */
   message_print_comment (mp, stream);
 
@@ -971,20 +1315,24 @@ message_print_obsolete (const message_ty *mp, ostream_t stream,
 
   /* Print the previous msgid.  This helps the translator when the msgid has
      only slightly changed.  */
+  begin_css_class (stream, class_previous_comment);
   if (mp->prev_msgctxt != NULL)
-    wrap (mp, stream, "#~| ", 0, "msgctxt", mp->prev_msgctxt, mp->do_wrap,
-         page_width, charset);
+    wrap (mp, stream, "#~| ", 0, class_previous, "msgctxt", mp->prev_msgctxt,
+         mp->do_wrap, page_width, charset);
   if (mp->prev_msgid != NULL)
-    wrap (mp, stream, "#~| ", 0, "msgid", mp->prev_msgid, mp->do_wrap,
-         page_width, charset);
-  if (mp->prev_msgid_plural != NULL)
-    wrap (mp, stream, "#~| ", 0, "msgid_plural", mp->prev_msgid_plural,
+    wrap (mp, stream, "#~| ", 0, class_previous, "msgid", mp->prev_msgid,
          mp->do_wrap, page_width, charset);
+  if (mp->prev_msgid_plural != NULL)
+    wrap (mp, stream, "#~| ", 0, class_previous, "msgid_plural",
+         mp->prev_msgid_plural, mp->do_wrap, page_width, charset);
+  end_css_class (stream, class_previous_comment);
   extra_indent = (mp->prev_msgctxt != NULL || mp->prev_msgid != NULL
                  || mp->prev_msgid_plural != NULL
                  ? 1
                  : 0);
 
+  end_css_class (stream, class_comment);
+
   /* Print each of the message components.  Wrap them nicely so they
      are as readable as possible.  */
   if (mp->msgctxt != NULL && !is_ascii_string (mp->msgctxt)
@@ -1012,17 +1360,17 @@ different from yours. Consider using a pure ASCII msgid instead.\n\
       free (warning_message);
     }
   if (mp->msgctxt != NULL)
-    wrap (mp, stream, "#~ ", extra_indent, "msgctxt", mp->msgctxt, mp->do_wrap,
-         page_width, charset);
-  wrap (mp, stream, "#~ ", extra_indent, "msgid", mp->msgid, mp->do_wrap,
-       page_width, charset);
-  if (mp->msgid_plural != NULL)
-    wrap (mp, stream, "#~ ", extra_indent, "msgid_plural", mp->msgid_plural,
+    wrap (mp, stream, "#~ ", extra_indent, class_msgid, "msgctxt", mp->msgctxt,
          mp->do_wrap, page_width, charset);
+  wrap (mp, stream, "#~ ", extra_indent, class_msgid, "msgid", mp->msgid,
+       mp->do_wrap, page_width, charset);
+  if (mp->msgid_plural != NULL)
+    wrap (mp, stream, "#~ ", extra_indent, class_msgid, "msgid_plural",
+         mp->msgid_plural, mp->do_wrap, page_width, charset);
 
   if (mp->msgid_plural == NULL)
-    wrap (mp, stream, "#~ ", extra_indent, "msgstr", mp->msgstr, mp->do_wrap,
-         page_width, charset);
+    wrap (mp, stream, "#~ ", extra_indent, class_msgstr, "msgstr", mp->msgstr,
+         mp->do_wrap, page_width, charset);
   else
     {
       char prefix_buf[20];
@@ -1034,10 +1382,12 @@ different from yours. Consider using a pure ASCII msgid instead.\n\
           p += strlen (p) + 1, i++)
        {
          sprintf (prefix_buf, "msgstr[%u]", i);
-         wrap (mp, stream, "#~ ", extra_indent, prefix_buf, p, mp->do_wrap,
-               page_width, charset);
+         wrap (mp, stream, "#~ ", extra_indent, class_msgstr, prefix_buf, p,
+               mp->do_wrap, page_width, charset);
        }
     }
+
+  end_css_class (stream, class_obsolete);
 }
 
 
@@ -1064,9 +1414,18 @@ msgdomain_list_print_po (msgdomain_list_ty *mdlp, ostream_t stream,
        {
          if (blank_line)
            print_blank_line (stream);
-         ostream_write_str (stream, "domain \"");
+         begin_css_class (stream, class_keyword);
+         ostream_write_str (stream, "domain");
+         end_css_class (stream, class_keyword);
+         ostream_write_str (stream, " ");
+         begin_css_class (stream, class_string);
+         ostream_write_str (stream, "\"");
+         begin_css_class (stream, class_text);
          ostream_write_str (stream, mdlp->item[k]->domain);
-         ostream_write_str (stream, "\"\n");
+         end_css_class (stream, class_text);
+         ostream_write_str (stream, "\"");
+         end_css_class (stream, class_string);
+         ostream_write_str (stream, "\n");
          blank_line = true;
        }
 
@@ -1134,6 +1493,7 @@ const struct catalog_output_format output_format_po =
 {
   msgdomain_list_print_po,             /* print */
   false,                               /* requires_utf8 */
+  true,                                        /* supports_color */
   true,                                        /* supports_multiple_domains */
   true,                                        /* supports_contexts */
   true,                                        /* supports_plurals */
index 50df33ab9df1711ed86bd8e7ffceaadd92bee16e..f5169fff82b84d2c8f7b29490cf192ed3c71f30c 100644 (file)
@@ -296,6 +296,7 @@ const struct catalog_output_format output_format_properties =
 {
   msgdomain_list_print_properties,     /* print */
   true,                                        /* requires_utf8 */
+  false,                               /* supports_color */
   false,                               /* supports_multiple_domains */
   false,                               /* supports_contexts */
   false,                               /* supports_plurals */
index 283828bef2fbf6a9f99d94caf864a65dadb06b71..5b2e301e981fbf5a7e180c9de8efdbe349f4fed2 100644 (file)
@@ -316,6 +316,7 @@ const struct catalog_output_format output_format_stringtable =
 {
   msgdomain_list_print_stringtable,    /* print */
   true,                                        /* requires_utf8 */
+  false,                               /* supports_color */
   false,                               /* supports_multiple_domains */
   false,                               /* supports_contexts */
   false,                               /* supports_plurals */