This is the mail archive of the libc-help@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

argp_domain applies to struct argp_option only if ARGP_NO_HELP


Is argp->argp_domain intended to allow a separate message catalog
for each child argp?  That doesn't currently work, because the
strings in struct argp_option are instead translated using the
default message catalog as set with the textdomain function.

If a program links with glibc and gets the argp functions from
there, then is the message catalog of the program supposed to
include "Give this help list" and other msgids hardcoded in the
argp functions, or should their translations get loaded from the
"libc" message catalog?

I considered filing a bug but the documentation is unclear, so
I'd first like to read your opinions on how argp is supposed to
work.

I built the following program with libc6:amd64 2.19-18+deb8u10
on Debian 8.8:

-----------------------------------------------------------------

#include <argp.h>
#include <locale.h>

#define TEXTDOMAIN "pvrfs"
#define N_(s) s
#define _(s) dgettext(TEXTDOMAIN, s)

#define USE_LOCAL_HELP 0

enum cmdline_argp_key {
	KEY_DUMP_DIRENTS = -1,
	KEY_OUTDIR = -2,
#if USE_LOCAL_HELP
	KEY_HELP = -3,
#endif
};
	
const struct argp_option cmdline_argp_options[] = {
	{ .name = "dump-dirents",
	  .key = KEY_DUMP_DIRENTS,
	  .arg = N_("FILE"),
	  .doc = N_("Write directory entries to FILE.") },
	{ .name = "out-directory",
	  .key = KEY_OUTDIR,
	  .arg = N_("DIRECTORY"),
	  .doc = N_("Extract all files to DIRECTORY.") },
#if USE_LOCAL_HELP
	{ .name = "help",
	  .key = KEY_HELP,
	  .doc = N_("Describe the options of the program.") },
#endif
	{ NULL }
};

#if USE_LOCAL_HELP
error_t
cmdline_argp_parser (int key, char *arg, struct argp_state *state)
{
	switch (key) {
	case KEY_HELP:
		argp_state_help(state, state->out_stream,
				ARGP_HELP_STD_HELP);
		return 0;
	default:
		return ARGP_ERR_UNKNOWN;
	}
}
#endif

const struct argp cmdline_argp = {
	.options = cmdline_argp_options,
#if USE_LOCAL_HELP
	.parser = cmdline_argp_parser,
#endif
	.args_doc = N_("DEVICE"),
	.argp_domain = TEXTDOMAIN,
};

int
main(int argc, char **argv)
{
	setlocale(LC_ALL, "");
	argp_parse(&cmdline_argp, argc, argv,
#if USE_LOCAL_HELP
		   ARGP_NO_HELP,
#else
		   0,
#endif
		   NULL, NULL);
	return 0;
}

-----------------------------------------------------------------

This program does not call the textdomain function.
I then ran "LANG=fi_FI.UTF-8 gdb --args ./a.out --help",
set a breakpoint in __dcigettext, and logged the calls.
I omit (msgid2=NULL, plural=0, n=0, category=5=LC_MESSAGES)
from the following listing:

__dcigettext (domainname=NULL, msgid1="Usage:", ...)
__dcigettext (domainname=NULL, msgid1=" [OPTION...]", ...)
__dcigettext (domainname=NULL, msgid1=NULL, ...)
__dcigettext (domainname="pvrfs", msgid1="DEVICE", ...)
__dcigettext (domainname="libc", msgid1=NULL, ...)
__dcigettext (domainname=NULL, msgid1=NULL, ...)
__dcigettext (domainname="pvrfs", msgid1=NULL, ...)
__dcigettext (domainname="libc", msgid1=NULL, ...)
__dcigettext (domainname=NULL, msgid1="FILE", ...)
__dcigettext (domainname=NULL, msgid1="Write directory entries to FILE.", ...)
__dcigettext (domainname=NULL, msgid1="DIRECTORY", ...)
__dcigettext (domainname=NULL, msgid1="Extract all files to DIRECTORY.", ...)
__dcigettext (domainname=NULL, msgid1="Give this help list", ...)
__dcigettext (domainname=NULL, msgid1="Give a short usage message", ...)
__dcigettext (domainname=NULL, msgid1=NULL, ...)
__dcigettext (domainname="pvrfs", msgid1=NULL, ...)
__dcigettext (domainname="libc", msgid1=NULL, ...)

Note that argp used cmdline_argp.arp_domain = "pvrfs" only for
translating cmdline_argp.args_doc = "DEVICE".  In contrast,
cmdline_argp->options[0].arg = "FILE" was translated with
domainname=NULL, which means the default message catalog,
i.e. "messages" because the program did not call textdomain.
Likewise, "Give this help list" was translated with the default
message catalog, not with "libc".

The reason of the current behavior seems to be that most dgettext
calls in argp-help.c use state->root_argp->argp_domain, which is
NULL because __argp_parse has set up a root argp that has the
program's argp and argp_default_argp as children.  I could get
rid of the extra root argp by setting ARGP_NO_HELP, like with
"#define USE_LOCAL_HELP 1" in the demo program; then the sequence
of __dcigettext calls would change to:

__dcigettext (domainname="pvrfs", msgid1="Usage:", ...)
__dcigettext (domainname="pvrfs", msgid1=" [OPTION...]", ...)
__dcigettext (domainname="pvrfs", msgid1="DEVICE", ...)
__dcigettext (domainname="pvrfs", msgid1=NULL, ...)
__dcigettext (domainname="pvrfs", msgid1="FILE", ...)
__dcigettext (domainname="pvrfs", msgid1="Write directory entries to FILE.", ...)
__dcigettext (domainname="pvrfs", msgid1="DIRECTORY", ...)
__dcigettext (domainname="pvrfs", msgid1="Extract all files to DIRECTORY.", ...)
__dcigettext (domainname="pvrfs", msgid1="Describe the options of the program.", ...)
__dcigettext (domainname="pvrfs", msgid1=NULL, ...)

I think the argp implementation should use the program's message
catalog for all msgids that are defined in the program, and the
"libc" message catalog for all msgids that are defined in glibc.
Thus:

1. Use argp->argp_domain for argp->args_doc, argp->doc,
   argp->options[].arg, and argp->options[].doc.

2. If the argp implementation is built as part of glibc, then
   argp_default_argp.argp_domain and argp_version_argp.argp_domain
   should be "libc", like now.  Other hardcoded msgids like
   " [OPTION...]" should likewise be looked up in "libc".
   (If a program requires glibc and does not embed a copy of the
   argp implementation, then it is not convenient to translate
   those strings as part of the program because the msgids do not
   even appear in the source tree of the program.)

3. If the argp implementation is built as part of a program, then
   the msgids described in item 2 should instead be looked up in
   a message catalog specified by the program, perhaps using a
   preprocessor macro or a global variable.  (In this case,
   xgettext easily finds the msgids, so the strings can be
   translated as part of the program.)

If argp worked that way, the __dcigettext calls would be:

__dcigettext (domainname="libc", msgid1="Usage:", ...)
__dcigettext (domainname="libc", msgid1=" [OPTION...]", ...)
__dcigettext (domainname="pvrfs", msgid1="DEVICE", ...)
__dcigettext (domainname="pvrfs", msgid1="FILE", ...)
__dcigettext (domainname="pvrfs", msgid1="Write directory entries to FILE.", ...)
__dcigettext (domainname="pvrfs", msgid1="DIRECTORY", ...)
__dcigettext (domainname="pvrfs", msgid1="Extract all files to DIRECTORY.", ...)
__dcigettext (domainname="libc", msgid1="Give this help list", ...)
__dcigettext (domainname="libc", msgid1="Give a short usage message", ...)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]