]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
msggrep: Add new options -W and -S.
authorBruno Haible <bruno@clisp.org>
Thu, 3 Jul 2025 06:48:44 +0000 (08:48 +0200)
committerBruno Haible <bruno@clisp.org>
Thu, 3 Jul 2025 06:48:44 +0000 (08:48 +0200)
Reported at <https://savannah.gnu.org/bugs/?40489>.

* gettext-tools/src/msggrep.c (workflow_flags, sticky_flags): New variables.
(main): Initialize them. Handle the options -W and -S.
(usage): Document the -W and -S options.
(is_message_selected_no_invert): Return true if one of the specified workflow
flags or one of the specified sticky flags is set.
* gettext-tools/tests/msggrep-12: New file.
* gettext-tools/tests/Makefile.am (TESTS): Add it.
* gettext-tools/doc/msggrep.texi: Document the -W and -S options.
* NEWS: Mention the change.

NEWS
gettext-tools/doc/msggrep.texi
gettext-tools/src/msggrep.c
gettext-tools/tests/Makefile.am
gettext-tools/tests/msggrep-12 [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index be95b6ba45d46e38de10c532c312ce8d640dbcce..4cbcd9ba4f23769c5e0e8ce715f9dd329d65a2f5 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -22,6 +22,11 @@ Version 0.26 - July 2025
     - xgettext now recognizes the \c, \u, and \U escape sequences in dollar-
       single-quoted strings $'...'.
 
+# Improvements for translators:
+  * msggrep:
+    - msggrep accepts two new options -W/--workflow-flags and -S/--sticky-flags
+      that allow to select only messages that have a specified flag.
+
 # Bug fixes:
   - The AM_GNU_GETTEXT macro now rejects the dysfunctional gettext() function
     in libc of Solaris 11.[0-3], Solaris OpenIndiana, and Solaris OmniOS.
index 1673d29d370494e30101df6669c169a891753ecb..c77ec97e7edeb6fca0e0b984c73a353f397e2f6c 100644 (file)
@@ -50,6 +50,7 @@ or if it is @samp{-}.
   [-N @var{sourcefile}]... [-M @var{domainname}]...
   [-J @var{msgctxt-pattern}] [-K @var{msgid-pattern}] [-T @var{msgstr-pattern}]
   [-C @var{comment-pattern}] [-X @var{extracted-comment-pattern}]
+  [-W @var{workflow-flag}] [-S @var{sticky-flag}]
 @end example
 
 A message is selected if
@@ -65,7 +66,9 @@ A message is selected if
 @item or if @samp{-C} is given and the translator's comment matches
 @var{comment-pattern},
 @item or if @samp{-X} is given and the extracted comment matches
-@var{extracted-comment-pattern}.
+@var{extracted-comment-pattern},
+@item or if @samp{-W} is given and its flags contain @var{workflow-flag},
+@item or if @samp{-S} is given and its flags contain @var{sticky-flag}.
 @end itemize
 
 When more than one selection criterion is specified, the set of selected
@@ -152,6 +155,20 @@ Obtain @var{pattern} from @var{file}.
 @opindex --ignore-case@r{, @code{msggrep} option}
 Ignore case distinctions.
 
+@item -W @var{workflow-flag}
+@itemx --workflow-flag=@var{workflow-flag}
+@opindex -W@r{, @code{msggrep} option}
+@opindex --workflow-flag@r{, @code{msggrep} option}
+Select messages with @var{workflow-flag}.
+Workflow flags are defined in @ref{Workflow flags}.
+
+@item -S @var{sticky-flag}
+@itemx --sticky-flag=@var{sticky-flag}
+@opindex -S@r{, @code{msggrep} option}
+@opindex --sticky-flag@r{, @code{msggrep} option}
+Select messages with @var{sticky-flag}.
+Sticky flags are defined in @ref{Sticky flags}.
+
 @item -v
 @itemx --invert-match
 @opindex -v@r{, @code{msggrep} option}
index b31b6f7574c9921efdef5dc984c46a6564d54cd8..94054dcd3288490eca0aedf7dd856a206fed775f 100644 (file)
@@ -91,6 +91,12 @@ struct grep_task {
 };
 static struct grep_task grep_task[5];
 
+/* Selected workflow flags.  */
+static string_list_ty *workflow_flags;
+
+/* Selected sticky flags.  */
+static string_list_ty *sticky_flags;
+
 
 /* Forward declaration of local functions.  */
 _GL_NORETURN_FUNC static void no_pass (int opt);
@@ -138,6 +144,8 @@ main (int argc, char **argv)
   grep_pass = -1;
   location_files = string_list_alloc ();
   domain_names = string_list_alloc ();
+  workflow_flags = string_list_alloc ();
+  sticky_flags = string_list_alloc ();
 
   for (i = 0; i < 5; i++)
     {
@@ -183,12 +191,14 @@ main (int argc, char **argv)
     { "regexp",             'e',            required_argument },
     { "sort-by-file",       CHAR_MAX + 4,   no_argument       },
     { "sort-output",        CHAR_MAX + 5,   no_argument       },
+    { "sticky-flag",        'S',            required_argument },
     { "strict",             CHAR_MAX + 12,  no_argument       },
     { "stringtable-input",  CHAR_MAX + 7,   no_argument       },
     { "stringtable-output", CHAR_MAX + 8,   no_argument       },
     { "style",              CHAR_MAX + 10,  required_argument },
     { "version",            'V',            no_argument       },
     { "width",              'w',            required_argument },
+    { "workflow-flag",      'W',            required_argument },
   };
   END_ALLOW_OMITTING_FIELD_INITIALIZERS
   start_options (argc, argv, options, MOVE_OPTIONS_FIRST, 0);
@@ -331,6 +341,10 @@ main (int argc, char **argv)
         message_print_style_uniforum ();
         break;
 
+      case 'S':
+        string_list_append (sticky_flags, optarg);
+        break;
+
       case 'T':
         grep_pass = 2;
         break;
@@ -353,6 +367,10 @@ main (int argc, char **argv)
         }
         break;
 
+      case 'W':
+        string_list_append (workflow_flags, optarg);
+        break;
+
       case 'X':
         grep_pass = 4;
         break;
@@ -547,13 +565,16 @@ Message selection:\n\
   [-N SOURCEFILE]... [-M DOMAINNAME]...\n\
   [-J MSGCTXT-PATTERN] [-K MSGID-PATTERN] [-T MSGSTR-PATTERN]\n\
   [-C COMMENT-PATTERN] [-X EXTRACTED-COMMENT-PATTERN]\n\
+  [-W WORKFLOW-FLAG] [-S STICKY-FLAG]\n\
 A message is selected if it comes from one of the specified source files,\n\
 or if it comes from one of the specified domains,\n\
 or if -J is given and its context (msgctxt) matches MSGCTXT-PATTERN,\n\
 or if -K is given and its key (msgid or msgid_plural) matches MSGID-PATTERN,\n\
 or if -T is given and its translation (msgstr) matches MSGSTR-PATTERN,\n\
 or if -C is given and the translator's comment matches COMMENT-PATTERN,\n\
-or if -X is given and the extracted comment matches EXTRACTED-COMMENT-PATTERN.\n\
+or if -X is given and the extracted comment matches EXTRACTED-COMMENT-PATTERN,\n\
+or if -W is given and its flags contain WORKFLOW-FLAG,\n\
+or if -S is given and its flags contain STICKY-FLAG.\n\
 \n\
 When more than one selection criterion is specified, the set of selected\n\
 messages is the union of the selected messages of each criterion.\n\
@@ -576,6 +597,8 @@ expressions if -E is given, or fixed strings if -F is given.\n\
   -e, --regexp=PATTERN        use PATTERN as a regular expression\n\
   -f, --file=FILE             obtain PATTERN from FILE\n\
   -i, --ignore-case           ignore case distinctions\n\
+  -W, --workflow-flag=FLAG    select messages with FLAG\n\
+  -S, --sticky-flag=FLAG      select messages with FLAG\n\
   -v, --invert-match          output only the messages that do not match any\n\
                               selection criterion\n\
 "));
@@ -697,6 +720,41 @@ is_message_selected_no_invert (const message_ty *mp)
     if (filename_list_match (location_files, mp->filepos[i].file_name))
       return true;
 
+  /* Test whether one of the workflow flags is selected.  */
+  if (mp->is_fuzzy && string_list_member (workflow_flags, "fuzzy"))
+    return true;
+
+  /* Test whether one of the sticky flags is selected.  */
+  /* Recognize flag "[no-]wrap".  */
+  if ((mp->do_wrap == yes && string_list_member (sticky_flags, "wrap"))
+      || (mp->do_wrap == no && string_list_member (sticky_flags, "no-wrap")))
+    return true;
+  /* Recognize flag "[no-]<language>-format".  */
+  for (i = 0; i < sticky_flags->nitems; i++)
+    {
+      const char *flag = sticky_flags->item[i];
+      size_t flag_len = strlen (flag);
+      if (flag_len >= 7 && memcmp (flag + flag_len - 7, "-format", 7) == 0)
+        {
+          flag_len -= 7;
+          bool has_no_prefix = (flag_len >= 3 && memcmp (flag, "no-", 3) == 0);
+          if (has_no_prefix)
+            {
+              flag += 3;
+              flag_len -= 3;
+            }
+          size_t j;
+          for (j = 0; j < NFORMATS; j++)
+            if (strlen (format_language[j]) == flag_len
+                && memcmp (format_language[j], flag, flag_len) == 0)
+              {
+                /* The value of the flag is stored in mp->is_format[j].  */
+                if (mp->is_format[j] == (has_no_prefix ? no : yes))
+                  return true;
+              }
+        }
+    }
+
   /* Test msgctxt using the --msgctxt arguments.  */
   if (mp->msgctxt != NULL
       && is_string_selected (0, mp->msgctxt, strlen (mp->msgctxt)))
index 54127ba9d450713abb05f5e1f4775e8d9be9ca00..6f02cc69c1390391f18ade94972c2aaa9ad793d7 100644 (file)
@@ -59,7 +59,7 @@ TESTS = gettext-1 gettext-2 \
        msgfmt-desktop-1 msgfmt-desktop-2 msgfmt-desktop-3 msgfmt-desktop-4 \
        msgfmt-xml-1 msgfmt-xml-2 msgfmt-xml-3 msgfmt-xml-4 msgfmt-xml-5 \
        msggrep-1 msggrep-2 msggrep-3 msggrep-4 msggrep-5 msggrep-6 msggrep-7 \
-       msggrep-8 msggrep-9 msggrep-10 msggrep-11 \
+       msggrep-8 msggrep-9 msggrep-10 msggrep-11 msggrep-12 \
        msginit-1 msginit-2 msginit-3 msginit-4 msginit-5 \
        msgmerge-1 msgmerge-2 msgmerge-3 msgmerge-4 msgmerge-5 msgmerge-6 \
        msgmerge-7 msgmerge-8 msgmerge-9 msgmerge-10 msgmerge-11 msgmerge-12 \
diff --git a/gettext-tools/tests/msggrep-12 b/gettext-tools/tests/msggrep-12
new file mode 100755 (executable)
index 0000000..c2ed170
--- /dev/null
@@ -0,0 +1,311 @@
+#! /bin/sh
+. "${srcdir=.}/init.sh"; path_prepend_ . ../src
+
+# Test --workflow-flag and --sticky-flag options.
+
+cat <<\EOF > mg-test12.po
+# German translations for GNU gettext package.
+# Copyright (C) 1995, 1996, 1997, 2001 Free Software Foundation, Inc.
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU gettext 0.11-pre1\n"
+"POT-Creation-Date: 2001-12-08 20:33+0100\n"
+"PO-Revision-Date: 2001-11-04 12:25+0100\n"
+"Last-Translator: Karl Eichwalder <ke@suse.de>\n"
+"Language-Team: German <de@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: argmatch.c:141
+#, c-format
+msgid "invalid argument `%s' for `%s'"
+msgstr "ungültiges Argument »%s« für »%s«"
+
+#: argmatch.c:142
+#, c-format
+msgid "ambiguous argument `%s' for `%s'"
+msgstr "mehrdeutiges Argument »%s« für »%s«"
+
+#: argmatch.c:162
+msgid "Valid arguments are:"
+msgstr "Gültige Argumente sind:"
+
+#: copy-file.c:60
+#, c-format
+msgid "error while opening \"%s\" for reading"
+msgstr "Öffnen der Datei »%s« zum Lesen fehlgeschlagen"
+
+#: copy-file.c:67
+#, c-format
+msgid "cannot open backup file \"%s\" for writing"
+msgstr "Öffnen der Sicherungsdatei »%s« zum Schreiben fehlgeschlagen"
+
+#: copy-file.c:80
+#, c-format
+msgid "error reading \"%s\""
+msgstr "Fehler beim Lesen von »%s«"
+
+#: copy-file.c:86 copy-file.c:90
+#, c-format
+msgid "error writing \"%s\""
+msgstr "Fehler beim Schreiben von »%s«"
+
+#: copy-file.c:92
+#, fuzzy, c-format
+msgid "error after reading \"%s\""
+msgstr "Fehler nach dem Lesen von »%s«"
+
+#: error.c:115
+#, fuzzy
+msgid "Unknown system error"
+msgstr "Unbekannter Systemfehler"
+
+#: execute.c:170 execute.c:205 pipe-bidi.c:156 pipe-bidi.c:191 pipe-in.c:169
+#: pipe-in.c:205 pipe-out.c:169 pipe-out.c:205 wait-process.c:136
+#, c-format
+msgid "%s subprocess failed"
+msgstr "Subprozess %s fehlgeschlagen"
+
+#: getopt.c:691
+#, c-format
+msgid "%s: option `%s' is ambiguous\n"
+msgstr "%s: Option »%s« ist mehrdeutig\n"
+
+#: getopt.c:716
+#, c-format
+msgid "%s: option `--%s' doesn't allow an argument\n"
+msgstr "%s: Option »--%s« erwartet kein Argument\n"
+
+#: getopt.c:721
+#, c-format
+msgid "%s: option `%c%s' doesn't allow an argument\n"
+msgstr "%s: Option »%c%s« erwartet kein Argument\n"
+
+#: getopt.c:739 getopt.c:912
+#, c-format
+msgid "%s: option `%s' requires an argument\n"
+msgstr "%s: Option »%s« erwartet ein Argument\n"
+
+#: getopt.c:768
+#, c-format
+msgid "%s: unrecognized option `--%s'\n"
+msgstr "%s: unbekannte Option »--%s«\n"
+
+#: getopt.c:772
+#, c-format
+msgid "%s: unrecognized option `%c%s'\n"
+msgstr "%s: unbekannte Option »%c%s«\n"
+
+# Möchte mal gerne wissen, was der Unterschied zwischen
+# "unzulässig" und "ungültig" ist.
+# Übrigens ist im Englischen "illegal" falsch.
+#: getopt.c:798
+#, c-format
+msgid "%s: illegal option -- %c\n"
+msgstr "%s: unzulässige Option -- %c\n"
+
+#: getopt.c:801
+#, c-format
+msgid "%s: invalid option -- %c\n"
+msgstr "%s: ungültige Option -- %c\n"
+
+#: getopt.c:831 getopt.c:961
+#, c-format
+msgid "%s: option requires an argument -- %c\n"
+msgstr "%s: Option erwartet ein Argument -- %c\n"
+
+#: getopt.c:878
+#, c-format
+msgid "%s: option `-W %s' is ambiguous\n"
+msgstr "%s: Option »-W %s« ist mehrdeutig\n"
+
+#: getopt.c:896
+#, c-format
+msgid "%s: option `-W %s' doesn't allow an argument\n"
+msgstr "%s: Option »-W %s« erwartet kein Argument\n"
+
+#: javacomp.c:465
+msgid "Java compiler not found, try installing gcj or set $JAVAC"
+msgstr ""
+"Java-Compiler nicht gefunden; bitte »gcj« installieren oder $JAVAC setzen"
+
+#: javaexec.c:404
+#, fuzzy
+msgid "Java virtual machine not found, try installing gij or set $JAVA"
+msgstr ""
+"Virtuelle Java-Maschine nicht gefunden; bitte »gcj« installieren oder\n"
+"$JAVA setzen"
+
+#: obstack.c:474 xerror.c:75 xmalloc.c:56
+#, objc-format
+msgid "memory exhausted"
+msgstr "virtueller Speicher erschöpft"
+
+# Auch "Pipe" eindeutschen.
+#: pipe-bidi.c:119 pipe-bidi.c:121 pipe-in.c:136 pipe-out.c:136
+msgid "cannot create pipe"
+msgstr "Es ist nicht möglich, eine Pipe zu erzeugen"
+
+#: wait-process.c:117
+#, c-format
+msgid "%s subprocess"
+msgstr "Subprozess %s"
+
+#: wait-process.c:129
+#, c-format
+msgid "%s subprocess got fatal signal"
+msgstr "Subprozess %s hat ein fatales Signal erhalten"
+EOF
+
+: ${MSGGREP=msggrep}
+LC_MESSAGES=C LC_ALL= \
+${MSGGREP} -W fuzzy -S c-format -o mg-test12.tmp mg-test12.po >mg-test12.err 2>&1
+result=$?
+cat mg-test12.err | grep -v 'warning: Locale charset' | grep -v '^ '
+test $result = 0 || { Exit 1; }
+LC_ALL=C tr -d '\r' < mg-test12.tmp > mg-test12.out || Exit 1
+
+cat <<\EOF > mg-test12.ok
+# German translations for GNU gettext package.
+# Copyright (C) 1995, 1996, 1997, 2001 Free Software Foundation, Inc.
+msgid ""
+msgstr ""
+"Project-Id-Version: GNU gettext 0.11-pre1\n"
+"POT-Creation-Date: 2001-12-08 20:33+0100\n"
+"PO-Revision-Date: 2001-11-04 12:25+0100\n"
+"Last-Translator: Karl Eichwalder <ke@suse.de>\n"
+"Language-Team: German <de@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: argmatch.c:141
+#, c-format
+msgid "invalid argument `%s' for `%s'"
+msgstr "ungültiges Argument »%s« für »%s«"
+
+#: argmatch.c:142
+#, c-format
+msgid "ambiguous argument `%s' for `%s'"
+msgstr "mehrdeutiges Argument »%s« für »%s«"
+
+#: copy-file.c:60
+#, c-format
+msgid "error while opening \"%s\" for reading"
+msgstr "Öffnen der Datei »%s« zum Lesen fehlgeschlagen"
+
+#: copy-file.c:67
+#, c-format
+msgid "cannot open backup file \"%s\" for writing"
+msgstr "Öffnen der Sicherungsdatei »%s« zum Schreiben fehlgeschlagen"
+
+#: copy-file.c:80
+#, c-format
+msgid "error reading \"%s\""
+msgstr "Fehler beim Lesen von »%s«"
+
+#: copy-file.c:86 copy-file.c:90
+#, c-format
+msgid "error writing \"%s\""
+msgstr "Fehler beim Schreiben von »%s«"
+
+#: copy-file.c:92
+#, fuzzy, c-format
+msgid "error after reading \"%s\""
+msgstr "Fehler nach dem Lesen von »%s«"
+
+#: error.c:115
+#, fuzzy
+msgid "Unknown system error"
+msgstr "Unbekannter Systemfehler"
+
+#: execute.c:170 execute.c:205 pipe-bidi.c:156 pipe-bidi.c:191 pipe-in.c:169
+#: pipe-in.c:205 pipe-out.c:169 pipe-out.c:205 wait-process.c:136
+#, c-format
+msgid "%s subprocess failed"
+msgstr "Subprozess %s fehlgeschlagen"
+
+#: getopt.c:691
+#, c-format
+msgid "%s: option `%s' is ambiguous\n"
+msgstr "%s: Option »%s« ist mehrdeutig\n"
+
+#: getopt.c:716
+#, c-format
+msgid "%s: option `--%s' doesn't allow an argument\n"
+msgstr "%s: Option »--%s« erwartet kein Argument\n"
+
+#: getopt.c:721
+#, c-format
+msgid "%s: option `%c%s' doesn't allow an argument\n"
+msgstr "%s: Option »%c%s« erwartet kein Argument\n"
+
+#: getopt.c:739 getopt.c:912
+#, c-format
+msgid "%s: option `%s' requires an argument\n"
+msgstr "%s: Option »%s« erwartet ein Argument\n"
+
+#: getopt.c:768
+#, c-format
+msgid "%s: unrecognized option `--%s'\n"
+msgstr "%s: unbekannte Option »--%s«\n"
+
+#: getopt.c:772
+#, c-format
+msgid "%s: unrecognized option `%c%s'\n"
+msgstr "%s: unbekannte Option »%c%s«\n"
+
+# Möchte mal gerne wissen, was der Unterschied zwischen
+# "unzulässig" und "ungültig" ist.
+# Übrigens ist im Englischen "illegal" falsch.
+#: getopt.c:798
+#, c-format
+msgid "%s: illegal option -- %c\n"
+msgstr "%s: unzulässige Option -- %c\n"
+
+#: getopt.c:801
+#, c-format
+msgid "%s: invalid option -- %c\n"
+msgstr "%s: ungültige Option -- %c\n"
+
+#: getopt.c:831 getopt.c:961
+#, c-format
+msgid "%s: option requires an argument -- %c\n"
+msgstr "%s: Option erwartet ein Argument -- %c\n"
+
+#: getopt.c:878
+#, c-format
+msgid "%s: option `-W %s' is ambiguous\n"
+msgstr "%s: Option »-W %s« ist mehrdeutig\n"
+
+#: getopt.c:896
+#, c-format
+msgid "%s: option `-W %s' doesn't allow an argument\n"
+msgstr "%s: Option »-W %s« erwartet kein Argument\n"
+
+#: javaexec.c:404
+#, fuzzy
+msgid "Java virtual machine not found, try installing gij or set $JAVA"
+msgstr ""
+"Virtuelle Java-Maschine nicht gefunden; bitte »gcj« installieren oder\n"
+"$JAVA setzen"
+
+#: wait-process.c:117
+#, c-format
+msgid "%s subprocess"
+msgstr "Subprozess %s"
+
+#: wait-process.c:129
+#, c-format
+msgid "%s subprocess got fatal signal"
+msgstr "Subprozess %s hat ein fatales Signal erhalten"
+EOF
+
+: ${DIFF=diff}
+${DIFF} mg-test12.ok mg-test12.out
+result=$?
+
+exit $result