From: Bruno Haible Date: Fri, 15 Dec 2006 12:57:56 +0000 (+0000) Subject: Add styling support to the PO output routines. X-Git-Tag: v0.17~585 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=64e43a52e9925e0552ff4dcdc471d9e4c9ab8282;p=thirdparty%2Fgettext.git Add styling support to the PO output routines. --- diff --git a/ChangeLog b/ChangeLog index 5f470b484..378b6afa4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2006-12-01 Bruno Haible + + * autogen.sh (GNULIB_MODULES_TOOLS_FOR_SRC): Add fd-ostream, + styled-ostream, html-styled-ostream, term-styled-ostream. + 2006-12-04 Bruno Haible * DEPENDENCIES: New file. diff --git a/autogen.sh b/autogen.sh index 62704e3de..e89ba7ed8 100755 --- a/autogen.sh +++ b/autogen.sh @@ -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 diff --git a/gettext-tools/src/ChangeLog b/gettext-tools/src/ChangeLog index 14f84e7b6..e59a22d33 100644 --- a/gettext-tools/src/ChangeLog +++ b/gettext-tools/src/ChangeLog @@ -1,3 +1,44 @@ +2006-12-01 Bruno Haible + + 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 * write-po.c (make_format_description_string, diff --git a/gettext-tools/src/FILES b/gettext-tools/src/FILES index 9340ade0b..49cf3faa3 100644 --- a/gettext-tools/src/FILES +++ b/gettext-tools/src/FILES @@ -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. diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am index 1cc0ca1bc..98c4c100a 100644 --- a/gettext-tools/src/Makefile.am +++ b/gettext-tools/src/Makefile.am @@ -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) diff --git a/gettext-tools/src/write-catalog.c b/gettext-tools/src/write-catalog.c index 4e12272d7..6c66918b3 100644 --- a/gettext-tools/src/write-catalog.c +++ b/gettext-tools/src/write-catalog.c @@ -23,11 +23,17 @@ #include "write-catalog.h" #include +#include #include #include #include #include +#include +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif + #include "ostream.h" #include "file-ostream.h" #include "fwriteerror.h" @@ -39,6 +45,23 @@ /* 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)); + } } } diff --git a/gettext-tools/src/write-catalog.h b/gettext-tools/src/write-catalog.h index 923302900..4d2e2dee4 100644 --- a/gettext-tools/src/write-catalog.h +++ b/gettext-tools/src/write-catalog.h @@ -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; diff --git a/gettext-tools/src/write-po.c b/gettext-tools/src/write-po.c index 801040a94..733375e7e 100644 --- a/gettext-tools/src/write-po.c +++ b/gettext-tools/src/write-po.c @@ -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 */ diff --git a/gettext-tools/src/write-properties.c b/gettext-tools/src/write-properties.c index 50df33ab9..f5169fff8 100644 --- a/gettext-tools/src/write-properties.c +++ b/gettext-tools/src/write-properties.c @@ -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 */ diff --git a/gettext-tools/src/write-stringtable.c b/gettext-tools/src/write-stringtable.c index 283828bef..5b2e301e9 100644 --- a/gettext-tools/src/write-stringtable.c +++ b/gettext-tools/src/write-stringtable.c @@ -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 */