From patchwork Sun Oct 5 16:34:24 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vivien Kraus X-Patchwork-Id: 121271 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 885743858C53 for ; Sun, 5 Oct 2025 16:38:34 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 885743858C53 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, secure) header.d=planete-kraus.eu header.i=@planete-kraus.eu header.a=rsa-sha1 header.s=albinoniA header.b=CT5HiwrD X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from planete-kraus.eu (planete-kraus.eu [89.234.140.182]) by sourceware.org (Postfix) with ESMTPS id 14E033858D2A for ; Sun, 5 Oct 2025 16:36:14 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 14E033858D2A Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=planete-kraus.eu Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=planete-kraus.eu ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 14E033858D2A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=89.234.140.182 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682175; cv=none; b=YbAmYmoHjHzw8d1UUKBqFwczh7bKzrYc9h73gKtq+K9jmfWYeKkyK/lu3zGr+CsODU9isjbv8AflmaINmTh8i+2k1U2cthMrkwnOPOGB0tdDMP+1zozllDBlm5DjyLlvP2h1zuPEMtDHJBFRfme6GdLme9TSzHE7qLJAihQkpa8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682175; c=relaxed/simple; bh=3KwwKc+qeR0RDE6pGj1351Kf4xnee7/4Ez80yqR5+Fw=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=pJElR1SPGbtFWiNmjwc+1v6kw4qECCc5N1gfwsuW623/pHUSjXop2aLhy/NKiafdapZKiZqMGQUbaWftcYYkbhs6O5RQpuQ0lXtL4+1w5YkQpI9dDApdtpxAH5SwngjhiF1Cynrfj7QuCsgBOLJhQ8CJ6q0XigFOdvcpyqJl6+I= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 14E033858D2A Received: from planete-kraus.eu (localhost [127.0.0.1]) by planete-kraus.eu (OpenSMTPD) with ESMTP id 136d72b3 for ; Sun, 5 Oct 2025 16:36:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=planete-kraus.eu; h=from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; s=albinoniA; bh=7/pBVRM HQiB1ofYvQrHuwzHFpD0=; b=CT5HiwrDaHEbkt45u/lc0FOE7OpUVaoXQL6LjhL KnOuGFlQ8rINNjA+CJmDTxgNjsyViES394h4WuE5Fr2Wqv75yZbkLYNHb3DRA0kV 5+IoU0C/+m0f4047OD8MYOY45OPlyD5fSCAaXGeZiycJ8/P7EjQqXdYRmL/+zVzn YpsyLBTgfvFHLMsmGMboSXvi7kZCnz5Q0H3wxbXHrUhL7fJbpMeTzZfNUrBhfvQq ybR7YtusfszolgHI+WzDDLGG2FdwyqO7PtfqodndrnOOBrLh4JHwxgl32QJtKu0k ZA6J/6/BNKd8jh4V/p5wkF4f/+0+9wVaLh//LbQggDD4ixQ== Received: by planete-kraus.eu (OpenSMTPD) with ESMTPSA id 295138d1 (TLSv1.3:TLS_CHACHA20_POLY1305_SHA256:256:NO); Sun, 5 Oct 2025 16:36:05 +0000 (UTC) From: Vivien Kraus To: libc-alpha@sourceware.org Cc: Vivien Kraus Subject: [PATCH v12 1/9] posix: allow getopt_long to match translated option names Date: Sun, 5 Oct 2025 18:34:24 +0200 Message-Id: <4444a507d4868091960e36cf33245b264166e9d8.1759681924.git.vivien@planete-kraus.eu> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-12.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org It is possible to support translated long option names in a program by adding new option records with the translated names in the options array. However, it is significant work for all packages. With this change, getopt will try and match the untranslated options names, then the translated option names if not found. Abbreviations will only match the untranslated names. Another argument to the _getopt_internal{_r} is added to avoid linking to gettext from the posix version of getopt. --- manual/getopt.texi | 25 ++++++++-- posix/Makefile | 13 +++++ posix/getopt.c | 97 ++++++++++++++++++++++++++++++------ posix/getopt1.c | 11 ++-- posix/getopt_int.h | 9 +++- posix/tstgetoptl.c | 119 ++++++++++++++++++++++++++++++++++++++++++++ posix/tstgetoptl.po | 25 ++++++++++ 7 files changed, 272 insertions(+), 27 deletions(-) create mode 100644 posix/tstgetoptl.c create mode 100644 posix/tstgetoptl.po diff --git a/manual/getopt.texi b/manual/getopt.texi index 79a942307c..da9df5225a 100644 --- a/manual/getopt.texi +++ b/manual/getopt.texi @@ -202,6 +202,15 @@ declared in @file{getopt.h}, not @file{unistd.h}. You should make every program accept long options if it uses any options, for this takes little extra work and helps beginners remember how to use the program. +Both long option names and their translations provided by the program +for the user's current locale are recognized. This helps users of +your program who do not speak English understand the meaning of the +options, and it does not break the function of the program in scripts +if the untranslated option names are used. If international +communication involves the invocation of your program, the program +users should be encouraged to use untranslated option names, or +publish the locale used for this invocation. + @deftp {Data Type} {struct option} @standards{GNU, getopt.h} This structure describes a single long option name for the sake of @@ -213,7 +222,9 @@ The @code{struct option} structure has these fields: @table @code @item const char *name -This field is the name of the option. It is a string. +This field is the name of the option. It is a string. In order for +@command{getopt_long} to accept either the long option name or its +translated form, you should mark this string for translation. @item int has_arg This field says whether the option takes an argument. It is an integer, @@ -248,10 +259,14 @@ When @code{getopt_long} encounters a short option, it does the same thing that @code{getopt} would do: it returns the character code for the option, and stores the option's argument (if it has one) in @code{optarg}. -When @code{getopt_long} encounters a long option, it takes actions based -on the @code{flag} and @code{val} fields of the definition of that -option. The option name may be abbreviated as long as the abbreviation is -unique. +When @code{getopt_long} encounters a long option or its translation in +the current textdomain, it takes actions based on the @code{flag} and +@code{val} fields of the definition of that option. The english name +of the option name may be abbreviated as long as the abbreviation is +unique. No abbreviation of the translated option name is recognized. +Since the untranslated option names have precedence over the +translated option names, it is not possible to hide or divert an +option with a translation. If @code{flag} is a null pointer, then @code{getopt_long} returns the contents of @code{val} to indicate which option it found. You should diff --git a/posix/Makefile b/posix/Makefile index a36e5decd3..1c787f53fb 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -328,8 +328,20 @@ tests := \ tst-waitid \ tst-wordexp-nocmd \ tstgetopt \ + tstgetoptl \ # tests +# tstgetoptl uses a translation catalog for translated option names. +tstgetoptl_mo = $(objpfx)domaindir/en_GB/LC_MESSAGES/tstgetoptl.mo + +$(tstgetoptl_mo): tstgetoptl.po + $(make-target-directory) + msgfmt -o $@T $< + mv -f $@T $@ + +$(objpfx)tstgetoptl.out: $(tstgetoptl_mo) +CFLAGS-tstgetoptl.c += -DOBJPFX=\"$(objpfx)\" + # Test for the glob symbol version that was replaced in glibc 2.27. ifeq ($(have-GLIBC_2.26)$(build-shared),yesyes) tests += \ @@ -602,6 +614,7 @@ CFLAGS-fork.c = $(libio-mtsafe) $(config-cflags-wno-ignored-attributes) tstgetopt-ARGS = -a -b -cfoobar --required foobar --optional=bazbug \ --none random --col --color --colour +tstgetoptl-ARGS = $(tstgetopt-ARGS) tst-exec-ARGS = -- $(host-test-program-cmd) tst-exec-static-ARGS = $(tst-exec-ARGS) diff --git a/posix/getopt.c b/posix/getopt.c index 66b43850ee..9540821e1f 100644 --- a/posix/getopt.c +++ b/posix/getopt.c @@ -182,6 +182,21 @@ exchange (char **argv, struct _getopt_data *d) d->__last_nonopt = d->optind; } +/* Return 1 iff a translation for opt_name has been found and it + matches the substring from argument, length argument_length. +*/ +static const int +match_translated_option_name (char *(*translate) (const char *msgid), + const char *argument, size_t argument_length, + const char *opt_name) +{ + const char *translated = opt_name; + if (translate) + translated = translate (opt_name); + return (!strncmp (translated, argument, argument_length) + && argument_length == strlen (translated)); +} + /* Process the argument starting with d->__nextchar as a long option. d->optind should *not* have been advanced over this argument. @@ -194,7 +209,8 @@ static int process_long_option (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, struct _getopt_data *d, - int print_errors, const char *prefix) + int print_errors, const char *prefix, + char *(*translate) (const char *msgid)) { char *nameend; size_t namelen; @@ -202,6 +218,7 @@ process_long_option (int argc, char **argv, const char *optstring, const struct option *pfound = NULL; int n_options; int option_index; + const char *translated_option_name; for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; @@ -221,7 +238,23 @@ process_long_option (int argc, char **argv, const char *optstring, if (pfound == NULL) { - /* Didn't find an exact match, so look for abbreviations. */ + /* Didn't find an exact match, try with translated option + names. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (match_translated_option_name (translate, d->__nextchar, namelen, p->name)) + { + /* Exact match found with translation. */ + pfound = p; + option_index = option_index; + break; + } + } + + if (pfound == NULL) + { + /* Didn't find an exact match with translations, so look for + abbreviations, but only for the option name in the C + locale. */ unsigned char *ambig_set = NULL; int ambig_malloced = 0; int ambig_fallback = 0; @@ -332,6 +365,7 @@ process_long_option (int argc, char **argv, const char *optstring, /* We have found a matching long option. Consume it. */ d->optind++; d->__nextchar = NULL; + translated_option_name = translate (pfound->name); if (*nameend) { /* Don't test has_arg with >, because some C compilers don't @@ -341,10 +375,23 @@ process_long_option (int argc, char **argv, const char *optstring, else { if (print_errors) - fprintf (stderr, - _("%s: option '%s%s' doesn't allow an argument\n"), - argv[0], prefix, pfound->name); - + { + if (strcmp (translated_option_name, pfound->name) != 0) + { + /* Print both names of the option. */ + fprintf (stderr, + _("%s: option '%s%s' / '%s%s' doesn't allow an argument\n"), + argv[0], prefix, translated_option_name, prefix, pfound->name); + } + else + { + /* Either the option name is not translated, or its + translation is the same as the option name. */ + fprintf (stderr, + _("%s: option '%s%s' doesn't allow an argument\n"), + argv[0], prefix, pfound->name); + } + } d->optopt = pfound->val; return '?'; } @@ -356,9 +403,22 @@ process_long_option (int argc, char **argv, const char *optstring, else { if (print_errors) - fprintf (stderr, - _("%s: option '%s%s' requires an argument\n"), - argv[0], prefix, pfound->name); + { + /* Same dichotomy as when the option does not allow an + argument. */ + if (strcmp (translated_option_name, pfound->name) != 0) + { + fprintf (stderr, + _("%s: option '%s%s' / '%s%s' requires an argument\n"), + argv[0], prefix, translated_option_name, prefix, pfound->name); + } + else + { + fprintf (stderr, + _("%s: option '%s%s' requires an argument\n"), + argv[0], prefix, pfound->name); + } + } d->optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; @@ -470,7 +530,8 @@ _getopt_initialize (_GL_UNUSED int argc, int _getopt_internal_r (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, - int long_only, struct _getopt_data *d, int posixly_correct) + int long_only, struct _getopt_data *d, int posixly_correct, + char *(*translate) (const char *msgid)) { int print_errors = d->opterr; @@ -573,7 +634,8 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, d->__nextchar = argv[d->optind] + 2; return process_long_option (argc, argv, optstring, longopts, longind, long_only, d, - print_errors, "--"); + print_errors, "--", + translate); } /* If long_only and the ARGV-element has the form "-f", @@ -595,7 +657,8 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, d->__nextchar = argv[d->optind] + 1; code = process_long_option (argc, argv, optstring, longopts, longind, long_only, d, - print_errors, "-"); + print_errors, "-", + translate); if (code != -1) return code; } @@ -649,7 +712,8 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, d->__nextchar = d->optarg; d->optarg = NULL; return process_long_option (argc, argv, optstring, longopts, longind, - 0 /* long_only */, d, print_errors, "-W "); + 0 /* long_only */, d, print_errors, "-W ", + translate); } if (temp[1] == ':') { @@ -702,7 +766,7 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, int _getopt_internal (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, - int posixly_correct) + int posixly_correct, char *(*translate) (const char *)) { int result; @@ -711,7 +775,7 @@ _getopt_internal (int argc, char **argv, const char *optstring, result = _getopt_internal_r (argc, argv, optstring, longopts, longind, long_only, &getopt_data, - posixly_correct); + posixly_correct, translate); optind = getopt_data.optind; optarg = getopt_data.optarg; @@ -729,7 +793,8 @@ _getopt_internal (int argc, char **argv, const char *optstring, NAME (int argc, char *const *argv, const char *optstring) \ { \ return _getopt_internal (argc, (char **)argv, optstring, \ - NULL, NULL, 0, POSIXLY_CORRECT); \ + NULL, NULL, 0, POSIXLY_CORRECT, \ + NULL); \ } #ifdef _LIBC diff --git a/posix/getopt1.c b/posix/getopt1.c index 733f58122a..6bf0087344 100644 --- a/posix/getopt1.c +++ b/posix/getopt1.c @@ -19,6 +19,9 @@ #ifndef _LIBC # include +# include "gettext.h" +#else +# include #endif #include "getopt.h" @@ -29,7 +32,7 @@ getopt_long (int argc, char *__getopt_argv_const *argv, const char *options, const struct option *long_options, int *opt_index) { return _getopt_internal (argc, (char **) argv, options, long_options, - opt_index, 0, 0); + opt_index, 0, 0, gettext); } int @@ -38,7 +41,7 @@ _getopt_long_r (int argc, char **argv, const char *options, struct _getopt_data *d) { return _getopt_internal_r (argc, argv, options, long_options, opt_index, - 0, d, 0); + 0, d, 0, gettext); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. @@ -52,7 +55,7 @@ getopt_long_only (int argc, char *__getopt_argv_const *argv, const struct option *long_options, int *opt_index) { return _getopt_internal (argc, (char **) argv, options, long_options, - opt_index, 1, 0); + opt_index, 1, 0, gettext); } int @@ -61,7 +64,7 @@ _getopt_long_only_r (int argc, char **argv, const char *options, struct _getopt_data *d) { return _getopt_internal_r (argc, argv, options, long_options, opt_index, - 1, d, 0); + 1, d, 0, gettext); } diff --git a/posix/getopt_int.h b/posix/getopt_int.h index 94c1945c5f..b15b21256a 100644 --- a/posix/getopt_int.h +++ b/posix/getopt_int.h @@ -22,10 +22,14 @@ #include +/* The translate argument here is optional (can be NULL), it is used + to avoid depending on the gettext functions in the posix getopt + function. */ extern int _getopt_internal (int ___argc, char **___argv, const char *__shortopts, const struct option *__longopts, int *__longind, - int __long_only, int __posixly_correct); + int __long_only, int __posixly_correct, + char *(*translate) (const char *msgid)); /* Reentrant versions which can handle parsing multiple argument @@ -102,7 +106,8 @@ extern int _getopt_internal_r (int ___argc, char **___argv, const char *__shortopts, const struct option *__longopts, int *__longind, int __long_only, struct _getopt_data *__data, - int __posixly_correct); + int __posixly_correct, + char *(*translate) (const char *msgid)); extern int _getopt_long_r (int ___argc, char **___argv, const char *__shortopts, diff --git a/posix/tstgetoptl.c b/posix/tstgetoptl.c new file mode 100644 index 0000000000..04b07093e4 --- /dev/null +++ b/posix/tstgetoptl.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include + +/* This is a modified copy of tstgetopt.c, but instead of having two + different options with the same short name, it has only one, and + the other one is a translation. */ + +/* This uses the en_GB locale so that colour means color. As a + special case, we also check that non-translated options have + precedence over translated options, by translated "optional" as + "required". */ + +static int +prepare_localedir (void) +{ + unsetenv ("LANGUAGE"); + setlocale (LC_ALL, "en_GB.UTF-8"); + if (bindtextdomain ("tstgetoptl", OBJPFX "domaindir") == NULL) + { + fputs ("Cannot call bindtextdomain.\n", stderr); + return -1; + } + if (textdomain ("tstgetoptl") == NULL) + { + fputs ("Cannot call textdomain.\n", stderr); + return -1; + } + /* Check that the catalog is OK: */ + if (strcmp (gettext ("color"), "colour") != 0) + { + fputs ("The mo file does not work.\n", stderr); + return -1; + } + return 0; +} + +int +main (int argc, char **argv) +{ + static const struct option options[] = + { + {"required", required_argument, NULL, 'r'}, + {"optional", optional_argument, NULL, 'o'}, + {"none", no_argument, NULL, 'n'}, + {"color", no_argument, NULL, 'C'}, + /* Now colour is handled as a translation of color */ + {NULL, 0, NULL, 0 } + }; + + /* The rest of the function is the same as in tstgetopt.c. */ + + int aflag = 0; + int bflag = 0; + char *cvalue = NULL; + int Cflag = 0; + int nflag = 0; + int index; + int c; + int result = 0; + + if (prepare_localedir () != 0) + { + fputs ("Error while setting up localedir.\n", stderr); + return 1; + } + while ((c = getopt_long (argc, argv, "abc:", options, NULL)) >= 0) + switch (c) + { + case 'a': + aflag = 1; + break; + case 'b': + bflag = 1; + break; + case 'c': + cvalue = optarg; + break; + case 'C': + ++Cflag; + break; + case '?': + fputs ("Unknown option.\n", stderr); + return 1; + default: + fprintf (stderr, "This should never happen!\n"); + return 1; + + case 'r': + printf ("--required %s\n", optarg); + result |= strcmp (optarg, "foobar") != 0; + break; + case 'o': + printf ("--optional %s\n", optarg); + result |= optarg == NULL || strcmp (optarg, "bazbug") != 0; + break; + case 'n': + puts ("--none"); + nflag = 1; + break; + } + + printf ("aflag = %d, bflag = %d, cvalue = %s, Cflags = %d, nflag = %d\n", + aflag, bflag, cvalue, Cflag, nflag); + + result |= (aflag != 1 || bflag != 1 || cvalue == NULL + || strcmp (cvalue, "foobar") != 0 || Cflag != 3 || nflag != 1); + + for (index = optind; index < argc; index++) + printf ("Non-option argument %s\n", argv[index]); + + result |= optind + 1 != argc || strcmp (argv[optind], "random") != 0; + + return result; +} diff --git a/posix/tstgetoptl.po b/posix/tstgetoptl.po new file mode 100644 index 0000000000..25cd595790 --- /dev/null +++ b/posix/tstgetoptl.po @@ -0,0 +1,25 @@ +# English translations for tstgetoptl, a test case in glibc. +# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the glibc package. +# +msgid "" +msgstr "" +"Project-Id-Version: tstgetoptl 0.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-05-27 19:29+0200\n" +"PO-Revision-Date: 2025-05-27 19:30+0200\n" +"Language-Team: English (British) <(nothing)>\n" +"Language: en_GB\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: xxx.c:yy +msgid "color" +msgstr "colour" + +# This is to make sure the translator cannot redirect options. +#: xxx.c:yy +msgid "optional" +msgstr "required" From patchwork Sun Oct 5 16:34:25 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vivien Kraus X-Patchwork-Id: 121274 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 251853858C30 for ; Sun, 5 Oct 2025 16:42:11 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 251853858C30 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, secure) header.d=planete-kraus.eu header.i=@planete-kraus.eu header.a=rsa-sha1 header.s=albinoniA header.b=nqbs0OyR X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from planete-kraus.eu (planete-kraus.eu [IPv6:2a00:5881:4008:2810::309]) by sourceware.org (Postfix) with ESMTPS id 7BB013858D33 for ; Sun, 5 Oct 2025 16:36:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7BB013858D33 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=planete-kraus.eu Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=planete-kraus.eu ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 7BB013858D33 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:5881:4008:2810::309 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682177; cv=none; b=vF4NC76ouSk3qlKPPXcQnjKytCBpF0LC1H4fCKSe1xMqrv11orfRc1JLZUjLfDTA/VXh5PQauPlgTU5Fu4Hhw9hElPrcqFiT9kAIqgLvW76ygc1FZpxYepSqyByUwkEHeH5k1SelB09FsaLjD1N2Vyk4Fi3B+Pc+8/q1QSLriCY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682177; c=relaxed/simple; bh=KRBh/ANG7qgV7LZ0Nv1VdHHOWttYYFz21hK8cVuWNNA=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=aNWXX2YYZDOhpMKq9ohWvGAYs5As0g12+RGBEu88bdqD9cnatpPFxbK6v31Xr57JrCsbAiRu/aWS947VVRkppccZUx7YUBozKnzWOk6BGWn0GHvgOm83+rt4U7Ps2lRYPwVJJ230lvvGPVLjt0Oujwi8tmFOUZEQfxgAQCsraZs= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7BB013858D33 Received: from planete-kraus.eu (localhost [127.0.0.1]) by planete-kraus.eu (OpenSMTPD) with ESMTP id 81bfbd11 for ; Sun, 5 Oct 2025 16:36:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=planete-kraus.eu; h=from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; s=albinoniA; bh=d700WTO dYKuNVSwBQ79y0s05wDk=; b=nqbs0OyR5bTCFUP3wU9BvGrchpQCxkQ78bXJ6iC ml/GiNJqQtXQtTJgNcM3M5Ijb549oEf5m5YlvzRagS4L0tnNjA3EP+u/yhsEw+la GjTPoqCbJIdMIPq5wFgiwzLz4ICFjWTHnuPnqNRMeztIrGLYmjxmqapzXwQZRHHH 9orQc+Kw1jd/9sePnVa62uvHkFsmmRgbaQWHA43ubh4EomUroTAxJcvT7T3rmMGS BnHKn2RedAM0C/RObODJDa30UGL3QRprZMqTBDE6DL/vdwDGCiAvqR7rmX5CFsyk RYLxDswfQTTfLo9wg1+IL4Ql1Rfj0SRVHmdSD9fmhbV4gsw== Received: by planete-kraus.eu (OpenSMTPD) with ESMTPSA id 59358305 (TLSv1.3:TLS_CHACHA20_POLY1305_SHA256:256:NO); Sun, 5 Oct 2025 16:36:05 +0000 (UTC) From: Vivien Kraus To: libc-alpha@sourceware.org Cc: Vivien Kraus Subject: [PATCH v12 2/9] posix: let the getopt caller set the translation context Date: Sun, 5 Oct 2025 18:34:25 +0200 Message-Id: X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Option names are typically one word, so they could be translated differently in different parts of the program. The use of a context lets the translator pick the most appropriate translation when used in the command-line. Another possibility would be to prepend two dashes to the option name before translation, such that it would be obvious this is the command-line option name. However, it would be difficult to mark this string for translation (to be processed by xgettext). The context is now mandatory. pgettext_expr is not available yet, so we use a custom function to combine the context and the long option name. This creates a new global variable / reentrant state field, optctxt, so that the caller can override it. --- manual/getopt.texi | 14 +++++- posix/Versions | 3 ++ posix/bits/getopt_ext.h | 7 +++ posix/getopt.c | 27 +++++++----- posix/getopt1.c | 43 +++++++++++++++++-- posix/getopt_int.h | 6 ++- posix/tstgetoptl.c | 6 ++- posix/tstgetoptl.po | 2 + sysdeps/mach/hurd/i386/libc.abilist | 1 + sysdeps/mach/hurd/x86_64/libc.abilist | 1 + sysdeps/unix/sysv/linux/aarch64/libc.abilist | 1 + sysdeps/unix/sysv/linux/alpha/libc.abilist | 1 + sysdeps/unix/sysv/linux/arc/libc.abilist | 1 + sysdeps/unix/sysv/linux/arm/be/libc.abilist | 1 + sysdeps/unix/sysv/linux/arm/le/libc.abilist | 1 + sysdeps/unix/sysv/linux/csky/libc.abilist | 1 + sysdeps/unix/sysv/linux/hppa/libc.abilist | 1 + sysdeps/unix/sysv/linux/i386/libc.abilist | 1 + .../sysv/linux/loongarch/lp64/libc.abilist | 1 + .../sysv/linux/m68k/coldfire/libc.abilist | 1 + .../unix/sysv/linux/m68k/m680x0/libc.abilist | 1 + .../sysv/linux/microblaze/be/libc.abilist | 1 + .../sysv/linux/microblaze/le/libc.abilist | 1 + .../sysv/linux/mips/mips32/fpu/libc.abilist | 1 + .../sysv/linux/mips/mips32/nofpu/libc.abilist | 1 + .../sysv/linux/mips/mips64/n32/libc.abilist | 1 + .../sysv/linux/mips/mips64/n64/libc.abilist | 1 + sysdeps/unix/sysv/linux/or1k/libc.abilist | 1 + .../linux/powerpc/powerpc32/fpu/libc.abilist | 1 + .../powerpc/powerpc32/nofpu/libc.abilist | 1 + .../linux/powerpc/powerpc64/be/libc.abilist | 1 + .../linux/powerpc/powerpc64/le/libc.abilist | 1 + .../unix/sysv/linux/riscv/rv32/libc.abilist | 1 + .../unix/sysv/linux/riscv/rv64/libc.abilist | 1 + .../unix/sysv/linux/s390/s390-32/libc.abilist | 1 + .../unix/sysv/linux/s390/s390-64/libc.abilist | 1 + sysdeps/unix/sysv/linux/sh/be/libc.abilist | 1 + sysdeps/unix/sysv/linux/sh/le/libc.abilist | 1 + .../sysv/linux/sparc/sparc32/libc.abilist | 1 + .../sysv/linux/sparc/sparc64/libc.abilist | 1 + .../unix/sysv/linux/x86_64/64/libc.abilist | 1 + .../unix/sysv/linux/x86_64/x32/libc.abilist | 1 + 42 files changed, 124 insertions(+), 18 deletions(-) diff --git a/manual/getopt.texi b/manual/getopt.texi index da9df5225a..65e24f1143 100644 --- a/manual/getopt.texi +++ b/manual/getopt.texi @@ -53,6 +53,16 @@ This variable is set by @code{getopt} to point at the value of the option argument, for those options that accept arguments. @end deftypevar +@deftypevar {const char *} optctxt +In order to match translated option names, @code{getopt} looks the +names in the current textdomain. Since option names may be short +words instead of long sentences, they may have different translations +in other places of the program. @xref{Contexts, , Using contexts for +solving ambiguities, gettext, the GNU Gettext manual}, for more +information. If this is @code{NULL}, then the translated option names +will not be processed. +@end deftypevar + @deftypefun int getopt (int @var{argc}, char *const *@var{argv}, const char *@var{options}) @standards{POSIX.2, unistd.h} @safety{@prelim{}@mtunsafe{@mtasurace{:getopt} @mtsenv{}}@asunsafe{@ascuheap{} @ascuintl{} @asulock{} @asucorrupt{}}@acunsafe{@acsmem{} @aculock{} @acucorrupt{}}} @@ -224,7 +234,9 @@ The @code{struct option} structure has these fields: @item const char *name This field is the name of the option. It is a string. In order for @command{getopt_long} to accept either the long option name or its -translated form, you should mark this string for translation. +translated form, you should mark this string for translation with a +translation context, and set @code{optctxt} to the translation +context. @item int has_arg This field says whether the option takes an argument. It is an integer, diff --git a/posix/Versions b/posix/Versions index 0624d24bcc..5873e8ec56 100644 --- a/posix/Versions +++ b/posix/Versions @@ -159,6 +159,9 @@ libc { GLIBC_2.35 { posix_spawn_file_actions_addtcsetpgrp_np; } + GLIBC_2.43 { + optctxt; + } GLIBC_PRIVATE { __libc_fork; __libc_pread; __libc_pwrite; __nanosleep_nocancel; __pause_nocancel; diff --git a/posix/bits/getopt_ext.h b/posix/bits/getopt_ext.h index 42be8ec38e..07d9407b64 100644 --- a/posix/bits/getopt_ext.h +++ b/posix/bits/getopt_ext.h @@ -26,6 +26,13 @@ __BEGIN_DECLS +#ifdef __USE_GNU +/* Callers store the translation context in which to retrieve option + names. If unset, the option names will not be translated. */ + +extern const char *optctxt; +#endif + /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of 'struct option' terminated by an element containing a name which is diff --git a/posix/getopt.c b/posix/getopt.c index 9540821e1f..28f0be03f8 100644 --- a/posix/getopt.c +++ b/posix/getopt.c @@ -182,17 +182,19 @@ exchange (char **argv, struct _getopt_data *d) d->__last_nonopt = d->optind; } -/* Return 1 iff a translation for opt_name has been found and it - matches the substring from argument, length argument_length. +/* Return 1 iff translation_context is not NULL, a translation for + opt_name has been found and it matches the substring from argument, + length argument_length. */ static const int -match_translated_option_name (char *(*translate) (const char *msgid), +match_translated_option_name (char *(*translate) (const char *, const char *), const char *argument, size_t argument_length, + const char *translation_context, const char *opt_name) { const char *translated = opt_name; if (translate) - translated = translate (opt_name); + translated = translate (translation_context, opt_name); return (!strncmp (translated, argument, argument_length) && argument_length == strlen (translated)); } @@ -210,7 +212,7 @@ process_long_option (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, struct _getopt_data *d, int print_errors, const char *prefix, - char *(*translate) (const char *msgid)) + char *(*translate) (const char *, const char *)) { char *nameend; size_t namelen; @@ -241,7 +243,9 @@ process_long_option (int argc, char **argv, const char *optstring, /* Didn't find an exact match, try with translated option names. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (match_translated_option_name (translate, d->__nextchar, namelen, p->name)) + if (match_translated_option_name (translate, + d->__nextchar, namelen, + d->optctxt, p->name)) { /* Exact match found with translation. */ pfound = p; @@ -365,7 +369,7 @@ process_long_option (int argc, char **argv, const char *optstring, /* We have found a matching long option. Consume it. */ d->optind++; d->__nextchar = NULL; - translated_option_name = translate (pfound->name); + translated_option_name = translate (d->optctxt, pfound->name); if (*nameend) { /* Don't test has_arg with >, because some C compilers don't @@ -531,7 +535,7 @@ int _getopt_internal_r (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, struct _getopt_data *d, int posixly_correct, - char *(*translate) (const char *msgid)) + char *(*translate) (const char *, const char *)) { int print_errors = d->opterr; @@ -766,12 +770,15 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, int _getopt_internal (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, - int posixly_correct, char *(*translate) (const char *)) + int posixly_correct, + char *(*translate) (const char *, const char *), + const char *ctxt) { int result; getopt_data.optind = optind; getopt_data.opterr = opterr; + getopt_data.optctxt = ctxt; result = _getopt_internal_r (argc, argv, optstring, longopts, longind, long_only, &getopt_data, @@ -794,7 +801,7 @@ _getopt_internal (int argc, char **argv, const char *optstring, { \ return _getopt_internal (argc, (char **)argv, optstring, \ NULL, NULL, 0, POSIXLY_CORRECT, \ - NULL); \ + NULL, NULL); \ } #ifdef _LIBC diff --git a/posix/getopt1.c b/posix/getopt1.c index 6bf0087344..28d090019d 100644 --- a/posix/getopt1.c +++ b/posix/getopt1.c @@ -26,13 +26,48 @@ #include "getopt.h" #include "getopt_int.h" +#include +#include + +/* Callers store an optional context to enable option name + translation. */ + +const char *optctxt = NULL; + +/* FIXME: use pgettext_expr. */ +static char * +do_translate (const char *context, const char *msgid) +{ + char *full_msgid; + const char *translated = msgid; + + if (context) + { + full_msgid = malloc (strlen (context) + 1 /* ^D */ + strlen (msgid) + 1); + if (msgid) + { + strcpy (full_msgid, context); + full_msgid[strlen (context)] = '\004'; + strcpy (full_msgid + strlen (context) + 1, msgid); + translated = __dcgettext (NULL, full_msgid, LC_MESSAGES); + if (!strcmp (translated, full_msgid)) + { + translated = msgid; + } + } + free (full_msgid); + } + else + translated = msgid; + return (char *) translated; +} int getopt_long (int argc, char *__getopt_argv_const *argv, const char *options, const struct option *long_options, int *opt_index) { return _getopt_internal (argc, (char **) argv, options, long_options, - opt_index, 0, 0, gettext); + opt_index, 0, 0, do_translate, optctxt); } int @@ -41,7 +76,7 @@ _getopt_long_r (int argc, char **argv, const char *options, struct _getopt_data *d) { return _getopt_internal_r (argc, argv, options, long_options, opt_index, - 0, d, 0, gettext); + 0, d, 0, do_translate); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. @@ -55,7 +90,7 @@ getopt_long_only (int argc, char *__getopt_argv_const *argv, const struct option *long_options, int *opt_index) { return _getopt_internal (argc, (char **) argv, options, long_options, - opt_index, 1, 0, gettext); + opt_index, 1, 0, do_translate, optctxt); } int @@ -64,7 +99,7 @@ _getopt_long_only_r (int argc, char **argv, const char *options, struct _getopt_data *d) { return _getopt_internal_r (argc, argv, options, long_options, opt_index, - 1, d, 0, gettext); + 1, d, 0, do_translate); } diff --git a/posix/getopt_int.h b/posix/getopt_int.h index b15b21256a..1d091979c3 100644 --- a/posix/getopt_int.h +++ b/posix/getopt_int.h @@ -29,7 +29,8 @@ extern int _getopt_internal (int ___argc, char **___argv, const char *__shortopts, const struct option *__longopts, int *__longind, int __long_only, int __posixly_correct, - char *(*translate) (const char *msgid)); + char *(*translate) (const char *, const char *), + const char *__optctxt); /* Reentrant versions which can handle parsing multiple argument @@ -71,6 +72,7 @@ struct _getopt_data int opterr; int optopt; char *optarg; + const char *optctxt; /* Internal members. */ @@ -107,7 +109,7 @@ extern int _getopt_internal_r (int ___argc, char **___argv, const struct option *__longopts, int *__longind, int __long_only, struct _getopt_data *__data, int __posixly_correct, - char *(*translate) (const char *msgid)); + char *(*translate) (const char *, const char *)); extern int _getopt_long_r (int ___argc, char **___argv, const char *__shortopts, diff --git a/posix/tstgetoptl.c b/posix/tstgetoptl.c index 04b07093e4..bffd56f47d 100644 --- a/posix/tstgetoptl.c +++ b/posix/tstgetoptl.c @@ -15,6 +15,8 @@ precedence over translated options, by translated "optional" as "required". */ +#define TRANSLATION_CONTEXT "command-line option" + static int prepare_localedir (void) { @@ -31,7 +33,7 @@ prepare_localedir (void) return -1; } /* Check that the catalog is OK: */ - if (strcmp (gettext ("color"), "colour") != 0) + if (strcmp (gettext (TRANSLATION_CONTEXT "\004" "color"), "colour") != 0) { fputs ("The mo file does not work.\n", stderr); return -1; @@ -42,6 +44,7 @@ prepare_localedir (void) int main (int argc, char **argv) { + static const char *translation_context = TRANSLATION_CONTEXT; static const struct option options[] = { {"required", required_argument, NULL, 'r'}, @@ -68,6 +71,7 @@ main (int argc, char **argv) fputs ("Error while setting up localedir.\n", stderr); return 1; } + optctxt = translation_context; while ((c = getopt_long (argc, argv, "abc:", options, NULL)) >= 0) switch (c) { diff --git a/posix/tstgetoptl.po b/posix/tstgetoptl.po index 25cd595790..e060c0d6e3 100644 --- a/posix/tstgetoptl.po +++ b/posix/tstgetoptl.po @@ -16,10 +16,12 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: xxx.c:yy +msgctxt "command-line option" msgid "color" msgstr "colour" # This is to make sure the translator cannot redirect options. #: xxx.c:yy +msgctxt "command-line option" msgid "optional" msgstr "required" diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist index e0dc8aea28..ef2389889d 100644 --- a/sysdeps/mach/hurd/i386/libc.abilist +++ b/sysdeps/mach/hurd/i386/libc.abilist @@ -2664,6 +2664,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 pthread_cancel F GLIBC_2.43 pthread_clockjoin_np F GLIBC_2.43 pthread_detach F diff --git a/sysdeps/mach/hurd/x86_64/libc.abilist b/sysdeps/mach/hurd/x86_64/libc.abilist index c0cac6d507..366f44b7b3 100644 --- a/sysdeps/mach/hurd/x86_64/libc.abilist +++ b/sysdeps/mach/hurd/x86_64/libc.abilist @@ -2345,6 +2345,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x8 GLIBC_2.43 pthread_cancel F GLIBC_2.43 pthread_clockjoin_np F GLIBC_2.43 pthread_detach F diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist index bde5e66ce0..b5adb590b1 100644 --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist @@ -2769,3 +2769,4 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x8 diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist index 0060616d20..c97471e756 100644 --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist @@ -3116,6 +3116,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x8 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist index 79895b9b37..7dd4589884 100644 --- a/sysdeps/unix/sysv/linux/arc/libc.abilist +++ b/sysdeps/unix/sysv/linux/arc/libc.abilist @@ -2530,3 +2530,4 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist index 3426d99f1e..35a50182f2 100644 --- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist @@ -2822,6 +2822,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist index eca31a2e27..70f444545a 100644 --- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist @@ -2819,6 +2819,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist index 7c5ebdf3ba..83ac7a3a4f 100644 --- a/sysdeps/unix/sysv/linux/csky/libc.abilist +++ b/sysdeps/unix/sysv/linux/csky/libc.abilist @@ -2806,3 +2806,4 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist index 742118e65e..85cfee194d 100644 --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist @@ -2843,6 +2843,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist index 8a8ff34e35..dea2312c5c 100644 --- a/sysdeps/unix/sysv/linux/i386/libc.abilist +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist @@ -3026,6 +3026,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist index 86b0246ad1..bde525090d 100644 --- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist +++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist @@ -2290,3 +2290,4 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x8 diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist index 92cef27d98..89f275f92c 100644 --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist @@ -2802,6 +2802,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist index 3b74574560..071de9934e 100644 --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist @@ -2969,6 +2969,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist index ae2d13ad8f..a983dd9776 100644 --- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist @@ -2855,3 +2855,4 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist index 1b0264df27..d8c4d03cee 100644 --- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist @@ -2852,3 +2852,4 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist index 8923228090..8431dca4fc 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist @@ -2932,6 +2932,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist index 9ce80245f6..ff0d3bcb1b 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist @@ -2930,6 +2930,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist index e3c971367d..834ca53a26 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist @@ -2938,6 +2938,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist index db707fad52..e8dcdec523 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist @@ -2840,6 +2840,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x8 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist index 0780d53978..f69e29d8ec 100644 --- a/sysdeps/unix/sysv/linux/or1k/libc.abilist +++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist @@ -2280,3 +2280,4 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist index f4f1551054..313b5ac2c2 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist @@ -3159,6 +3159,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist index 34ec11f685..9ac04c675a 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist @@ -3204,6 +3204,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist index b790b43f4b..030c44cfd6 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist @@ -2913,6 +2913,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x8 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist index b7de3f95a8..ba27c807a8 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist @@ -2989,3 +2989,4 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x8 diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist index 2680eabc7c..59901b5a84 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist @@ -2533,3 +2533,4 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist index 074ca2f280..d861e9348a 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist @@ -2733,3 +2733,4 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x8 diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist index 0573db9f50..94eddb2157 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist @@ -3157,6 +3157,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist index 55760b901f..a8279df601 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist @@ -2950,6 +2950,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x8 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist index a15b4df221..dab732c1d3 100644 --- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist @@ -2849,6 +2849,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist index b490e1064c..54fc47ab6c 100644 --- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist @@ -2846,6 +2846,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist index 8fd42d21d0..b73cd54dbb 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist @@ -3180,6 +3180,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist index b62dec6a63..dcd144bb46 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist @@ -2816,6 +2816,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x8 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist index fe23ac6682..b9549a8de1 100644 --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist @@ -2765,6 +2765,7 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x8 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist index 986dfcca2a..35fc746c3b 100644 --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist @@ -2784,3 +2784,4 @@ GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F +GLIBC_2.43 optctxt D 0x4 From patchwork Sun Oct 5 16:34:26 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Vivien Kraus X-Patchwork-Id: 121273 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A07723858D2A for ; Sun, 5 Oct 2025 16:40:55 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A07723858D2A Authentication-Results: sourceware.org; dkim=pass (2048-bit key, secure) header.d=planete-kraus.eu header.i=@planete-kraus.eu header.a=rsa-sha1 header.s=albinoniA header.b=PMQBj0Fr X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from planete-kraus.eu (planete-kraus.eu [IPv6:2a00:5881:4008:2810::309]) by sourceware.org (Postfix) with ESMTPS id 19FAC3858C51 for ; Sun, 5 Oct 2025 16:36:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 19FAC3858C51 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=planete-kraus.eu Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=planete-kraus.eu ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 19FAC3858C51 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:5881:4008:2810::309 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682178; cv=none; b=buqW0AOBC+upnwpjNDUCNnv3i9bUIuGOffcoQUZ9F5e2z//lXH1qTDm98GjceghYcr/8ZEl9I26+RYa9NVkG/4pCj/aWRoS8G7VcF4xPEOgeoCr+efUwNYSnJvOMLUbA34WEMZSosN23JIPL9oVnLrmP2ptKhPQX6VjdusiSHE4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682178; c=relaxed/simple; bh=8wX0RH9e7sv5EAcu0s/413sgjrztVHbUpvg/cHPIvw8=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=bB+8WZlrMFQudV9dF7E0SHMt/UX6GgriXIj1dUhz7vxofoawXfVEjsBn+5ypCZYjEiZKDgZOeXChVKDGMVgQMqpWKuZ73TZ5l7F8x7Xj0VCqaAWLfiQ0ub0IrTFb70TASb/nsmiyMeOLfkWtFNRpTgyjuuR6Lg0f0ccj4kFpvec= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 19FAC3858C51 Received: from planete-kraus.eu (localhost [127.0.0.1]) by planete-kraus.eu (OpenSMTPD) with ESMTP id dcb6f3b0 for ; Sun, 5 Oct 2025 16:36:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=planete-kraus.eu; h=from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; s= albinoniA; bh=FkGybN5zkAEgiKbU26/ylV9Koeg=; b=PMQBj0FrJ6ql3OMt9d yfEdlhgY4ocCbAldWyaP1KModFO9/HobUF1zyjDhob0Lyo4gwb4mg/xTqkUYygpq jkSq7y7t0gxDhjc4RAyR3Pjsyss6geKzANDCSPdNzyDkAescPStv1kqKk4mlByne AU7/ZRfm2di7rsjeZAQ4NftzMSx5IHdXBsEUOn4S5qZedKjec1M9gYzewz+SKUDr +XcgPR8DfRDi9dRL7xoaNzzV0jsaIpYXh1QDYrBh2Wwpa34X5MeIh4/uUaDxwWXp vGjNKKn267YXnkU9SOLRfM3CK8UXt2iBC84FG7v4H1JCIWQODrM2s+Usl4loJrve apsQ== Received: by planete-kraus.eu (OpenSMTPD) with ESMTPSA id 5074c3b0 (TLSv1.3:TLS_CHACHA20_POLY1305_SHA256:256:NO); Sun, 5 Oct 2025 16:36:06 +0000 (UTC) From: Vivien Kraus To: libc-alpha@sourceware.org Cc: Vivien Kraus Subject: [PATCH v12 3/9] argp: document translated names in --help and --usage Date: Sun, 5 Oct 2025 18:34:26 +0200 Message-Id: X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org They are displayed in the --usage and --help output as the main option name, and the untranslated name is also displayed in parenthesis, so that someone reading a script (presumably with untranslated option names) can then figure out which translated option name it relates to. A translation context is required for translated options processing. However, its configuration would be most likely expected to be in the struct argp definition, but it would change this struct type. The easiest solution is to use a fixed context. Since the configuration of the translation domain is on a per-parser basis, and getopt_long is called with all options (including options from children parsers), then all option names in all parsers must be in the same domain. However, it makes sense to use documentation from different domains. Thus, the translations for option names will all be searched in the current textdomain, while documentation will respect the parser’s domain. --- argp/Makefile | 14 +++++ argp/argp-help.c | 63 +++++++++++++++++--- argp/argp-parse.c | 1 + argp/tst-argphelp-localized.c | 102 +++++++++++++++++++++++++++++++++ argp/tst-argphelp-localized.po | 18 ++++++ argp/tst-argpusage-localized.c | 82 ++++++++++++++++++++++++++ manual/argp.texi | 22 ++++--- 7 files changed, 286 insertions(+), 16 deletions(-) create mode 100644 argp/tst-argphelp-localized.c create mode 100644 argp/tst-argphelp-localized.po create mode 100644 argp/tst-argpusage-localized.c diff --git a/argp/Makefile b/argp/Makefile index 01eb6b6db8..59fff5e1d0 100644 --- a/argp/Makefile +++ b/argp/Makefile @@ -44,6 +44,8 @@ tests = \ bug-argp2 \ tst-argp1 \ tst-argp2 \ + tst-argphelp-localized \ + tst-argpusage-localized \ tst-ldbl-argp \ # tests @@ -52,6 +54,18 @@ CFLAGS-argp-parse.c += $(uses-callbacks) $(config-cflags-wno-ignored-attributes) CFLAGS-argp-fmtstream.c += -fexceptions $(config-cflags-wno-ignored-attributes) CFLAGS-argp-fs-xinl.c += $(config-cflags-wno-ignored-attributes) +tst_argphelp_localized_mo = $(objpfx)domaindir/en_GB/LC_MESSAGES/tst-argphelp-localized.mo + +$(tst_argphelp_localized_mo): tst-argphelp-localized.po + $(make-target-directory) + msgfmt -o $@T $< + mv -f $@T $@ + +$(objpfx)tst-argphelp-localized.out: $(tst_argphelp_localized_mo) +$(objpfx)tst-argpusage-localized.out: $(tst_argphelp_localized_mo) +CFLAGS-tst-argphelp-localized.c += -DOBJPFX=\"$(objpfx)\" +CFLAGS-tst-argpusage-localized.c += -DOBJPFX=\"$(objpfx)\" + bug-argp1-ARGS = -- --help bug-argp2-ARGS = -- -d 111 --dstaddr 222 -p 333 --peer 444 diff --git a/argp/argp-help.c b/argp/argp-help.c index 3fe3a244ce..672e733fc8 100644 --- a/argp/argp-help.c +++ b/argp/argp-help.c @@ -1213,6 +1213,8 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state, unsigned num; const struct argp_option *real = entry->opt, *opt; char *so = entry->short_options; + char *option_msgid; + const char *translated_option_name; int have_long_opt = 0; /* We have any long options. */ /* Saved margins. */ int old_lm = __argp_fmtstream_set_lmargin (stream, 0); @@ -1276,9 +1278,25 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state, if (opt->name && ovisible (opt)) { comma (uparams.long_opt_col, &pest); - __argp_fmtstream_printf (stream, "--%s", opt->name); + option_msgid = + malloc (strlen ("command-line option\004") + + strlen (opt->name) + 1); + if (option_msgid) + { + strcpy (option_msgid, "command-line option\004"); + strcat (option_msgid, opt->name); + translated_option_name = gettext (option_msgid); + if (!strcmp (translated_option_name, option_msgid)) + translated_option_name = opt->name; + } + else + translated_option_name = opt->name; + __argp_fmtstream_printf (stream, "--%s", translated_option_name); arg (real, "=%s", "[=%s]", state == NULL ? NULL : state->root_argp->argp_domain, stream); + if (strcmp (translated_option_name, opt->name)) + __argp_fmtstream_printf (stream, " (--%s)", opt->name); + free (option_msgid); } } @@ -1420,6 +1438,8 @@ usage_long_opt (const struct argp_option *opt, { argp_fmtstream_t stream = cookie; const char *arg = opt->arg; + char *option_msgid; + const char *translated_opt_name = opt->name; int flags = opt->flags | real->flags; if (! arg) @@ -1427,16 +1447,41 @@ usage_long_opt (const struct argp_option *opt, if (! (flags & OPTION_NO_USAGE)) { + /* Since we cannot customize the translation context, we will + use a default one. FIXME: use pgettext_expr(). */ + static const char *default_context = "command-line option\004"; + option_msgid = malloc (strlen (default_context) + strlen (opt->name) + 1); + translated_opt_name = NULL; + if (option_msgid) + { + strcpy (option_msgid, default_context); + strcat (option_msgid, opt->name); + translated_opt_name = gettext (option_msgid); + if (!strcmp (translated_opt_name, option_msgid)) + translated_opt_name = opt->name; + } + if (!strcmp (translated_opt_name, opt->name)) + translated_opt_name = NULL; if (arg) - { - arg = dgettext (domain, arg); - if (flags & OPTION_ARG_OPTIONAL) - __argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg); - else - __argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg); - } + { + arg = dgettext (domain, arg); + if ((flags & OPTION_ARG_OPTIONAL) && translated_opt_name) + __argp_fmtstream_printf (stream, " [--%s[=%s] (--%s)]", + translated_opt_name, arg, opt->name); + else if (flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg); + else if (translated_opt_name) + __argp_fmtstream_printf (stream, " [--%s=%s (--%s)]", + translated_opt_name, arg, opt->name); + else + __argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg); + } + else if (translated_opt_name) + __argp_fmtstream_printf (stream, " [--%s (--%s)]", + translated_opt_name, opt->name); else - __argp_fmtstream_printf (stream, " [--%s]", opt->name); + __argp_fmtstream_printf (stream, " [--%s]", opt->name); + free (option_msgid); } return 0; diff --git a/argp/argp-parse.c b/argp/argp-parse.c index 99f8d9ecd4..90525cdf61 100644 --- a/argp/argp-parse.c +++ b/argp/argp-parse.c @@ -472,6 +472,7 @@ parser_init (struct parser *parser, const struct argp *argp, struct parser_sizes szs; struct _getopt_data opt_data = _GETOPT_DATA_INITIALIZER; + opt_data.optctxt = "command-line option"; szs.short_len = (flags & ARGP_NO_ARGS) ? 0 : 1; szs.long_len = 0; szs.num_groups = 0; diff --git a/argp/tst-argphelp-localized.c b/argp/tst-argphelp-localized.c new file mode 100644 index 0000000000..abc3325827 --- /dev/null +++ b/argp/tst-argphelp-localized.c @@ -0,0 +1,102 @@ +/* Test program for argp argument parser + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PN_(ctxt, str) (str) + +const char *argp_program_version = "argphelp-test 1.0"; + +struct argp_option options[] = +{ + {PN_ ("command-line option", "color"), 'c', 0, 0, "Rainbow!"}, + {0} +}; + +static int color_set = 0; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + (void) state; + if (key == 'c' && color_set) + { + fprintf (stderr, "%s:%d: color already set.\n", __FILE__, __LINE__); + abort (); + } + else if (key == 'c') + { + color_set = 1; + } + return 0; +} + +static struct argp argp = { options, parse_opt }; + +int +main (int argc, char *argv[]) +{ + char *test1_argv[3] = + { (char *) "/bin/tst-argphelp-localized", (char *) "--colour", NULL }; + char *test2_argv[3] = + { (char *) "/bin/tst-argphelp-localized", (char *) "--help", NULL }; + + unsetenv ("LANGUAGE"); + setlocale (LC_ALL, "en_GB.UTF-8"); + if (bindtextdomain ("tst-argphelp-localized", OBJPFX "domaindir") == NULL) + { + fprintf (stderr, "%s:%d: cannot call bindtextdomain.\n", + __FILE__, __LINE__); + abort (); + } + if (textdomain ("tst-argphelp-localized") == NULL) + { + fprintf (stderr, "%s:%d: cannot call textdomain.\n", + __FILE__, __LINE__); + abort (); + } + /* Check that the catalog is OK: */ + if (strcmp (gettext ("command-line option\004color"), "colour") != 0) + { + fprintf (stderr, "%s:%d: the mo file does not work.\n", + __FILE__, __LINE__); + abort (); + } + argp_parse (&argp, 2, test1_argv, 0, 0, NULL); + if (!color_set) + { + fprintf (stderr, "%s:%d: color not set.\n", __FILE__, __LINE__); + abort (); + } + argp_parse (&argp, 2, test2_argv, 0, 0, NULL); + fprintf (stderr, "%s:%d: --help did not exit the program.\n", __FILE__, __LINE__); + abort (); + return 0; +} diff --git a/argp/tst-argphelp-localized.po b/argp/tst-argphelp-localized.po new file mode 100644 index 0000000000..4e301bf278 --- /dev/null +++ b/argp/tst-argphelp-localized.po @@ -0,0 +1,18 @@ +# English translations for a GNU C Library test. +# This file is distributed under the same license as the GNU C Library. +# +msgid "" +msgstr "" +"Project-Id-Version: tst-argphelp-localized\n" +"Report-Msgid-Bugs-To: \n" +"Language-Team: English (British) <(nothing)>\n" +"Language: en_GB\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: tst-argphelp-localized.c:73 +msgctxt "command-line option" +msgid "color" +msgstr "colour" \ No newline at end of file diff --git a/argp/tst-argpusage-localized.c b/argp/tst-argpusage-localized.c new file mode 100644 index 0000000000..4061609fc3 --- /dev/null +++ b/argp/tst-argpusage-localized.c @@ -0,0 +1,82 @@ +/* Test program for argp argument parser + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PN_(ctxt, str) (str) + +const char *argp_program_version = "argpusage-test 1.0"; + +struct argp_option options[] = +{ + {PN_ ("command-line option", "color"), 'c', 0, 0, "Rainbow!"}, + {0} +}; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + return 0; +} + +static struct argp argp = { options, parse_opt }; + +int +main (int argc, char *argv[]) +{ + char *test_argv[3] = + { (char *) "/bin/tst-argpusage-localized", (char *) "--usage", NULL }; + + unsetenv ("LANGUAGE"); + setlocale (LC_ALL, "en_GB.UTF-8"); + if (bindtextdomain ("tst-argphelp-localized", OBJPFX "domaindir") == NULL) + { + fprintf (stderr, "%s:%d: cannot call bindtextdomain.\n", + __FILE__, __LINE__); + abort (); + } + if (textdomain ("tst-argphelp-localized") == NULL) + { + fprintf (stderr, "%s:%d: cannot call textdomain.\n", + __FILE__, __LINE__); + abort (); + } + /* Check that the catalog is OK: */ + if (strcmp (gettext ("command-line option\004color"), "colour") != 0) + { + fprintf (stderr, "%s:%d: the mo file does not work.\n", + __FILE__, __LINE__); + abort (); + } + argp_parse (&argp, 2, test_argv, 0, 0, NULL); + fprintf (stderr, "%s:%d: --usage did not exit the program.\n", __FILE__, __LINE__); + abort (); + return 0; +} diff --git a/manual/argp.texi b/manual/argp.texi index 0023441812..97456ef20e 100644 --- a/manual/argp.texi +++ b/manual/argp.texi @@ -206,8 +206,10 @@ messages. @xref{Argp Help Filtering}. @item const char *argp_domain If non-zero, the strings used in the argp library are translated using -the domain described by this string. If zero, the current default domain -is used. +the domain described by this string. If zero, the current default +domain is used. The long option names are always translated with the +current default domain, and with the @samp{"command-line option"} +disambiguation string. @end table @end deftp @@ -233,7 +235,9 @@ beginning, the unused fields left unspecified. The @code{options} field in a @code{struct argp} points to a vector of @code{struct argp_option} structures, each of which specifies an option that the argp parser supports. Multiple entries may be used for a single -option provided it has multiple names. This should be terminated by an +option provided it has multiple names. In any case, option names are +translated, so either the translated or untranslated form is +recognized for each option. This should be terminated by an entry with zero in all fields. Note that when using an initialized C array for options, writing @code{@{ 0 @}} is enough to achieve this. @@ -247,9 +251,12 @@ the following fields: @item const char *name The long name for this option, corresponding to the long option @samp{--@var{name}}; this field may be zero if this option @emph{only} -has a short name. To specify multiple names for an option, additional -entries may follow this one, with the @code{OPTION_ALIAS} flag -set. @xref{Argp Option Flags}. +has a short name. You should mark this string for translation with +the fixed @samp{"command-line option"} context. To specify multiple +names for an option, additional entries may follow this one, with the +@code{OPTION_ALIAS} flag set. @xref{Argp Option Flags}. Translations +are added automatically, it is not necessary to use an alias for +translations. @item int key The integer key provided by the current option to the option parser. If @@ -323,7 +330,8 @@ This option isn't displayed in any help messages. This option is an alias for the closest previous non-alias option. This means that it will be displayed in the same help entry, and will inherit fields other than @code{name} and @code{key} from the option being -aliased. +aliased. It is not necessary to list the translation of an option +name as an alias. @item OPTION_DOC From patchwork Sun Oct 5 16:34:27 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vivien Kraus X-Patchwork-Id: 121276 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id C31883858C55 for ; Sun, 5 Oct 2025 16:44:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C31883858C55 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, secure) header.d=planete-kraus.eu header.i=@planete-kraus.eu header.a=rsa-sha1 header.s=albinoniA header.b=TpMhPV1T X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from planete-kraus.eu (planete-kraus.eu [89.234.140.182]) by sourceware.org (Postfix) with ESMTPS id 41F7B3858C98 for ; Sun, 5 Oct 2025 16:36:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 41F7B3858C98 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=planete-kraus.eu Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=planete-kraus.eu ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 41F7B3858C98 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=89.234.140.182 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682180; cv=none; b=eQVsN4o1+Rd9RZRJmPfTPrjvjfX8xncGH98CU0BQK+rZUCEmSLzyUg4J0x21d7cLH6PP6wN5nED01xL9K7JvTIuIUWpFUoqosOGIfrn0mb7w9ffcVnRSEKVP7sWNPzc/eeUiBrrpqdDLnL4w52yQnF3DhZDGaqmcAgXaLS6LP6I= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682180; c=relaxed/simple; bh=1SUYyjAi6gN6uvccPj+5QRgargkkQS3jxzIN9vJPQgg=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=W2RvCQv5MlE8+/8Z4NfLbQCF/pYSJ3JeLvfOUjhZIJOq6pESJP4K3b9HfdGCKZjEU5RyvF5iNEPsurU6857MPrVGzQSBDltbWTiXG7hGm5r7HKZNRy7OBWiU2RtGOE9u0PO8W8Kpbf87liaJU0fZVlrjOsNTnty2BEsf2zYfK5E= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 41F7B3858C98 Received: from planete-kraus.eu (localhost [127.0.0.1]) by planete-kraus.eu (OpenSMTPD) with ESMTP id 731e297e for ; Sun, 5 Oct 2025 16:36:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=planete-kraus.eu; h=from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; s=albinoniA; bh=aBdDyV5 0TPwm1JEkvRYhzSbe5Cw=; b=TpMhPV1TTSWxLDtifC4t87mwe3dxyxyJldBSPW6 Z7S93kLKDWAZQLS5sLYrMm4sxCiL2G9XSm9mlzNPXRLvwMKVHrzZMZo8Tf6q0e5q RNoMHHd8w6XS7UbTfVIoDG8TBhsW2syfaKT9S7CLhrPPCkD02LxXeSH3jbXLcS6A 7zIFLY/I5gRMBwV/uhG3mWuKnq4/ujskhggW+GBgeJtziiGlu3BzAs1nuXm8fPO9 BzKhBrLpld6OGcbHbposkHxHupKDGDXbkk0VrsqOQaeJzCC3RwoXa+ZPBbbC73sv Di2lHGHL5fUxH9X9FSIfKh1jhbcAfHfTPysXyN+PxcadHRA== Received: by planete-kraus.eu (OpenSMTPD) with ESMTPSA id 3bf69b90 (TLSv1.3:TLS_CHACHA20_POLY1305_SHA256:256:NO); Sun, 5 Oct 2025 16:36:06 +0000 (UTC) From: Vivien Kraus To: libc-alpha@sourceware.org Cc: Vivien Kraus Subject: [PATCH v12 4/9] posix: let the getopt caller choose the textdomain for translation Date: Sun, 5 Oct 2025 18:34:27 +0200 Message-Id: <5c5d4df02ab39de87f2312502d3fac6314381eac.1759681925.git.vivien@planete-kraus.eu> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-12.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Using the same solution as for the option translation context, a new opttextdomain variable is defined. --- manual/argp.texi | 7 +++-- manual/getopt.texi | 11 +++++-- posix/Versions | 2 +- posix/bits/getopt_ext.h | 5 ++++ posix/getopt.c | 30 ++++++++++++------- posix/getopt1.c | 15 +++++++--- posix/getopt_int.h | 11 ++++--- posix/tstgetoptl.c | 9 ++---- sysdeps/mach/hurd/i386/libc.abilist | 1 + sysdeps/mach/hurd/x86_64/libc.abilist | 1 + sysdeps/unix/sysv/linux/aarch64/libc.abilist | 1 + sysdeps/unix/sysv/linux/alpha/libc.abilist | 1 + sysdeps/unix/sysv/linux/arc/libc.abilist | 1 + sysdeps/unix/sysv/linux/arm/be/libc.abilist | 1 + sysdeps/unix/sysv/linux/arm/le/libc.abilist | 1 + sysdeps/unix/sysv/linux/csky/libc.abilist | 1 + sysdeps/unix/sysv/linux/hppa/libc.abilist | 1 + sysdeps/unix/sysv/linux/i386/libc.abilist | 1 + .../sysv/linux/loongarch/lp64/libc.abilist | 1 + .../sysv/linux/m68k/coldfire/libc.abilist | 1 + .../unix/sysv/linux/m68k/m680x0/libc.abilist | 1 + .../sysv/linux/microblaze/be/libc.abilist | 1 + .../sysv/linux/microblaze/le/libc.abilist | 1 + .../sysv/linux/mips/mips32/fpu/libc.abilist | 1 + .../sysv/linux/mips/mips32/nofpu/libc.abilist | 1 + .../sysv/linux/mips/mips64/n32/libc.abilist | 1 + .../sysv/linux/mips/mips64/n64/libc.abilist | 1 + sysdeps/unix/sysv/linux/or1k/libc.abilist | 1 + .../linux/powerpc/powerpc32/fpu/libc.abilist | 1 + .../powerpc/powerpc32/nofpu/libc.abilist | 1 + .../linux/powerpc/powerpc64/be/libc.abilist | 1 + .../linux/powerpc/powerpc64/le/libc.abilist | 1 + .../unix/sysv/linux/riscv/rv32/libc.abilist | 1 + .../unix/sysv/linux/riscv/rv64/libc.abilist | 1 + .../unix/sysv/linux/s390/s390-32/libc.abilist | 1 + .../unix/sysv/linux/s390/s390-64/libc.abilist | 1 + sysdeps/unix/sysv/linux/sh/be/libc.abilist | 1 + sysdeps/unix/sysv/linux/sh/le/libc.abilist | 1 + .../sysv/linux/sparc/sparc32/libc.abilist | 1 + .../sysv/linux/sparc/sparc64/libc.abilist | 1 + .../unix/sysv/linux/x86_64/64/libc.abilist | 1 + .../unix/sysv/linux/x86_64/x32/libc.abilist | 1 + 42 files changed, 94 insertions(+), 30 deletions(-) diff --git a/manual/argp.texi b/manual/argp.texi index 97456ef20e..50d67b6c55 100644 --- a/manual/argp.texi +++ b/manual/argp.texi @@ -208,8 +208,11 @@ messages. @xref{Argp Help Filtering}. If non-zero, the strings used in the argp library are translated using the domain described by this string. If zero, the current default domain is used. The long option names are always translated with the -current default domain, and with the @samp{"command-line option"} -disambiguation string. +current default domain (not this one), and with the +@samp{"command-line option"} disambiguation string. This is because +all the option names, including those defined in sub-parsers, must be +in the same textdomain for @command{getopt} to process the options +correctly. @end table @end deftp diff --git a/manual/getopt.texi b/manual/getopt.texi index 65e24f1143..20be0fba69 100644 --- a/manual/getopt.texi +++ b/manual/getopt.texi @@ -59,8 +59,15 @@ names in the current textdomain. Since option names may be short words instead of long sentences, they may have different translations in other places of the program. @xref{Contexts, , Using contexts for solving ambiguities, gettext, the GNU Gettext manual}, for more -information. If this is @code{NULL}, then the translated option names -will not be processed. +information. If this is @code{NULL} (the default), then the +translated option names will not be processed. +@end deftypevar + +@deftypevar {const char *} opttextdomain +Option names may be translated in a textdomain that is not currently +the default (@pxref{Interface to gettext, , The Interface, gettext, +the GNU Gettext manual}). If this is @code{NULL} (the default), the +translation will be searched in the current text domain. @end deftypevar @deftypefun int getopt (int @var{argc}, char *const *@var{argv}, const char *@var{options}) diff --git a/posix/Versions b/posix/Versions index 5873e8ec56..d1c2cffcf9 100644 --- a/posix/Versions +++ b/posix/Versions @@ -160,7 +160,7 @@ libc { posix_spawn_file_actions_addtcsetpgrp_np; } GLIBC_2.43 { - optctxt; + optctxt; opttextdomain; } GLIBC_PRIVATE { __libc_fork; __libc_pread; __libc_pwrite; diff --git a/posix/bits/getopt_ext.h b/posix/bits/getopt_ext.h index 07d9407b64..6cd0932596 100644 --- a/posix/bits/getopt_ext.h +++ b/posix/bits/getopt_ext.h @@ -31,6 +31,11 @@ __BEGIN_DECLS names. If unset, the option names will not be translated. */ extern const char *optctxt; + +/* Callers store the textdomain to use to retrieve option names, or + NULL to use the current textdomain. */ + +extern const char *opttextdomain; #endif /* Describe the long-named options requested by the application. diff --git a/posix/getopt.c b/posix/getopt.c index 28f0be03f8..28c8aa4bbf 100644 --- a/posix/getopt.c +++ b/posix/getopt.c @@ -185,16 +185,20 @@ exchange (char **argv, struct _getopt_data *d) /* Return 1 iff translation_context is not NULL, a translation for opt_name has been found and it matches the substring from argument, length argument_length. + + The translate function pointer is like dpgettext. */ static const int -match_translated_option_name (char *(*translate) (const char *, const char *), +match_translated_option_name (char *(*translate) (const char *, const char *, + const char *), const char *argument, size_t argument_length, const char *translation_context, + const char *opt_textdomain, const char *opt_name) { const char *translated = opt_name; if (translate) - translated = translate (translation_context, opt_name); + translated = translate (opt_textdomain, translation_context, opt_name); return (!strncmp (translated, argument, argument_length) && argument_length == strlen (translated)); } @@ -212,7 +216,8 @@ process_long_option (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, struct _getopt_data *d, int print_errors, const char *prefix, - char *(*translate) (const char *, const char *)) + char *(*translate) (const char *, const char *, + const char *)) { char *nameend; size_t namelen; @@ -243,9 +248,9 @@ process_long_option (int argc, char **argv, const char *optstring, /* Didn't find an exact match, try with translated option names. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (match_translated_option_name (translate, - d->__nextchar, namelen, - d->optctxt, p->name)) + if (match_translated_option_name (translate, d->__nextchar, namelen, + d->optctxt, d->opttextdomain, + p->name)) { /* Exact match found with translation. */ pfound = p; @@ -369,7 +374,7 @@ process_long_option (int argc, char **argv, const char *optstring, /* We have found a matching long option. Consume it. */ d->optind++; d->__nextchar = NULL; - translated_option_name = translate (d->optctxt, pfound->name); + translated_option_name = translate (d->opttextdomain, d->optctxt, pfound->name); if (*nameend) { /* Don't test has_arg with >, because some C compilers don't @@ -535,7 +540,8 @@ int _getopt_internal_r (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, struct _getopt_data *d, int posixly_correct, - char *(*translate) (const char *, const char *)) + char *(*translate) (const char *, const char *, + const char *)) { int print_errors = d->opterr; @@ -771,14 +777,16 @@ int _getopt_internal (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, int posixly_correct, - char *(*translate) (const char *, const char *), - const char *ctxt) + char *(*translate) (const char *, const char *, const char *), + const char *ctxt, + const char *domain) { int result; getopt_data.optind = optind; getopt_data.opterr = opterr; getopt_data.optctxt = ctxt; + getopt_data.opttextdomain = domain; result = _getopt_internal_r (argc, argv, optstring, longopts, longind, long_only, &getopt_data, @@ -801,7 +809,7 @@ _getopt_internal (int argc, char **argv, const char *optstring, { \ return _getopt_internal (argc, (char **)argv, optstring, \ NULL, NULL, 0, POSIXLY_CORRECT, \ - NULL, NULL); \ + NULL, NULL, NULL); \ } #ifdef _LIBC diff --git a/posix/getopt1.c b/posix/getopt1.c index 28d090019d..48852dc692 100644 --- a/posix/getopt1.c +++ b/posix/getopt1.c @@ -34,9 +34,14 @@ const char *optctxt = NULL; +/* Callers store the textdomain in which the option names are to be + looked up. */ + +const char *opttextdomain = NULL; + /* FIXME: use pgettext_expr. */ static char * -do_translate (const char *context, const char *msgid) +do_translate (const char *domain, const char *context, const char *msgid) { char *full_msgid; const char *translated = msgid; @@ -49,7 +54,7 @@ do_translate (const char *context, const char *msgid) strcpy (full_msgid, context); full_msgid[strlen (context)] = '\004'; strcpy (full_msgid + strlen (context) + 1, msgid); - translated = __dcgettext (NULL, full_msgid, LC_MESSAGES); + translated = __dcgettext (domain, full_msgid, LC_MESSAGES); if (!strcmp (translated, full_msgid)) { translated = msgid; @@ -67,7 +72,8 @@ getopt_long (int argc, char *__getopt_argv_const *argv, const char *options, const struct option *long_options, int *opt_index) { return _getopt_internal (argc, (char **) argv, options, long_options, - opt_index, 0, 0, do_translate, optctxt); + opt_index, 0, 0, do_translate, + optctxt, opttextdomain); } int @@ -90,7 +96,8 @@ getopt_long_only (int argc, char *__getopt_argv_const *argv, const struct option *long_options, int *opt_index) { return _getopt_internal (argc, (char **) argv, options, long_options, - opt_index, 1, 0, do_translate, optctxt); + opt_index, 1, 0, do_translate, + optctxt, opttextdomain); } int diff --git a/posix/getopt_int.h b/posix/getopt_int.h index 1d091979c3..1deb6cad35 100644 --- a/posix/getopt_int.h +++ b/posix/getopt_int.h @@ -24,13 +24,14 @@ /* The translate argument here is optional (can be NULL), it is used to avoid depending on the gettext functions in the posix getopt - function. */ + function. It is like dpgettext. */ extern int _getopt_internal (int ___argc, char **___argv, const char *__shortopts, const struct option *__longopts, int *__longind, int __long_only, int __posixly_correct, - char *(*translate) (const char *, const char *), - const char *__optctxt); + char *(*translate) (const char *, const char *, + const char *), + const char *__optctxt, const char *__optdomain); /* Reentrant versions which can handle parsing multiple argument @@ -73,6 +74,7 @@ struct _getopt_data int optopt; char *optarg; const char *optctxt; + const char *opttextdomain; /* Internal members. */ @@ -109,7 +111,8 @@ extern int _getopt_internal_r (int ___argc, char **___argv, const struct option *__longopts, int *__longind, int __long_only, struct _getopt_data *__data, int __posixly_correct, - char *(*translate) (const char *, const char *)); + char *(*translate) (const char *, const char *, + const char *)); extern int _getopt_long_r (int ___argc, char **___argv, const char *__shortopts, diff --git a/posix/tstgetoptl.c b/posix/tstgetoptl.c index bffd56f47d..3580358a67 100644 --- a/posix/tstgetoptl.c +++ b/posix/tstgetoptl.c @@ -27,13 +27,8 @@ prepare_localedir (void) fputs ("Cannot call bindtextdomain.\n", stderr); return -1; } - if (textdomain ("tstgetoptl") == NULL) - { - fputs ("Cannot call textdomain.\n", stderr); - return -1; - } /* Check that the catalog is OK: */ - if (strcmp (gettext (TRANSLATION_CONTEXT "\004" "color"), "colour") != 0) + if (strcmp (dgettext ("tstgetoptl", TRANSLATION_CONTEXT "\004" "color"), "colour") != 0) { fputs ("The mo file does not work.\n", stderr); return -1; @@ -45,6 +40,7 @@ int main (int argc, char **argv) { static const char *translation_context = TRANSLATION_CONTEXT; + static const char *translation_textdomain = "tstgetoptl"; static const struct option options[] = { {"required", required_argument, NULL, 'r'}, @@ -72,6 +68,7 @@ main (int argc, char **argv) return 1; } optctxt = translation_context; + opttextdomain = translation_textdomain; while ((c = getopt_long (argc, argv, "abc:", options, NULL)) >= 0) switch (c) { diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist index ef2389889d..d879f5a363 100644 --- a/sysdeps/mach/hurd/i386/libc.abilist +++ b/sysdeps/mach/hurd/i386/libc.abilist @@ -2665,6 +2665,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.43 pthread_cancel F GLIBC_2.43 pthread_clockjoin_np F GLIBC_2.43 pthread_detach F diff --git a/sysdeps/mach/hurd/x86_64/libc.abilist b/sysdeps/mach/hurd/x86_64/libc.abilist index 366f44b7b3..4b6580b2cc 100644 --- a/sysdeps/mach/hurd/x86_64/libc.abilist +++ b/sysdeps/mach/hurd/x86_64/libc.abilist @@ -2346,6 +2346,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 +GLIBC_2.43 opttextdomain D 0x8 GLIBC_2.43 pthread_cancel F GLIBC_2.43 pthread_clockjoin_np F GLIBC_2.43 pthread_detach F diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist index b5adb590b1..93730d969e 100644 --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist @@ -2770,3 +2770,4 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 +GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist index c97471e756..06412be15c 100644 --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist @@ -3117,6 +3117,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 +GLIBC_2.43 opttextdomain D 0x8 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist index 7dd4589884..e0c6c9f293 100644 --- a/sysdeps/unix/sysv/linux/arc/libc.abilist +++ b/sysdeps/unix/sysv/linux/arc/libc.abilist @@ -2531,3 +2531,4 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist index 35a50182f2..7929ecb5ce 100644 --- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist @@ -2823,6 +2823,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist index 70f444545a..4b7ca1bdf0 100644 --- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist @@ -2820,6 +2820,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist index 83ac7a3a4f..326be1421c 100644 --- a/sysdeps/unix/sysv/linux/csky/libc.abilist +++ b/sysdeps/unix/sysv/linux/csky/libc.abilist @@ -2807,3 +2807,4 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist index 85cfee194d..31be97a20d 100644 --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist @@ -2844,6 +2844,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist index dea2312c5c..3100d3720d 100644 --- a/sysdeps/unix/sysv/linux/i386/libc.abilist +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist @@ -3027,6 +3027,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist index bde525090d..521343313d 100644 --- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist +++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist @@ -2291,3 +2291,4 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 +GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist index 89f275f92c..974a643266 100644 --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist @@ -2803,6 +2803,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist index 071de9934e..474da3c6e6 100644 --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist @@ -2970,6 +2970,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist index a983dd9776..3772bbf961 100644 --- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist @@ -2856,3 +2856,4 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist index d8c4d03cee..81fdbf5528 100644 --- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist @@ -2853,3 +2853,4 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist index 8431dca4fc..8bf825fbd0 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist @@ -2933,6 +2933,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist index ff0d3bcb1b..3995eb7112 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist @@ -2931,6 +2931,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist index 834ca53a26..c7f9d75936 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist @@ -2939,6 +2939,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist index e8dcdec523..fa4c0b6abb 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist @@ -2841,6 +2841,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 +GLIBC_2.43 opttextdomain D 0x8 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist index f69e29d8ec..685e674205 100644 --- a/sysdeps/unix/sysv/linux/or1k/libc.abilist +++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist @@ -2281,3 +2281,4 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist index 313b5ac2c2..b739c5b3dd 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist @@ -3160,6 +3160,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist index 9ac04c675a..25a45c7596 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist @@ -3205,6 +3205,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist index 030c44cfd6..88979415c3 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist @@ -2914,6 +2914,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 +GLIBC_2.43 opttextdomain D 0x8 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist index ba27c807a8..f55d5abaa1 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist @@ -2990,3 +2990,4 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 +GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist index 59901b5a84..d644e2b658 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist @@ -2534,3 +2534,4 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist index d861e9348a..8f90416e6f 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist @@ -2734,3 +2734,4 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 +GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist index 94eddb2157..2eb7244ede 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist @@ -3158,6 +3158,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist index a8279df601..0418207b55 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist @@ -2951,6 +2951,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 +GLIBC_2.43 opttextdomain D 0x8 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist index dab732c1d3..c882ecd323 100644 --- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist @@ -2850,6 +2850,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist index 54fc47ab6c..d8ad0073b8 100644 --- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist @@ -2847,6 +2847,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist index b73cd54dbb..fee6128da5 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist @@ -3181,6 +3181,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist index dcd144bb46..78e1358a87 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist @@ -2817,6 +2817,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 +GLIBC_2.43 opttextdomain D 0x8 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist index b9549a8de1..33afb283fa 100644 --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist @@ -2766,6 +2766,7 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 +GLIBC_2.43 opttextdomain D 0x8 GLIBC_2.5 __readlinkat_chk F GLIBC_2.5 inet6_opt_append F GLIBC_2.5 inet6_opt_find F diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist index 35fc746c3b..6bf8159064 100644 --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist @@ -2785,3 +2785,4 @@ GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 +GLIBC_2.43 opttextdomain D 0x4 From patchwork Sun Oct 5 16:34:28 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vivien Kraus X-Patchwork-Id: 121275 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9683C3858D33 for ; Sun, 5 Oct 2025 16:44:29 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 9683C3858D33 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, secure) header.d=planete-kraus.eu header.i=@planete-kraus.eu header.a=rsa-sha1 header.s=albinoniA header.b=P7s5j3cQ X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from planete-kraus.eu (planete-kraus.eu [IPv6:2a00:5881:4008:2810::309]) by sourceware.org (Postfix) with ESMTPS id DAB203858C78 for ; Sun, 5 Oct 2025 16:36:22 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DAB203858C78 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=planete-kraus.eu Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=planete-kraus.eu ARC-Filter: OpenARC Filter v1.0.0 sourceware.org DAB203858C78 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:5881:4008:2810::309 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682183; cv=none; b=ExtVEBcKRlN81UgZdvYWjuzbjRI0af6ZijGXCBTbt1SqrQtwXIFlyMPaBF1HKf1/47BQ04LceC/YG4+MeEaZTWfW9VMP3/fLIgOOCbAycv8pD/E/wbgtyAqOS8RsQUoxYe4Hr4d83d0kTbSADmdVTqhvIdB9bfY5/KEZoJwHczQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682183; c=relaxed/simple; bh=pFeScogVsVHltSxR64iiyM6D/ktitB952on31L9+m00=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=LjHDqofCuZfewvm8WcGxWFu3uMPTD0v0MgF4sS/hQZw6lpM4pRF0K0gLDSHt2zVlPVjOwBtspGINyle1CEd2F3QvDAw9VaYTiE9OSlfPU7mzwnMCtYr/us0mTXaWi5QxmcWQns9tmnwGpZRHpZaNIWRjCbZStNIqMyKJdlqjVCQ= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org DAB203858C78 Received: from planete-kraus.eu (localhost [127.0.0.1]) by planete-kraus.eu (OpenSMTPD) with ESMTP id d05b3340 for ; Sun, 5 Oct 2025 16:36:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=planete-kraus.eu; h=from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; s=albinoniA; bh=+67GtQE RnH6fW8O5QRp4KkqN3wY=; b=P7s5j3cQV/jY+PSADsp32WEn8wlDvh12p7/yx3q 9BY2hBkDhEPP8Wh8BnVTm9Y14Tvjj2LXrNfYMoDAMu1iEDG3gaNzNP5bPiM+l3K0 Sj+Wj5OBt40Ccvw1ZGLw/bM4e939mwYoGrmWaGjyuf4vM/tgMg/dqiCOFfmIFE5n jPZISLkqjSyG8fWALzWeqGZHzj1EpD1MzHEMz0ZnEoCY4gZil+2+LfVuWsU0QgR5 Czp9DUu6Bjpd/vOSI23EGs/gcmE37RXn2O4TWnSrPX8hMngVqcughux9cgCcoBv8 Qc55+20fepfcv+c6lCA8OCBwnDj/KVHLAJ306skRqE2GpiQ== Received: by planete-kraus.eu (OpenSMTPD) with ESMTPSA id e30602e5 (TLSv1.3:TLS_CHACHA20_POLY1305_SHA256:256:NO); Sun, 5 Oct 2025 16:36:06 +0000 (UTC) From: Vivien Kraus To: libc-alpha@sourceware.org Cc: Vivien Kraus Subject: [PATCH v12 5/9] posix: do not allow option name translations for secure programs Date: Sun, 5 Oct 2025 18:34:28 +0200 Message-Id: <1601b2e26c86b1fe0130bafb4511a58ca495eaef.1759681925.git.vivien@planete-kraus.eu> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org SETUID / SETGID / AT_SECURE programs should not accept translated names, so that the programmer knows exactly how the program can be invoked. --- posix/getopt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posix/getopt.c b/posix/getopt.c index 28c8aa4bbf..6b235fa4e3 100644 --- a/posix/getopt.c +++ b/posix/getopt.c @@ -197,7 +197,7 @@ match_translated_option_name (char *(*translate) (const char *, const char *, const char *opt_name) { const char *translated = opt_name; - if (translate) + if (translate != NULL && !__libc_enable_secure) translated = translate (opt_textdomain, translation_context, opt_name); return (!strncmp (translated, argument, argument_length) && argument_length == strlen (translated)); From patchwork Sun Oct 5 16:34:29 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Vivien Kraus X-Patchwork-Id: 121278 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0BF0D3858D39 for ; Sun, 5 Oct 2025 16:47:30 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0BF0D3858D39 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, secure) header.d=planete-kraus.eu header.i=@planete-kraus.eu header.a=rsa-sha1 header.s=albinoniA header.b=Fr8Xjd4K X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from planete-kraus.eu (planete-kraus.eu [IPv6:2a00:5881:4008:2810::309]) by sourceware.org (Postfix) with ESMTPS id DADC83858C36 for ; Sun, 5 Oct 2025 16:36:22 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DADC83858C36 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=planete-kraus.eu Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=planete-kraus.eu ARC-Filter: OpenARC Filter v1.0.0 sourceware.org DADC83858C36 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:5881:4008:2810::309 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682183; cv=none; b=L9ELxjteZDMxCCkk3xRKNMq6zqAtwTXn6SnYAx9Fr3O3asFXzB2uoZFguoSJlTbIQYKOMkLdqQSuQJTTgzw9R3Gm8CC/NBaeakA+sw7mTna7FkEvUyTtHCqElT4DLYfZZgTRg+1G506uJA8dZncShS3XbZM2qbXbTo37jvPZXRA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682183; c=relaxed/simple; bh=Bco2F2x4srUIhNuXorOwx8nFu22OKxTwlY8yGmb7lzA=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=NOif/xeeFyCnxBxH3m9ks6Mj80ao/l2bRcK21nH+8eeDCNQjG6J2U3wzI8Z8RLM1uDYoqCz2yZjUy9Dc1eNLe3RfIwNr8EjhXntB6a4e2XWYfnAvDrdixKznHtSgVqjFTeNWeb6qWHAlw+pqY31SVAByhoH29BlUYogMkBkqKhE= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org DADC83858C36 Received: from planete-kraus.eu (localhost [127.0.0.1]) by planete-kraus.eu (OpenSMTPD) with ESMTP id 5bb6e355 for ; Sun, 5 Oct 2025 16:36:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=planete-kraus.eu; h=from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; s= albinoniA; bh=7gjLIbUhrgQWoHW2TGvKbdWHKZ0=; b=Fr8Xjd4KTuPL0EyBB0 GrZ7YONojPrEaF4xUY4NadnSMRRVSbxU10mMWhm6C3SEwOgi0I3OrsWGqOZiKbYR T0GGNiQ3El84MaP/U95PNoeU59SHKE3wj7hQAMT2SbyYCQ1aVqgELcK43D6yHKd5 AJa0YW2LOmmAgD7xMmihvU6FN7Q+rNPYYUMBcuUz9+ymoYR/jF5ux4iZH8NQClR+ lS7qTwWTSsv1idBga5dLaPlQkmvKX9eHlrSDGhi1Ra3BNvJ6b7vRy+e2kbqfm4rd EvgKS7FqbHPzMbYU1Re4B6a9eO+xaMteiS/g7vDKOnDQlQYsli+BQsN/zDpP8PVZ M1bw== Received: by planete-kraus.eu (OpenSMTPD) with ESMTPSA id e63b1085 (TLSv1.3:TLS_CHACHA20_POLY1305_SHA256:256:NO); Sun, 5 Oct 2025 16:36:07 +0000 (UTC) From: Vivien Kraus To: libc-alpha@sourceware.org Cc: Vivien Kraus Subject: [PATCH v12 6/9] posix, argp: Support deprecation of long option name translations Date: Sun, 5 Oct 2025 18:34:29 +0200 Message-Id: <4141d2c839f64ca8ddc5278627927c49384fca7c.1759681925.git.vivien@planete-kraus.eu> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org The translation for an option name is expected to be a space-separated list. The first field is the canonical translation. The other fields are deprecated translations. getopt will warn if they are recognized, and argp will not print them in help or usage output. I chose a space because it would be a bad choice to have a space character in an option name. --- argp/argp-help.c | 101 +++++++++++++++++++-------------- argp/tst-argphelp-localized.c | 69 +++++++++++++++++++++- argp/tst-argphelp-localized.po | 7 ++- argp/tst-argpusage-localized.c | 2 +- manual/getopt.texi | 5 ++ posix/Makefile | 2 +- posix/getopt.c | 62 +++++++++++++++++++- posix/tstgetoptl.c | 19 +++++-- posix/tstgetoptl.po | 4 +- 9 files changed, 214 insertions(+), 57 deletions(-) diff --git a/argp/argp-help.c b/argp/argp-help.c index 672e733fc8..08650ea5b1 100644 --- a/argp/argp-help.c +++ b/argp/argp-help.c @@ -1205,6 +1205,45 @@ comma (unsigned col, struct pentry_state *pest) indent_to (pest->stream, col); } +/* Return the canonical translation of an option name. There might be + multiple alternative translation for backward compatibility, but + only one should be documented. */ +static const char * +canonical_option_translation (const char *option_name, + char **allocated) +{ + char *option_msgid; + const char *all_names = option_name; + size_t canonical_length; + static const char *default_context = "command-line option\004"; + *allocated = NULL; + option_msgid = malloc (strlen (default_context) + strlen (option_name) + 1); + all_names = NULL; + if (option_msgid) + { + strcpy (option_msgid, default_context); + strcat (option_msgid, option_name); + all_names = gettext (option_msgid); + if (strcmp (all_names, option_msgid) == 0) + all_names = option_name; + } + if (strcmp (all_names, option_name) == 0) + all_names = option_name; + free (option_msgid); + if (strchr (all_names, ' ')) + { + canonical_length = strchr (all_names, ' ') - all_names; + *allocated = malloc (canonical_length + 1); + if (*allocated != NULL) + { + memcpy (*allocated, all_names, canonical_length); + (*allocated)[canonical_length] = '\0'; + all_names = *allocated; + } + } + return all_names; +} + /* Print help for ENTRY to STREAM. */ static void hol_entry_help (struct hol_entry *entry, const struct argp_state *state, @@ -1213,8 +1252,8 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state, unsigned num; const struct argp_option *real = entry->opt, *opt; char *so = entry->short_options; - char *option_msgid; - const char *translated_option_name; + char *canonical_translation_buffer; + const char *canonical_translation; int have_long_opt = 0; /* We have any long options. */ /* Saved margins. */ int old_lm = __argp_fmtstream_set_lmargin (stream, 0); @@ -1278,25 +1317,15 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state, if (opt->name && ovisible (opt)) { comma (uparams.long_opt_col, &pest); - option_msgid = - malloc (strlen ("command-line option\004") - + strlen (opt->name) + 1); - if (option_msgid) - { - strcpy (option_msgid, "command-line option\004"); - strcat (option_msgid, opt->name); - translated_option_name = gettext (option_msgid); - if (!strcmp (translated_option_name, option_msgid)) - translated_option_name = opt->name; - } - else - translated_option_name = opt->name; - __argp_fmtstream_printf (stream, "--%s", translated_option_name); + canonical_translation = + canonical_option_translation (opt->name, + &canonical_translation_buffer); + __argp_fmtstream_printf (stream, "--%s", canonical_translation); arg (real, "=%s", "[=%s]", state == NULL ? NULL : state->root_argp->argp_domain, stream); - if (strcmp (translated_option_name, opt->name)) + if (strcmp (canonical_translation, opt->name)) __argp_fmtstream_printf (stream, " (--%s)", opt->name); - free (option_msgid); + free (canonical_translation_buffer); } } @@ -1438,8 +1467,8 @@ usage_long_opt (const struct argp_option *opt, { argp_fmtstream_t stream = cookie; const char *arg = opt->arg; - char *option_msgid; - const char *translated_opt_name = opt->name; + const char *canonical_translation = opt->name; + char *canonical_translation_buffer; int flags = opt->flags | real->flags; if (! arg) @@ -1447,41 +1476,29 @@ usage_long_opt (const struct argp_option *opt, if (! (flags & OPTION_NO_USAGE)) { - /* Since we cannot customize the translation context, we will - use a default one. FIXME: use pgettext_expr(). */ - static const char *default_context = "command-line option\004"; - option_msgid = malloc (strlen (default_context) + strlen (opt->name) + 1); - translated_opt_name = NULL; - if (option_msgid) - { - strcpy (option_msgid, default_context); - strcat (option_msgid, opt->name); - translated_opt_name = gettext (option_msgid); - if (!strcmp (translated_opt_name, option_msgid)) - translated_opt_name = opt->name; - } - if (!strcmp (translated_opt_name, opt->name)) - translated_opt_name = NULL; + canonical_translation = + canonical_option_translation (opt->name, &canonical_translation_buffer); if (arg) { arg = dgettext (domain, arg); - if ((flags & OPTION_ARG_OPTIONAL) && translated_opt_name) + if ((flags & OPTION_ARG_OPTIONAL) + && strcmp (canonical_translation, opt->name) != 0) __argp_fmtstream_printf (stream, " [--%s[=%s] (--%s)]", - translated_opt_name, arg, opt->name); + canonical_translation, arg, opt->name); else if (flags & OPTION_ARG_OPTIONAL) __argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg); - else if (translated_opt_name) + else if (strcmp (canonical_translation, opt->name) != 0) __argp_fmtstream_printf (stream, " [--%s=%s (--%s)]", - translated_opt_name, arg, opt->name); + canonical_translation, arg, opt->name); else __argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg); } - else if (translated_opt_name) + else if (strcmp (canonical_translation, opt->name) != 0) __argp_fmtstream_printf (stream, " [--%s (--%s)]", - translated_opt_name, opt->name); + canonical_translation, opt->name); else __argp_fmtstream_printf (stream, " [--%s]", opt->name); - free (option_msgid); + free (canonical_translation_buffer); } return 0; diff --git a/argp/tst-argphelp-localized.c b/argp/tst-argphelp-localized.c index abc3325827..214f0d992f 100644 --- a/argp/tst-argphelp-localized.c +++ b/argp/tst-argphelp-localized.c @@ -37,10 +37,14 @@ const char *argp_program_version = "argphelp-test 1.0"; struct argp_option options[] = { {PN_ ("command-line option", "color"), 'c', 0, 0, "Rainbow!"}, + {PN_ ("command-line option", "flavor"), 'f', 0, 0, "Sweet!"}, + {PN_ ("command-line option", "texture"), 't', 0, 0, "Smooth!"}, {0} }; static int color_set = 0; +static int flavor_set = 0; +static int texture_set = 0; static error_t parse_opt (int key, char *arg, struct argp_state *state) @@ -55,6 +59,24 @@ parse_opt (int key, char *arg, struct argp_state *state) { color_set = 1; } + if (key == 'f' && flavor_set) + { + fprintf (stderr, "%s:%d: flavor already set.\n", __FILE__, __LINE__); + abort (); + } + else if (key == 'f') + { + flavor_set = 1; + } + else if (key == 't' && texture_set) + { + fprintf (stderr, "%s:%d: texture already set.\n", __FILE__, __LINE__); + abort (); + } + else if (key == 't') + { + texture_set = 1; + } return 0; } @@ -66,6 +88,16 @@ main (int argc, char *argv[]) char *test1_argv[3] = { (char *) "/bin/tst-argphelp-localized", (char *) "--colour", NULL }; char *test2_argv[3] = + { (char *) "/bin/tst-argphelp-localized", (char *) "--color", NULL }; + char *test3_argv[3] = + { (char *) "/bin/tst-argphelp-localized", (char *) "--coolur", NULL }; + char *test4_argv[3] = + { (char *) "/bin/tst-argphelp-localized", (char *) "--flavour", NULL }; + char *test5_argv[3] = + { (char *) "/bin/tst-argphelp-localized", (char *) "--flavor", NULL }; + char *test6_argv[3] = + { (char *) "/bin/tst-argphelp-localized", (char *) "--texture", NULL }; + char *test7_argv[3] = { (char *) "/bin/tst-argphelp-localized", (char *) "--help", NULL }; unsetenv ("LANGUAGE"); @@ -83,7 +115,7 @@ main (int argc, char *argv[]) abort (); } /* Check that the catalog is OK: */ - if (strcmp (gettext ("command-line option\004color"), "colour") != 0) + if (strcmp (gettext ("command-line option\004color"), "colour coolur") != 0) { fprintf (stderr, "%s:%d: the mo file does not work.\n", __FILE__, __LINE__); @@ -95,7 +127,42 @@ main (int argc, char *argv[]) fprintf (stderr, "%s:%d: color not set.\n", __FILE__, __LINE__); abort (); } + color_set = 0; argp_parse (&argp, 2, test2_argv, 0, 0, NULL); + if (!color_set) + { + fprintf (stderr, "%s:%d: color not set.\n", __FILE__, __LINE__); + abort (); + } + color_set = 0; + argp_parse (&argp, 2, test3_argv, 0, 0, NULL); + if (!color_set) + { + fprintf (stderr, "%s:%d: color not set.\n", __FILE__, __LINE__); + abort (); + } + flavor_set = 0; + argp_parse (&argp, 2, test4_argv, 0, 0, NULL); + if (!flavor_set) + { + fprintf (stderr, "%s:%d: flavor not set.\n", __FILE__, __LINE__); + abort (); + } + flavor_set = 0; + argp_parse (&argp, 2, test5_argv, 0, 0, NULL); + if (!flavor_set) + { + fprintf (stderr, "%s:%d: flavor not set.\n", __FILE__, __LINE__); + abort (); + } + texture_set = 0; + argp_parse (&argp, 2, test6_argv, 0, 0, NULL); + if (!flavor_set) + { + fprintf (stderr, "%s:%d: texture not set.\n", __FILE__, __LINE__); + abort (); + } + argp_parse (&argp, 2, test7_argv, 0, 0, NULL); fprintf (stderr, "%s:%d: --help did not exit the program.\n", __FILE__, __LINE__); abort (); return 0; diff --git a/argp/tst-argphelp-localized.po b/argp/tst-argphelp-localized.po index 4e301bf278..e87330f7b5 100644 --- a/argp/tst-argphelp-localized.po +++ b/argp/tst-argphelp-localized.po @@ -15,4 +15,9 @@ msgstr "" #: tst-argphelp-localized.c:73 msgctxt "command-line option" msgid "color" -msgstr "colour" \ No newline at end of file +msgstr "colour coolur" + +#: tst-argphelp-localized.c:74 +msgctxt "command-line option" +msgid "flavor" +msgstr "flavour" \ No newline at end of file diff --git a/argp/tst-argpusage-localized.c b/argp/tst-argpusage-localized.c index 4061609fc3..bb8865bc99 100644 --- a/argp/tst-argpusage-localized.c +++ b/argp/tst-argpusage-localized.c @@ -69,7 +69,7 @@ main (int argc, char *argv[]) abort (); } /* Check that the catalog is OK: */ - if (strcmp (gettext ("command-line option\004color"), "colour") != 0) + if (strcmp (gettext ("command-line option\004color"), "colour coolur") != 0) { fprintf (stderr, "%s:%d: the mo file does not work.\n", __FILE__, __LINE__); diff --git a/manual/getopt.texi b/manual/getopt.texi index 20be0fba69..7834f1c2a3 100644 --- a/manual/getopt.texi +++ b/manual/getopt.texi @@ -228,6 +228,11 @@ communication involves the invocation of your program, the program users should be encouraged to use untranslated option names, or publish the locale used for this invocation. +If the translation of an option name contains a space character, then +it means multiple translations recognize the same option name. This +is useful to upgrade a translation without disrupting the user's +workflow. + @deftp {Data Type} {struct option} @standards{GNU, getopt.h} This structure describes a single long option name for the sake of diff --git a/posix/Makefile b/posix/Makefile index 1c787f53fb..1a2cdb053f 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -614,7 +614,7 @@ CFLAGS-fork.c = $(libio-mtsafe) $(config-cflags-wno-ignored-attributes) tstgetopt-ARGS = -a -b -cfoobar --required foobar --optional=bazbug \ --none random --col --color --colour -tstgetoptl-ARGS = $(tstgetopt-ARGS) +tstgetoptl-ARGS = $(tstgetopt-ARGS) --coolur tst-exec-ARGS = -- $(host-test-program-cmd) tst-exec-static-ARGS = $(tst-exec-ARGS) diff --git a/posix/getopt.c b/posix/getopt.c index 6b235fa4e3..55afde8b4c 100644 --- a/posix/getopt.c +++ b/posix/getopt.c @@ -191,16 +191,70 @@ exchange (char **argv, struct _getopt_data *d) static const int match_translated_option_name (char *(*translate) (const char *, const char *, const char *), + const char *program_name, const char *prefix, const char *argument, size_t argument_length, const char *translation_context, const char *opt_textdomain, const char *opt_name) { const char *translated = opt_name; + /* Multiple alternative names can be provided by the translator, so + that continuous improvement of translations is possible. To + allow multiple translations, separate the translation with a + space character. */ + int canonical = 1; + const char *names_list; + const char *next_item; + size_t canonical_length; + char *canonical_name; + char *matched_name; + size_t item_length; if (translate != NULL && !__libc_enable_secure) translated = translate (opt_textdomain, translation_context, opt_name); - return (!strncmp (translated, argument, argument_length) - && argument_length == strlen (translated)); + if (strlen (translated) == argument_length + && strncmp (translated, argument, argument_length) == 0 + && !strchr (translated, ' ')) + return 1; + else if (!strchr (translated, ' ')) + return 0; + names_list = translated; + while (names_list != NULL) + { + item_length = strlen (names_list); + next_item = strchr (names_list, ' '); + if (next_item) + { + item_length = next_item - names_list; + next_item++; + } + if (item_length == argument_length + && strncmp (names_list, argument, argument_length) == 0) + { + if (!canonical) + { + canonical_length = strchr (translated, ' ') - translated; + canonical_name = malloc (canonical_length + 1); + matched_name = malloc (item_length + 1); + if (canonical_name != NULL && matched_name != NULL) + { + memcpy (canonical_name, translated, canonical_length); + canonical_name[canonical_length] = '\0'; + memcpy (matched_name, names_list, item_length); + matched_name[item_length] = '\0'; + fprintf (stderr, _("%s: option '%s%s' is deprecated, use '%s%s' instead\n"), + program_name, + prefix, matched_name, + prefix, canonical_name); + } + free (canonical_name); + free (matched_name); + } + return 1; + } + canonical = 0; + names_list = next_item; + } + return 0; } /* Process the argument starting with d->__nextchar as a long option. @@ -248,7 +302,9 @@ process_long_option (int argc, char **argv, const char *optstring, /* Didn't find an exact match, try with translated option names. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (match_translated_option_name (translate, d->__nextchar, namelen, + if (match_translated_option_name (translate, + argv[0], prefix, + d->__nextchar, namelen, d->optctxt, d->opttextdomain, p->name)) { diff --git a/posix/tstgetoptl.c b/posix/tstgetoptl.c index 3580358a67..75abdac8ef 100644 --- a/posix/tstgetoptl.c +++ b/posix/tstgetoptl.c @@ -10,8 +10,12 @@ different options with the same short name, it has only one, and the other one is a translation. */ -/* This uses the en_GB locale so that colour means color. As a - special case, we also check that non-translated options have +/* This uses the en_GB locale so that colour means color. Oh no! The + translator made a mistake and translated with “coolur”. A bug-fix + has been released, but it has been decided to support both “colour” + and “coolur” with a deprecation warning. + + As a special case, we also check that non-translated options have precedence over translated options, by translated "optional" as "required". */ @@ -28,7 +32,9 @@ prepare_localedir (void) return -1; } /* Check that the catalog is OK: */ - if (strcmp (dgettext ("tstgetoptl", TRANSLATION_CONTEXT "\004" "color"), "colour") != 0) + if (strcmp (dgettext ("tstgetoptl", + TRANSLATION_CONTEXT "\004" "color"), + "colour coolur") != 0) { fputs ("The mo file does not work.\n", stderr); return -1; @@ -47,11 +53,12 @@ main (int argc, char **argv) {"optional", optional_argument, NULL, 'o'}, {"none", no_argument, NULL, 'n'}, {"color", no_argument, NULL, 'C'}, - /* Now colour is handled as a translation of color */ + /* Now colour (and coolur) is handled as a translation of color */ {NULL, 0, NULL, 0 } }; - /* The rest of the function is the same as in tstgetopt.c. */ + /* The rest of the function is the same as in tstgetopt.c, except we + expect 4 instances of -C instead of just 3. */ int aflag = 0; int bflag = 0; @@ -109,7 +116,7 @@ main (int argc, char **argv) aflag, bflag, cvalue, Cflag, nflag); result |= (aflag != 1 || bflag != 1 || cvalue == NULL - || strcmp (cvalue, "foobar") != 0 || Cflag != 3 || nflag != 1); + || strcmp (cvalue, "foobar") != 0 || Cflag != 4 || nflag != 1); for (index = optind; index < argc; index++) printf ("Non-option argument %s\n", argv[index]); diff --git a/posix/tstgetoptl.po b/posix/tstgetoptl.po index e060c0d6e3..f418776a6b 100644 --- a/posix/tstgetoptl.po +++ b/posix/tstgetoptl.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: tstgetoptl 0.0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-05-27 19:29+0200\n" -"PO-Revision-Date: 2025-05-27 19:30+0200\n" +"PO-Revision-Date: 2025-06-06 21:22+0200\n" "Language-Team: English (British) <(nothing)>\n" "Language: en_GB\n" "MIME-Version: 1.0\n" @@ -18,7 +18,7 @@ msgstr "" #: xxx.c:yy msgctxt "command-line option" msgid "color" -msgstr "colour" +msgstr "colour coolur" # This is to make sure the translator cannot redirect options. #: xxx.c:yy From patchwork Sun Oct 5 16:34:30 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vivien Kraus X-Patchwork-Id: 121272 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 182C63858D29 for ; Sun, 5 Oct 2025 16:40:04 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 182C63858D29 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, secure) header.d=planete-kraus.eu header.i=@planete-kraus.eu header.a=rsa-sha1 header.s=albinoniA header.b=WDOsjF+F X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from planete-kraus.eu (planete-kraus.eu [IPv6:2a00:5881:4008:2810::309]) by sourceware.org (Postfix) with ESMTPS id CCB383858C24 for ; Sun, 5 Oct 2025 16:36:25 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org CCB383858C24 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=planete-kraus.eu Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=planete-kraus.eu ARC-Filter: OpenARC Filter v1.0.0 sourceware.org CCB383858C24 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:5881:4008:2810::309 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682186; cv=none; b=bFmGM+ZE24b0br/WbSSVWT1nw5J8UYKFI5ssWepXxfe77qYLUjcjQ2UbG21mN0xKAUnte1q8pI1DSCXgl+PJrCtJ9JVCOt/boIosp3byeNaYiqIAJh8xKicp8lJVSh0JGURHXL/Ez8g+GJOu7Y+8xAy3XVfsykwDGxhvNpgpDqI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682186; c=relaxed/simple; bh=XVSlSjIr4Gttwmw05dQ4lmSOcOfpA4Mq2j4Jr/d2kQ0=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=X+0Ov+LAUAvFFPgjy4bn4PgZEKOeuo6sfPSImJDnYjpRkyRUbETEUhaB715+unJDodgcdozkX2CN4NWA7PnocDzpY6hrNKfa3p6gtvjNmRdLV715BmIM4KZWFeL+27sm3SeyO0MXMXXA7JMMfnxf5zpKWWuFJxa7fXaAlw8kZVA= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CCB383858C24 Received: from planete-kraus.eu (localhost [127.0.0.1]) by planete-kraus.eu (OpenSMTPD) with ESMTP id 53e1dd09 for ; Sun, 5 Oct 2025 16:36:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=planete-kraus.eu; h=from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; s=albinoniA; bh=nm+0/hz Q/jKc0er7I4ELF/bHmco=; b=WDOsjF+FRlNGFsWDy6bq3LCbalkRAEKNCgmOEf1 3FB34qiCgET/6V5TEg38jll/cTWKj28nh62+2SezJrr/9Z4Z7ZODeQ1MS/ry5ZN5 5gluzCO4xap1aVKzrRtcxVizt3YPnrnAYDsUoBHP3uwFjGVMcP1wsxaWopOd2fMb MqEgs7zHOfSyMaoGIrwtNefeiePQf4RvjBDWqQr8FqyjZcd7prg+oRjxNX7yaSYG xmfRA8zu4kqotl8/Th2KO/Yz7XlgSXo1smm0ghljK24i4Kiyzv/Vk91jkaERadCH D2zyym52lsoy5Sy2HFffExn9pklqNd2SswsiuAwMpKo4XTg== Received: by planete-kraus.eu (OpenSMTPD) with ESMTPSA id 15d9edf5 (TLSv1.3:TLS_CHACHA20_POLY1305_SHA256:256:NO); Sun, 5 Oct 2025 16:36:07 +0000 (UTC) From: Vivien Kraus To: libc-alpha@sourceware.org Cc: Vivien Kraus Subject: [PATCH v12 7/9] argp: do not display option name translations if __libc_enable_secure Date: Sun, 5 Oct 2025 18:34:30 +0200 Message-Id: <7d5044e53c6064532dbdd4ac660331e221216590.1759681925.git.vivien@planete-kraus.eu> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org --- argp/argp-help.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/argp/argp-help.c b/argp/argp-help.c index 08650ea5b1..2feb2a1ac6 100644 --- a/argp/argp-help.c +++ b/argp/argp-help.c @@ -1217,6 +1217,9 @@ canonical_option_translation (const char *option_name, size_t canonical_length; static const char *default_context = "command-line option\004"; *allocated = NULL; + if (__libc_enable_secure) + /* Translations are disabled. */ + return option_name; option_msgid = malloc (strlen (default_context) + strlen (option_name) + 1); all_names = NULL; if (option_msgid) From patchwork Sun Oct 5 16:34:31 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Vivien Kraus X-Patchwork-Id: 121277 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 287953858D39 for ; Sun, 5 Oct 2025 16:45:25 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 287953858D39 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, secure) header.d=planete-kraus.eu header.i=@planete-kraus.eu header.a=rsa-sha1 header.s=albinoniA header.b=c931iBy1 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from planete-kraus.eu (planete-kraus.eu [89.234.140.182]) by sourceware.org (Postfix) with ESMTPS id 7B2F13858C20 for ; Sun, 5 Oct 2025 16:36:26 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7B2F13858C20 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=planete-kraus.eu Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=planete-kraus.eu ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 7B2F13858C20 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=89.234.140.182 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682186; cv=none; b=csXQv22Cerm6D9+4pkzw1qOKgb6K26CEo/U6sfgVdh55nvbtQDVAkA48Z1e17uO99fa163QuF93ZCeswocBLf77otuqnYvf9yYFU3pQxiHJbQQgSSyardUbBlmWFmbnt3M6S6DA7fTtrzMrsLwsC6rumZbCCEFkLuhpZzV38GZE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682186; c=relaxed/simple; bh=ExyhmoFLG05K/u2/viZAz6IaM0687HuUmBu9Kb1igX0=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=ncz9kPUjIdIZ7tYF2SFFGJKXEcQP478oDR+6selKf6i4GTdQ8CzMtMAKROc6wU2TJT1tzJYvVCWSuY6tjlh9B9Sf8EZIR9sv7brtaV2Z2v/DD6DI7RuvKt44+7brhtAhx/hMNFaZ01hxd1SFWkPps9IWKUK28chw4RxSEVXnGl0= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7B2F13858C20 Received: from planete-kraus.eu (localhost [127.0.0.1]) by planete-kraus.eu (OpenSMTPD) with ESMTP id 8273d686 for ; Sun, 5 Oct 2025 16:36:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=planete-kraus.eu; h=from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; s= albinoniA; bh=mn0GyWPXD7FCgEkgjklSIqSdxK0=; b=c931iBy1KSwVk7mYJx KpogWTLwWuXvw41KFkw58mfNTjhplkwqa6Nr70L157365sduxXcsj1zcQVpkLYbV tjJDLiwdm9nx7W95QT6GlH7EzZAggiDUGeYGp1Nuxom4eEcYOK+Nlz80DpzAiIip RvqLEI019CzIN6Q091He3BFdsrrQHd9/RoiTOO4cT7pBQi3ooll4ERXeXPamKFw/ RjMgAUorodZnmxHDAYvPcZxQhF8JZLhALaShBk0VzsLnq9pc4MAPeEO1i5IHivpK UsJiGq8lMmCYcnj0SKv59butdqoSreugULvDS2ugeUMnWUCf/IVhZlk3NiiPCFKJ EfnQ== Received: by planete-kraus.eu (OpenSMTPD) with ESMTPSA id 21a88f78 (TLSv1.3:TLS_CHACHA20_POLY1305_SHA256:256:NO); Sun, 5 Oct 2025 16:36:07 +0000 (UTC) From: Vivien Kraus To: libc-alpha@sourceware.org Cc: Vivien Kraus Subject: [PATCH v12 8/9] posix: Add getopt_long_collision Date: Sun, 5 Oct 2025 18:34:31 +0200 Message-Id: <4e401a6d57846f54cf6f12ce1d8882b81f51d679.1759681925.git.vivien@planete-kraus.eu> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-12.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org This function is used before introducing a new option name with all known linguas to check that no translator has used the exact same name as a translation for a different option. It only checks for untranslated / translated collision, because: - translated / translated conflicts in the same language can be solved by the translator; - translated / translated conflicts in another language will not happen during a call to getopt_long. When a collision is detected, it only returns the untranslated side of the collision because the translator for the current locale knows which option conflicts with the untranslated one. It would maybe more useful for developers to have a maintainer script doing equivalent work, but this function serves as a way to remind them to check for collisions and discuss them with the translators before introducing a new option name. --- manual/getopt.texi | 22 +++++++ posix/Makefile | 10 +++ posix/Versions | 2 +- posix/bits/getopt_ext.h | 5 ++ posix/getopt1.c | 49 ++++++++++++++ posix/tst-getopt_long_collision.c | 66 +++++++++++++++++++ posix/tst-getopt_long_collision.po | 22 +++++++ sysdeps/mach/hurd/i386/libc.abilist | 1 + sysdeps/mach/hurd/x86_64/libc.abilist | 1 + sysdeps/unix/sysv/linux/aarch64/libc.abilist | 1 + sysdeps/unix/sysv/linux/alpha/libc.abilist | 1 + sysdeps/unix/sysv/linux/arc/libc.abilist | 1 + sysdeps/unix/sysv/linux/arm/be/libc.abilist | 1 + sysdeps/unix/sysv/linux/arm/le/libc.abilist | 1 + sysdeps/unix/sysv/linux/csky/libc.abilist | 1 + sysdeps/unix/sysv/linux/hppa/libc.abilist | 1 + sysdeps/unix/sysv/linux/i386/libc.abilist | 1 + .../sysv/linux/loongarch/lp64/libc.abilist | 1 + .../sysv/linux/m68k/coldfire/libc.abilist | 1 + .../unix/sysv/linux/m68k/m680x0/libc.abilist | 1 + .../sysv/linux/microblaze/be/libc.abilist | 1 + .../sysv/linux/microblaze/le/libc.abilist | 1 + .../sysv/linux/mips/mips32/fpu/libc.abilist | 1 + .../sysv/linux/mips/mips32/nofpu/libc.abilist | 1 + .../sysv/linux/mips/mips64/n32/libc.abilist | 1 + .../sysv/linux/mips/mips64/n64/libc.abilist | 1 + sysdeps/unix/sysv/linux/or1k/libc.abilist | 1 + .../linux/powerpc/powerpc32/fpu/libc.abilist | 1 + .../powerpc/powerpc32/nofpu/libc.abilist | 1 + .../linux/powerpc/powerpc64/be/libc.abilist | 1 + .../linux/powerpc/powerpc64/le/libc.abilist | 1 + .../unix/sysv/linux/riscv/rv32/libc.abilist | 1 + .../unix/sysv/linux/riscv/rv64/libc.abilist | 1 + .../unix/sysv/linux/s390/s390-32/libc.abilist | 1 + .../unix/sysv/linux/s390/s390-64/libc.abilist | 1 + sysdeps/unix/sysv/linux/sh/be/libc.abilist | 1 + sysdeps/unix/sysv/linux/sh/le/libc.abilist | 1 + .../sysv/linux/sparc/sparc32/libc.abilist | 1 + .../sysv/linux/sparc/sparc64/libc.abilist | 1 + .../unix/sysv/linux/x86_64/64/libc.abilist | 1 + .../unix/sysv/linux/x86_64/x32/libc.abilist | 1 + 41 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 posix/tst-getopt_long_collision.c create mode 100644 posix/tst-getopt_long_collision.po diff --git a/manual/getopt.texi b/manual/getopt.texi index 7834f1c2a3..e769b0ee70 100644 --- a/manual/getopt.texi +++ b/manual/getopt.texi @@ -383,6 +383,28 @@ not match a long option (or its abbreviation). @end deftypefun +It is possible for the programmer to introduce a new option name that +conflicts with the translation of an existing option name. Such a +case would disrupt the workflow of users as the new option would +replace the existing option. Before adding a new option to a program, +the developer should check for collisions with all known translations. + +@deftypefun int getopt_long_collision (const struct option *@var{longopts}, const char *@var{context}, const char *@var{domain}, const struct option **@var{first_collision}) + +Check whether there is a collision with any of the untranslated names +of @var{longopts} and any of the translated names in the current +locale. Translations are looked up with @var{context}, in +@var{domain}. Set @var{first_collision} to @code{NULL}, or to the +address of the first option whose untranslated name collides with +another option’s translated name. + +If the collision happens between two identical translations of +different options, it is not recognized, as it is simply a problem to +be solved by the translator. + +Return 0 if no collisions are detected, 1 if a collision is detected. +@end deftypefun + @node Getopt Long Option Example @subsection Example of Parsing Long Options with @code{getopt_long} diff --git a/posix/Makefile b/posix/Makefile index 1a2cdb053f..1bdb0b2585 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -288,6 +288,7 @@ tests := \ tst-fork \ tst-gai_strerror \ tst-getopt_long1 \ + tst-getopt_long_collision \ tst-glob-tilde \ tst-glob_symlinks \ tst-gnuglob \ @@ -342,6 +343,15 @@ $(tstgetoptl_mo): tstgetoptl.po $(objpfx)tstgetoptl.out: $(tstgetoptl_mo) CFLAGS-tstgetoptl.c += -DOBJPFX=\"$(objpfx)\" +tst_getopt_long_collision_mo = $(objpfx)domaindir/fr_FR/LC_MESSAGES/tst-getopt_long_collision.mo +$(tst_getopt_long_collision_mo): tst-getopt_long_collision.po + $(make-target-directory) + msgfmt -o $@T $< + mv -f $@T $@ + +$(objpfx)tst-getopt_long_collision.out: $(tst_getopt_long_collision_mo) +CFLAGS-tst-getopt_long_collision.c += -DOBJPFX=\"$(objpfx)\" + # Test for the glob symbol version that was replaced in glibc 2.27. ifeq ($(have-GLIBC_2.26)$(build-shared),yesyes) tests += \ diff --git a/posix/Versions b/posix/Versions index d1c2cffcf9..5b4c2e753e 100644 --- a/posix/Versions +++ b/posix/Versions @@ -160,7 +160,7 @@ libc { posix_spawn_file_actions_addtcsetpgrp_np; } GLIBC_2.43 { - optctxt; opttextdomain; + optctxt; opttextdomain; getopt_long_collision; } GLIBC_PRIVATE { __libc_fork; __libc_pread; __libc_pwrite; diff --git a/posix/bits/getopt_ext.h b/posix/bits/getopt_ext.h index 6cd0932596..9c7f940d1f 100644 --- a/posix/bits/getopt_ext.h +++ b/posix/bits/getopt_ext.h @@ -84,6 +84,11 @@ extern int getopt_long_only (int ___argc, char *__getopt_argv_const *___argv, const struct option *__longopts, int *__longind) __THROW __nonnull ((2, 3)); +extern int getopt_long_collision (const struct option *__longopts, + const char *__context, + const char *__domain, + const struct option **__first_collision); + __END_DECLS #endif /* getopt_ext.h */ diff --git a/posix/getopt1.c b/posix/getopt1.c index 48852dc692..1e1265c531 100644 --- a/posix/getopt1.c +++ b/posix/getopt1.c @@ -109,6 +109,55 @@ _getopt_long_only_r (int argc, char **argv, const char *options, 1, d, 0, do_translate); } +int +getopt_long_collision (const struct option *long_options, + const char *context, + const char *domain, + const struct option **first_collision) +{ + size_t n_options = 0; + size_t untranslated_option_index, translated_option_index; + const char *names_list; + const char *item_end; + size_t item_length; + const struct option *untranslated; + const struct option *translated; + + *first_collision = NULL; + for (n_options = 0; long_options[n_options].name; n_options++) + ; + for (untranslated_option_index = 0; + untranslated_option_index < n_options; + untranslated_option_index++) + for (translated_option_index = 0; + translated_option_index < n_options; + translated_option_index++) + if (translated_option_index != untranslated_option_index) + { + untranslated = &(long_options[untranslated_option_index]); + translated = &(long_options[translated_option_index]); + names_list = do_translate (domain, context, translated->name); + while (names_list) + { + item_length = strlen (names_list); + item_end = strchr (names_list, ' '); + if (item_end) + { + item_length = item_end - names_list; + item_end++; + } + if (item_length == strlen (untranslated->name) + && strncmp (untranslated->name, names_list, item_length) == 0) + { + *first_collision = untranslated; + return 1; + } + names_list = item_end; + } + } + return 0; +} + #ifdef TEST diff --git a/posix/tst-getopt_long_collision.c b/posix/tst-getopt_long_collision.c new file mode 100644 index 0000000000..4cd9447709 --- /dev/null +++ b/posix/tst-getopt_long_collision.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include +#include + +#define PN_(ctxt, str) (str) + +/* This checks that getopt_long_collision detects if the translation + of an option collides with an untranslated option name. Such a + conflict would be resolved by favoring the untranslated option by + the way. */ + +#define TRANSLATION_CONTEXT "command-line option" + +/* In this example, the French translator translated --instant by + --moment, but then the developer wants to introduce a new --moment + option... */ + +static const struct option options[] = + { + {PN_ ("command-line option", "instant"), no_argument, NULL, 'I'}, + {"moment", no_argument, NULL, 'M'}, + {NULL, 0, NULL, 0} + }; + +int +main (int argc, char **argv) +{ + (void) argc; + (void) argv; + const struct option *collision = NULL; + int has_collision; + unsetenv ("LANGUAGE"); + setlocale (LC_ALL, "fr_FR.UTF-8"); + if (bindtextdomain ("tst-getopt_long_collision", OBJPFX "domaindir") == NULL) + { + fprintf (stderr, "%s:%d: cannot call bindtextdomain.\n", __FILE__, __LINE__); + abort (); + } + /* Check that the catalog is OK: */ + if (strcmp (dgettext ("tst-getopt_long_collision", + TRANSLATION_CONTEXT "\004" "instant"), + "moment instant") != 0) + { + fprintf (stderr, "%s:%d: the mo file does not work.\n", __FILE__, __LINE__); + abort (); + } + has_collision = + getopt_long_collision (options, TRANSLATION_CONTEXT, + "tst-getopt_long_collision", &collision); + if (!has_collision) + { + fprintf (stderr, "%s:%d: collision not found.\n", __FILE__, __LINE__); + abort (); + } + if (strcmp (collision->name, "moment") != 0) + { + fprintf (stderr, "%s:%d: wrong collision detected, '%s' instead of 'moment'.\n", + __FILE__, __LINE__, collision->name); + abort (); + } + return 0; +} diff --git a/posix/tst-getopt_long_collision.po b/posix/tst-getopt_long_collision.po new file mode 100644 index 0000000000..9cd4a81861 --- /dev/null +++ b/posix/tst-getopt_long_collision.po @@ -0,0 +1,22 @@ +# French translations for tst-getopt_long_collision.c +# Copyright (C) 2025 THE GNU C Library'S COPYRIGHT HOLDER +# This file is distributed under the same license as the GNU C Library. +# +msgid "" +msgstr "" +"Project-Id-Version: GNU C Library (see version.h)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-06-06 22:37+0200\n" +"PO-Revision-Date: 2025-06-06 22:38+0200\n" +"Language-Team: French \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +# Oops, I first thought it would be translated as instant, but it’s moment in fact. +#: tst-getopt_long_collision.c:22 +msgctxt "command-line option" +msgid "instant" +msgstr "moment instant" diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist index d879f5a363..9952a339f1 100644 --- a/sysdeps/mach/hurd/i386/libc.abilist +++ b/sysdeps/mach/hurd/i386/libc.abilist @@ -2663,6 +2663,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/mach/hurd/x86_64/libc.abilist b/sysdeps/mach/hurd/x86_64/libc.abilist index 4b6580b2cc..d27a434b8e 100644 --- a/sysdeps/mach/hurd/x86_64/libc.abilist +++ b/sysdeps/mach/hurd/x86_64/libc.abilist @@ -2344,6 +2344,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist index 93730d969e..8e5627f2d5 100644 --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist @@ -2768,6 +2768,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist index 06412be15c..95c82143c4 100644 --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist @@ -3115,6 +3115,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist index e0c6c9f293..baa67af1f8 100644 --- a/sysdeps/unix/sysv/linux/arc/libc.abilist +++ b/sysdeps/unix/sysv/linux/arc/libc.abilist @@ -2529,6 +2529,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist index 7929ecb5ce..42b2843120 100644 --- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist @@ -2821,6 +2821,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist index 4b7ca1bdf0..7f5772e6a6 100644 --- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist @@ -2818,6 +2818,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist index 326be1421c..07347bcaf9 100644 --- a/sysdeps/unix/sysv/linux/csky/libc.abilist +++ b/sysdeps/unix/sysv/linux/csky/libc.abilist @@ -2805,6 +2805,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist index 31be97a20d..a104588ead 100644 --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist @@ -2842,6 +2842,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist index 3100d3720d..215f81f310 100644 --- a/sysdeps/unix/sysv/linux/i386/libc.abilist +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist @@ -3025,6 +3025,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist index 521343313d..c3ee573c25 100644 --- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist +++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist @@ -2289,6 +2289,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist index 974a643266..25328bcedf 100644 --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist @@ -2801,6 +2801,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist index 474da3c6e6..cafd37e64e 100644 --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist @@ -2968,6 +2968,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist index 3772bbf961..0163585ba6 100644 --- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist @@ -2854,6 +2854,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist index 81fdbf5528..ed50cec614 100644 --- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist @@ -2851,6 +2851,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist index 8bf825fbd0..00f29bfb0c 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist @@ -2931,6 +2931,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist index 3995eb7112..fadc850360 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist @@ -2929,6 +2929,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist index c7f9d75936..fb838cf6e0 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist @@ -2937,6 +2937,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist index fa4c0b6abb..db88142e10 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist @@ -2839,6 +2839,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist index 685e674205..a9da56876b 100644 --- a/sysdeps/unix/sysv/linux/or1k/libc.abilist +++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist @@ -2279,6 +2279,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist index b739c5b3dd..fee1496cac 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist @@ -3158,6 +3158,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist index 25a45c7596..6f6400c22d 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist @@ -3203,6 +3203,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist index 88979415c3..e44a3b326c 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist @@ -2912,6 +2912,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist index f55d5abaa1..a11e6d406e 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist @@ -2988,6 +2988,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist index d644e2b658..2276defe21 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist @@ -2532,6 +2532,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist index 8f90416e6f..c2e4d1c0d1 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist @@ -2732,6 +2732,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist index 2eb7244ede..7aa56fc320 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist @@ -3156,6 +3156,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist index 0418207b55..c6d5939ea7 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist @@ -2949,6 +2949,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist index c882ecd323..a82a82aaf0 100644 --- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist @@ -2848,6 +2848,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist index d8ad0073b8..e49dbe47e1 100644 --- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist @@ -2845,6 +2845,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist index fee6128da5..78f3d976ae 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist @@ -3179,6 +3179,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist index 78e1358a87..b06c012322 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist @@ -2815,6 +2815,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist index 33afb283fa..3451a56e6d 100644 --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist @@ -2764,6 +2764,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x8 GLIBC_2.43 opttextdomain D 0x8 diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist index 6bf8159064..1a85101261 100644 --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist @@ -2783,6 +2783,7 @@ GLIBC_2.42 uimaxabs F GLIBC_2.42 ulabs F GLIBC_2.42 ullabs F GLIBC_2.43 __memset_explicit_chk F +GLIBC_2.43 getopt_long_collision F GLIBC_2.43 memset_explicit F GLIBC_2.43 optctxt D 0x4 GLIBC_2.43 opttextdomain D 0x4 From patchwork Sun Oct 5 16:34:32 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Vivien Kraus X-Patchwork-Id: 121279 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1F6AB3858D37 for ; Sun, 5 Oct 2025 16:50:14 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1F6AB3858D37 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, secure) header.d=planete-kraus.eu header.i=@planete-kraus.eu header.a=rsa-sha1 header.s=albinoniA header.b=FbA5EWH2 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from planete-kraus.eu (planete-kraus.eu [IPv6:2a00:5881:4008:2810::309]) by sourceware.org (Postfix) with ESMTPS id 0E8333858C52 for ; Sun, 5 Oct 2025 16:36:26 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 0E8333858C52 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=planete-kraus.eu Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=planete-kraus.eu ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 0E8333858C52 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:5881:4008:2810::309 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682187; cv=none; b=eHb/ld5P5BxAl6qlLdB0pW6n4qRPp7gydUgsNFKvYGJaxlQCFz2/f7k0Ay6MKvnbCTgxPIM5ygtDuXGBjZQyOZ7CEXuN91TE/0+rNqsnMdCLpXmAQiQMKmECo+2zdIQ4hb+aNruHlUZJjXu/buuUdji7KwV6HTG8i8iKgTyiuAA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1759682187; c=relaxed/simple; bh=ik13OJdblFUPP50JO0SY2aOGE2WSjVE+f2gTOPO37I8=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=fOOj2bA3t+JunwH5uRpfN+FYsVnQRRtnKCQYPbFWuDLPqQXGRwu1YQC2mUOydJYr4yw4mPgxjC0zvg9KSFewVDXgJQ7XV4mmnxkqOrCZCEwTK/VTAdY+AZiuM1ZmD9OqDbO3J5U+k5pQJOBYPAQF60RnhhiwGhmDIbcW9BkLoXM= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0E8333858C52 Received: from planete-kraus.eu (localhost [127.0.0.1]) by planete-kraus.eu (OpenSMTPD) with ESMTP id 2b557fd1 for ; Sun, 5 Oct 2025 16:36:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=planete-kraus.eu; h=from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; s= albinoniA; bh=HBitgdgBy1q3t8fm/tj1d4J/aQk=; b=FbA5EWH2xMEY1jKGdJ ubNdWpvJcjlSAU+3o2plXEUnNQtDqWreLVHSL4mWKPwVcXl8CMJDAbcnxXxXQnTw dmL1+lcY6ShCq8tGDoefHNAcJqr3MueP5XDEL4/FothEGNm9So9f0SxS2Co11JVg 3SatoIJqwulWAwQFgD8RjxdsHYK0mOfoD/yMxMfQdCbF0VepJ4/nyev3fV5jJmN0 Qz/wG1YSLv0NowwwoQWpjqol0nJfcmL9B6RgDIQEXkV7KnPl3QotDxkxOQLNCsJP LsXxxNvetNXGbOgSArnn5V6se5et3ZhQEEMpoeoAPte2WttVqY8B82mR5nsL+o21 gMtw== Received: by planete-kraus.eu (OpenSMTPD) with ESMTPSA id a7275143 (TLSv1.3:TLS_CHACHA20_POLY1305_SHA256:256:NO); Sun, 5 Oct 2025 16:36:08 +0000 (UTC) From: Vivien Kraus To: libc-alpha@sourceware.org Cc: Vivien Kraus Subject: [PATCH v12 9/9] posix: Add a script for static validation of getopt_long PO files Date: Sun, 5 Oct 2025 18:34:32 +0200 Message-Id: <5fbf6c756c0db08a0d2b90f3ceda1944eef92ffc.1759681925.git.vivien@planete-kraus.eu> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-12.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org It is better to statically check the PO files on the developer’s side, because there is a chance to detect the problem early and not embarrass the translation team just before a release. This is a perl script that I made by adapting bits and pieces from mtrace.pl. I don’t know how I am supposed to test it, but it should fail with the following output: ----- Translation toto is used for more than one option: - bar - foo bar is a translation of pub, but it is also a different option. There were 2 failures. ----- --- manual/getopt.texi | 10 + posix/Makefile | 17 ++ posix/check-getopt-translations.pl | 199 ++++++++++++++++++ .../standalone-multiple-getopt-collisions.po | 45 ++++ posix/tst-check-getopt-translations.sh | 59 ++++++ 5 files changed, 330 insertions(+) create mode 100644 posix/check-getopt-translations.pl create mode 100644 posix/standalone-multiple-getopt-collisions.po create mode 100644 posix/tst-check-getopt-translations.sh diff --git a/manual/getopt.texi b/manual/getopt.texi index e769b0ee70..3cb7eabf9f 100644 --- a/manual/getopt.texi +++ b/manual/getopt.texi @@ -388,6 +388,16 @@ conflicts with the translation of an existing option name. Such a case would disrupt the workflow of users as the new option would replace the existing option. Before adding a new option to a program, the developer should check for collisions with all known translations. +This can be done with the installed +@command{check-getopt-translations} script, by calling for each PO +file in the project: + +@smallexample +check-getopt-translations "context used for translations" @file{file.po} +@end smallexample + +Otherwise, you may repeatedly call the @command{getopt_long_collision} +function after setting the locale, for each known locale. @deftypefun int getopt_long_collision (const struct option *@var{longopts}, const char *@var{context}, const char *@var{domain}, const struct option **@var{first_collision}) diff --git a/posix/Makefile b/posix/Makefile index 1bdb0b2585..e6fa13404b 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -435,6 +435,9 @@ install-others-programs := \ $(inst_libexecdir)/getconf \ # install-others-programs +install-bin-scripts = check-getopt-translations +generated += check-getopt-translations + before-compile += \ $(objpfx)posix-conf-vars-def.h \ # before-compile @@ -449,6 +452,7 @@ generated += \ testcases.h \ tst-getconf.out \ wordexp-tst.out \ + tst-check-getopt-translations.out \ # generated ifeq ($(run-built-tests),yes) @@ -514,6 +518,7 @@ tests-special += \ $(objpfx)tst-pcre-mem.out \ $(objpfx)tst-rxspencer-no-utf8-mem.out \ $(objpfx)tst-vfork3-mem.out \ + $(objpfx)tst-check-getopt-translations.out \ # tests-special endif endif @@ -798,3 +803,15 @@ $(objpfx)posix-conf-vars-def.h: $(..)scripts/gen-posix-conf-vars.awk \ $(make-target-directory) $(AWK) -f $(filter-out Makefile, $^) > $@.tmp mv -f $@.tmp $@ + +$(objpfx)check-getopt-translations: check-getopt-translations.pl + rm -f $@.new + sed -e 's|@XXX@|$(address-width)|' \ + -e 's|@VERSION@|$(version)|' \ + -e 's|@PKGVERSION@|$(PKGVERSION)|' \ + -e 's|@REPORT_BUGS_TO@|$(REPORT_BUGS_TO)|' $^ > $@.new \ + && rm -f $@ && mv $@.new $@ && chmod +x $@ + +$(objpfx)tst-check-getopt-translations.out: tst-check-getopt-translations.sh $(objpfx)check-getopt-translations standalone-multiple-getopt-collisions.po + $(SHELL) $^ $(common-objpfx)posix/tst-check-getopt-translations.out + $(evaluate-test) diff --git a/posix/check-getopt-translations.pl b/posix/check-getopt-translations.pl new file mode 100644 index 0000000000..58c4e087b3 --- /dev/null +++ b/posix/check-getopt-translations.pl @@ -0,0 +1,199 @@ +#! /usr/bin/perl + +# Copyright (C) 2025 Free Software Foundation, Inc. +# This file is part of the GNU C Library. +# Based on the mtrace.awk script. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# . + +use strict; +use warnings; +use Data::Dumper; + +my $VERSION = "@VERSION@"; + +my $PKGVERSION = "@PKGVERSION@"; +my $REPORT_BUGS_TO = '@REPORT_BUGS_TO@'; +my $progname = $_; + +sub usage { + print "Usage: getopt-check [OPTION]... msgctxt lang.po\n"; + print " --help print this help, then exit\n"; + print " --version print version number, then exit\n"; + print "\n"; + print "For bug reporting instructions, please see:\n"; + print "$REPORT_BUGS_TO.\n"; + exit 0; +} + +sub fatal { + print STDERR "$_[0]\n"; + exit 1; +} + +# This script takes two positional arguments: the context for +# translated option names, and the PO file to check. Then, the PO file +# is parsed, looking at three things: +# 1. the msgctxt: it must be equal to the first positional argument, msgctxt; +# 2. the msgid; +# 3. the space-separated list msgstr. +# +# We are looking for two different problems: +# +# 1. Every translation element, current or obsolete, must be unique +# across all option names. +# 2. For every option name, for every translation, current or +# deprecated, if it doesn’t match the untranslated name, then it +# should not match any other untranslated option names. +# +# If we detect an example of the first case, it is a problem with the +# translator only. They have to remove one use of the word, preferably +# one that is deprecated. +# +# If we detect an example of the second case, then it is a problem +# with the developer: they want to introduce an option name that is +# already used for something else by users of this native language! If +# nothing is done, these users will be surprised that the same word +# now means another option, as the untranslated options have +# precedence over the translations. If the translated name is already +# deprecated, then the language team may agree to completely remove +# it. Otherwise, it may be better to find a new untranslated name. + +# This script uses the same format as mtrace.pl. + + arglist: while (@ARGV) { + if ($ARGV[0] eq "--v" || $ARGV[0] eq "--ve" || $ARGV[0] eq "--ver" || + $ARGV[0] eq "--vers" || $ARGV[0] eq "--versi" || + $ARGV[0] eq "--versio" || $ARGV[0] eq "--version") { + print "getopt-check $PKGVERSION$VERSION\n"; + print "Copyright (C) 2025 Free Software Foundation, Inc.\n"; + print "This is free software; see the source for copying conditions. There is NO\n"; + print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"; + print "Written by Vivien Kraus \n"; + + exit 0; + } elsif ($ARGV[0] eq "--h" || $ARGV[0] eq "--he" || $ARGV[0] eq "--hel" || + $ARGV[0] eq "--help") { + &usage; + } elsif ($ARGV[0] =~ /^-/) { + print "$progname: unrecognized option `$ARGV[0]'\n"; + print "Try `$progname --help' for more information.\n"; + exit 1; + } else { + last arglist; + } +} + +if ($#ARGV != 1) { + fatal "You must provide two arguments: the msgctxt for option names, and the name of the PO file."; +} + +my $relevant_msgctxt = $ARGV[0]; +my $pofilename = $ARGV[1]; +my %translations; + +# %translation_used will be populated to detect multiple use of a +# %translation directly when we parse. + +my $entry_msgid; + +# The ad-hoc PO file parser has 3 states: 1. Wating for msgctxt; +# 2. Waiting for msgid; 3. Wating for msgstr. At the start, the state +# is 1. Then, if we find "msgctxt \"$relevant_msgctxt\"" in a single +# line, we jump to 2. Otherwise, if this is the end of the file, stop +# parsing. Otherwise, whatever the line, stay in 1. This includes: the +# empty line, meaning we are considering a new entry; or a comment, a +# #: location, or another relevant line. +# +# When we are in state 2., we are waiting for the msgid (untranslated +# option name). If we find an empty line, we jump back to 1. If we +# find a line starting with "msgid \"" and ending with a double quote, +# we store what is in the middle in $entry_msgid and jump to +# 3. Otherwise, we stay in state 2. +# +# When we are in state 3., we are waiting for msgstr (canonical and +# obsolete translations). If we find an empty line, drop $entry_msgid, +# and back to 1. If the line starts with "msgstr \"", we add a record +# to %translations: the key is $entry_msgid, and the value, what is +# between the detected prefix and the next space. Then, back to state +# 1. + +my $parser_state = 1; + +open (my $pofile, "<", $pofilename) || fatal "PO file name ${pofilename} cannot be read."; + +while (my $line = <$pofile>) { + chomp $line; + if ($parser_state == 1 && $line =~ /^msgctxt\s*"${relevant_msgctxt}"$/) { + $parser_state = 2; + } elsif ($parser_state == 2 && $line eq "") { + $parser_state = 1; + } elsif ($parser_state == 2 && $line =~ /^msgid\s*"([^"]+)"$/) { + $parser_state = 3; + $entry_msgid = $1; + } elsif ($parser_state == 3 && $line eq "") { + $parser_state = 1; + } elsif ($parser_state == 3 && $line =~ /^msgstr\s*"([^"]*)"$/) { + my @translations_for_this = split(/\s+/, $1); + $translations{$entry_msgid} = \@translations_for_this; + $parser_state = 1; + } +} + +my $number_of_errors = 0; + +# Verify that every option name is unique. +my %untranslated_name; +for my $option_name (keys %translations) { + for my $translation (@{$translations{$option_name}}) { + my @existing; + if (exists $untranslated_name{$translation}) { + @existing = @{$untranslated_name{$translation}}; + } + push(@existing, $option_name); + $untranslated_name{$translation} = \@existing; + } +} +for my $translation (keys %untranslated_name) { + my $names = $untranslated_name{$translation}; + if (@{$names} > 1) { + print STDERR "Translation ${translation} is used for more than one option:\n"; + for my $untranslated (@{$names}) { + print STDERR " - ${untranslated}\n"; + } + ++$number_of_errors; + } +} + +# Verify that every option translation does not match any other +# untranslated name. +for my $option_name (keys %translations) { + for my $other_option_name (keys %translations) { + if ($option_name ne $other_option_name) { + for my $translation (@{$translations{$option_name}}) { + if ($translation eq $other_option_name) { + print STDERR "${translation} is a translation of ${option_name}, but it is also a different option.\n"; + ++$number_of_errors; + } + } + } + } +} + +if ($number_of_errors eq 0) { + exit 0 +} +print STDERR "There were ${number_of_errors} failures.\n"; +exit 1 diff --git a/posix/standalone-multiple-getopt-collisions.po b/posix/standalone-multiple-getopt-collisions.po new file mode 100644 index 0000000000..479fdccf2c --- /dev/null +++ b/posix/standalone-multiple-getopt-collisions.po @@ -0,0 +1,45 @@ +# French translations for the getopt static checker +# Copyright (C) 2025 THE GNU C Library'S COPYRIGHT HOLDER +# This file is distributed under the same license as the GNU C Library. +# +# This has two errors: +# 1. "toto" is used both as a translation of "foo" and "bar"; +# 2. "bar" is used as a translation of "pub", but it is another option. +msgid "" +msgstr "" +"Project-Id-Version: GNU C Library (see version.h)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-06-06 22:37+0200\n" +"PO-Revision-Date: 2025-06-06 22:38+0200\n" +"Language-Team: French \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +# This is not an option name, so it’s OK for it to clash with option +# names. +msgctxt "fish" +msgid "bass" +msgstr "bar" + +# This is the --foo option. +msgctxt "command-line option" +msgid "foo" +msgstr "tata toto" + +# This is the --bar option. Oops, I translated with toto here too. +msgctxt "command-line option" +msgid "bar" +msgstr "titi toto" + +# Let’s go to the --pub! +msgctxt "command-line option" +msgid "pub" +msgstr "bar club" + +# Wait, it’s OK if baz is translated to baz though. +msgctxt "command-line option" +msgid "baz" +msgstr "baz" diff --git a/posix/tst-check-getopt-translations.sh b/posix/tst-check-getopt-translations.sh new file mode 100644 index 0000000000..a9a905dbae --- /dev/null +++ b/posix/tst-check-getopt-translations.sh @@ -0,0 +1,59 @@ +#!/bin/sh +# Test for check-getopt-translations. +# Copyright (C) 2025 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# . + +set -e + +check_getopt_translations_program=$1; shift +po_file=$1; shift +logfile=$1; shift + +rm -f $logfile +result=0 +expected_output="\ +Translation toto is used for more than one option: + - bar + - foo +bar is a translation of pub, but it is also a different option. +There were 2 failures." + +if output=$(${check_getopt_translations_program} "command-line option" ${po_file} 2>&1) ; then + echo "the errors were not caught." >> $logfile + echo "*** check-getopt-translations FAILED" >> $logfile + result=1 +fi + +if test "$output" != "$expected_output"; then + echo "Expected:" >> $logfile + echo "$expected_output" >> $logfile + echo "Actual:" >> $logfile + echo "$output" >> $logfile + echo "*** check-getopt-translations FAILED" >> $logfile + result=1 +fi + +exit $result + +# Preserve executable bits for this shell script. +Local Variables: +eval:(defun frobme () (set-file-modes buffer-file-name file-mode)) +eval:(make-local-variable 'file-mode) +eval:(setq file-mode (file-modes (buffer-file-name))) +eval:(make-local-variable 'after-save-hook) +eval:(add-hook 'after-save-hook 'frobme) +End: