mirror of
https://https.git.savannah.gnu.org/git/gettext.git
synced 2026-01-26 07:37:57 +00:00
xgettext: Allow inhibiting the warnings about URLs and email addresses.
Reported at <https://savannah.gnu.org/bugs/?67672>. * gettext-tools/src/message.h (enum syntax_check_type): Add sc_url, sc_email. (NSYNTAXCHECKS): Increase by 2. * gettext-tools/src/message.c (syntax_check_name): Update. * gettext-tools/src/xgettext.h (default_syntax_check): Add comment. * gettext-tools/src/xgettext.c (default_syntax_check): Add initializer. (main): Recognize --no-check option. (usage): Document --no-check option. * gettext-tools/src/xg-message.c (decide_syntax_check): Assume that default_syntax_check[i] != undecided. * gettext-tools/src/xg-check.c (syntax_check_function): Remove the second argument. (string_has_ascii_ellipsis): New function, extracted from syntax_check_ellipsis_unicode. (message_has_ascii_ellipsis): New function. (syntax_check_ellipsis_unicode): Remove the second argument. Simplify. Emit only a single error for both msgid and msgid_plural. (string_has_space_ellipsis): New function, extracted from syntax_check_space_ellipsis. (message_has_space_ellipsis): New function. (syntax_check_space_ellipsis): Remove the second argument. Simplify. Emit only a single error for both msgid and msgid_plural. (syntax_check_quote_unicode): Remove the second argument. (syntax_check_bullet_unicode_string): New function, extracted from syntax_check_bullet_unicode. (syntax_check_bullet_unicode): Remove the second argument. Simplify. (string_has_url): Don't recognize 'mailto:' URLs. (syntax_check_url, syntax_check_email): New functions, extracted from url_check_message. (url_check_message): Remove function. (sc_funcs): Add syntax_check_url, syntax_check_email. (syntax_check_message): Simplify. (xgettext_check_message_list): Don't invoke url_check_message. * gettext-tools/tests/xgettext-14: Update after xg-check.c changesd. * gettext-tools/tests/xgettext-20: Add more testcases. * gettext-tools/doc/xgettext.texi: Document the --no-check option. * gettext-tools/doc/gettext.texi: Bump copyright year. * NEWS: Mention the change.
This commit is contained in:
parent
213b865692
commit
f1459a89e2
10
NEWS
10
NEWS
@ -43,6 +43,16 @@ Version 1.0 - December 2025
|
||||
message.
|
||||
- The documentation has a new chapter "Pretranslation".
|
||||
|
||||
# Improvements for maintainers:
|
||||
* xgettext:
|
||||
- The refactoring suggestion when a translatable string contains an URL
|
||||
or email address can now be inhibited through a command-line option
|
||||
'--no-check=url' or '--no-check=email', or through a comment in the
|
||||
source code of the form
|
||||
/* xgettext: no-url-check */
|
||||
or
|
||||
/* xgettext: no-email-check */
|
||||
|
||||
# Programming languages support:
|
||||
* OCaml:
|
||||
- xgettext now supports OCaml.
|
||||
|
||||
@ -94,7 +94,7 @@ This file provides documentation for GNU @code{gettext} utilities.
|
||||
It also serves as a reference for the free Translation Project.
|
||||
|
||||
@copying
|
||||
Copyright (C) 1995-1998, 2001-2025 Free Software Foundation, Inc.
|
||||
Copyright (C) 1995-1998, 2001-2026 Free Software Foundation, Inc.
|
||||
|
||||
This manual is free documentation. It is dually licensed under the
|
||||
GNU FDL and the GNU GPL. This means that you can redistribute this
|
||||
@ -129,7 +129,7 @@ A copy of the license is included in @ref{GNU GPL}.
|
||||
@page
|
||||
@vskip 0pt plus 1filll
|
||||
@c @insertcopying
|
||||
Copyright (C) 1995-1998, 2001-2025 Free Software Foundation, Inc.
|
||||
Copyright (C) 1995-1998, 2001-2026 Free Software Foundation, Inc.
|
||||
|
||||
This manual is free documentation. It is dually licensed under the
|
||||
GNU FDL and the GNU GPL. This means that you can redistribute this
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@c This file is part of the GNU gettext manual.
|
||||
@c Copyright (C) 1995-2025 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 1995-2026 Free Software Foundation, Inc.
|
||||
@c See the file gettext.texi for copying conditions.
|
||||
|
||||
@pindex xgettext
|
||||
@ -188,8 +188,8 @@ the line with the string there is merely a blank line.
|
||||
@item --check[=@var{CHECK}]
|
||||
@opindex --check@r{, @code{xgettext} option}
|
||||
@cindex supported syntax checks, @code{xgettext}
|
||||
Perform a syntax check on msgid and msgid_plural. The supported checks
|
||||
are:
|
||||
Perform a syntax check on msgid and msgid_plural.
|
||||
The supported checks that are disabled by default and that can be enabled are:
|
||||
|
||||
@table @samp
|
||||
@item ellipsis-unicode
|
||||
@ -223,11 +223,37 @@ where @var{name} is the name of a valid syntax check. If a flag is
|
||||
prefixed by @code{no-}, the meaning is negated.
|
||||
|
||||
Some tests apply the checks to each sentence within the msgid, rather
|
||||
than the whole string. xgettext detects the end of sentence by
|
||||
than the whole string. @code{xgettext} detects the end of sentence by
|
||||
performing a pattern match, which usually looks for a period followed by
|
||||
a certain number of spaces. The number is specified with the
|
||||
@code{--sentence-end} option.
|
||||
|
||||
@item --no-check[=@var{CHECK}]
|
||||
@opindex --no-check@r{, @code{xgettext} option}
|
||||
Don't perform a syntax check on msgid and msgid_plural
|
||||
that is enabled by default.
|
||||
The supported checks that are enabled by default are:
|
||||
|
||||
@table @samp
|
||||
@item url
|
||||
Prohibit a URL inside a string.
|
||||
|
||||
@item email
|
||||
Prohibit an email address inside a string.
|
||||
|
||||
@end table
|
||||
|
||||
The option has an effect on all input files.
|
||||
To disable a check for a certain string,
|
||||
you can mark it with an @code{xgettext:} special comment in the source file.
|
||||
For example, if you want to suppress the check on a particular string,
|
||||
add the following comment:
|
||||
|
||||
@example
|
||||
/* xgettext: no-email-check */
|
||||
gettext ("Specify your@@email-address.com here.");
|
||||
@end example
|
||||
|
||||
@item --sentence-end[=@var{TYPE}]
|
||||
@opindex --sentence-end@r{, @code{xgettext} option}
|
||||
@cindex sentence end markers, @code{xgettext}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* GNU gettext - internationalization aids
|
||||
Copyright (C) 1995-2025 Free Software Foundation, Inc.
|
||||
Copyright (C) 1995-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file was written by Peter Miller <millerp@canb.auug.org.au>
|
||||
|
||||
@ -175,7 +175,9 @@ const char *const syntax_check_name[NSYNTAXCHECKS] =
|
||||
/* sc_ellipsis_unicode */ "ellipsis-unicode",
|
||||
/* sc_space_ellipsis */ "space-ellipsis",
|
||||
/* sc_quote_unicode */ "quote-unicode",
|
||||
/* sc_bullet_unicode */ "bullet-unicode"
|
||||
/* sc_bullet_unicode */ "bullet-unicode",
|
||||
/* sc_url */ "url",
|
||||
/* sc_email */ "email"
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* GNU gettext - internationalization aids
|
||||
Copyright (C) 1995-2025 Free Software Foundation, Inc.
|
||||
Copyright (C) 1995-2026 Free Software Foundation, Inc.
|
||||
|
||||
This file was written by Peter Miller <millerp@canb.auug.org.au>
|
||||
|
||||
@ -134,9 +134,11 @@ enum syntax_check_type
|
||||
sc_ellipsis_unicode,
|
||||
sc_space_ellipsis,
|
||||
sc_quote_unicode,
|
||||
sc_bullet_unicode
|
||||
sc_bullet_unicode,
|
||||
sc_url,
|
||||
sc_email
|
||||
};
|
||||
#define NSYNTAXCHECKS 4
|
||||
#define NSYNTAXCHECKS 6
|
||||
extern LIBGETTEXTSRC_DLL_VARIABLE const char *const syntax_check_name[NSYNTAXCHECKS];
|
||||
|
||||
/* Is current msgid subject to a syntax check? */
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* Checking of messages in POT files: so-called "syntax checks".
|
||||
Copyright (C) 2015-2025 Free Software Foundation, Inc.
|
||||
Copyright (C) 2015-2026 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -45,117 +45,148 @@
|
||||
|
||||
/* Function that implements a single syntax check.
|
||||
MP is a message.
|
||||
MSGID is either MP->msgid or MP->msgid_plural.
|
||||
Returns the number of errors that were seen and reported. */
|
||||
typedef int (* syntax_check_function) (const message_ty *mp, const char *msgid);
|
||||
typedef int (* syntax_check_function) (const message_ty *mp);
|
||||
|
||||
|
||||
/* Implementation of the sc_ellipsis_unicode syntax check. */
|
||||
/* ----- Implementation of the sc_ellipsis_unicode syntax check. ----- */
|
||||
|
||||
/* Determine whether a string (msgid or msgid_plural) contains an ASCII
|
||||
ellipsis. */
|
||||
static bool
|
||||
string_has_ascii_ellipsis (const char *string)
|
||||
{
|
||||
const char *str = string;
|
||||
const char *str_limit = str + strlen (string);
|
||||
while (str < str_limit)
|
||||
{
|
||||
ucs4_t ending_char;
|
||||
const char *end = sentence_end (str, &ending_char);
|
||||
|
||||
/* sentence_end doesn't treat '...' specially. */
|
||||
const char *cp = end - (ending_char == '.' ? 2 : 3);
|
||||
|
||||
if (cp >= str && memcmp (cp, "...", 3) == 0)
|
||||
return true;
|
||||
|
||||
str = end + 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Determine whether a message contains an ASCII ellipsis. */
|
||||
static bool
|
||||
message_has_ascii_ellipsis (const message_ty *mp)
|
||||
{
|
||||
return string_has_ascii_ellipsis (mp->msgid)
|
||||
|| (mp->msgid_plural != NULL
|
||||
&& string_has_ascii_ellipsis (mp->msgid_plural));
|
||||
}
|
||||
|
||||
static int
|
||||
syntax_check_ellipsis_unicode (const message_ty *mp, const char *msgid)
|
||||
syntax_check_ellipsis_unicode (const message_ty *mp)
|
||||
{
|
||||
int seen_errors = 0;
|
||||
{
|
||||
const char *str = msgid;
|
||||
const char *str_limit = str + strlen (msgid);
|
||||
while (str < str_limit)
|
||||
{
|
||||
ucs4_t ending_char;
|
||||
const char *end = sentence_end (str, &ending_char);
|
||||
if (message_has_ascii_ellipsis (mp))
|
||||
{
|
||||
po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
|
||||
_("ASCII ellipsis ('...') instead of Unicode"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* sentence_end doesn't treat '...' specially. */
|
||||
const char *cp = end - (ending_char == '.' ? 2 : 3);
|
||||
|
||||
if (cp >= str && memcmp (cp, "...", 3) == 0)
|
||||
{
|
||||
po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
|
||||
_("ASCII ellipsis ('...') instead of Unicode"));
|
||||
seen_errors++;
|
||||
}
|
||||
|
||||
str = end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return seen_errors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Implementation of the sc_space_ellipsis syntax check. */
|
||||
/* ----- Implementation of the sc_space_ellipsis syntax check. ----- */
|
||||
|
||||
/* Determine whether a string (msgid or msgid_plural) contains a space before
|
||||
an ellipsis. */
|
||||
static bool
|
||||
string_has_space_ellipsis (const char *string)
|
||||
{
|
||||
const char *str = string;
|
||||
const char *str_limit = str + strlen (string);
|
||||
while (str < str_limit)
|
||||
{
|
||||
ucs4_t ending_char;
|
||||
const char *end = sentence_end (str, &ending_char);
|
||||
|
||||
const char *ellipsis = NULL;
|
||||
if (ending_char == 0x2026)
|
||||
ellipsis = end;
|
||||
else if (ending_char == '.')
|
||||
{
|
||||
/* sentence_end doesn't treat '...' specially. */
|
||||
const char *cp = end - 2;
|
||||
if (cp >= str && memcmp (cp, "...", 3) == 0)
|
||||
ellipsis = cp;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Look for a '...'. */
|
||||
const char *cp = end - 3;
|
||||
if (cp >= str && memcmp (cp, "...", 3) == 0)
|
||||
ellipsis = cp;
|
||||
else
|
||||
{
|
||||
/* Look for a U+2026. */
|
||||
ucs4_t uc = 0xfffd;
|
||||
for (cp = end - 1; cp >= str; cp--)
|
||||
{
|
||||
u8_mbtouc (&uc, (const unsigned char *) cp, end - cp);
|
||||
if (uc != 0xfffd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (uc == 0x2026)
|
||||
ellipsis = cp;
|
||||
}
|
||||
}
|
||||
|
||||
if (ellipsis)
|
||||
{
|
||||
/* Look at the character before ellipsis. */
|
||||
ucs4_t uc = 0xfffd;
|
||||
for (const char *cp = ellipsis - 1; cp >= str; cp--)
|
||||
{
|
||||
u8_mbtouc (&uc, (const unsigned char *) cp, ellipsis - cp);
|
||||
if (uc != 0xfffd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (uc != 0xfffd && uc_is_space (uc))
|
||||
return true;
|
||||
}
|
||||
|
||||
str = end + 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Determine whether a message contains a space before an ellipsis. */
|
||||
static bool
|
||||
message_has_space_ellipsis (const message_ty *mp)
|
||||
{
|
||||
return string_has_space_ellipsis (mp->msgid)
|
||||
|| (mp->msgid_plural != NULL
|
||||
&& string_has_space_ellipsis (mp->msgid_plural));
|
||||
}
|
||||
|
||||
static int
|
||||
syntax_check_space_ellipsis (const message_ty *mp, const char *msgid)
|
||||
syntax_check_space_ellipsis (const message_ty *mp)
|
||||
{
|
||||
int seen_errors = 0;
|
||||
{
|
||||
const char *str = msgid;
|
||||
const char *str_limit = str + strlen (msgid);
|
||||
while (str < str_limit)
|
||||
{
|
||||
ucs4_t ending_char;
|
||||
const char *end = sentence_end (str, &ending_char);
|
||||
if (message_has_space_ellipsis (mp))
|
||||
{
|
||||
po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
|
||||
_("space before ellipsis found in user visible string"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *ellipsis = NULL;
|
||||
if (ending_char == 0x2026)
|
||||
ellipsis = end;
|
||||
else if (ending_char == '.')
|
||||
{
|
||||
/* sentence_end doesn't treat '...' specially. */
|
||||
const char *cp = end - 2;
|
||||
if (cp >= str && memcmp (cp, "...", 3) == 0)
|
||||
ellipsis = cp;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Look for a '...'. */
|
||||
const char *cp = end - 3;
|
||||
if (cp >= str && memcmp (cp, "...", 3) == 0)
|
||||
ellipsis = cp;
|
||||
else
|
||||
{
|
||||
/* Look for a U+2026. */
|
||||
ucs4_t uc = 0xfffd;
|
||||
for (cp = end - 1; cp >= str; cp--)
|
||||
{
|
||||
u8_mbtouc (&uc, (const unsigned char *) cp, end - cp);
|
||||
if (uc != 0xfffd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (uc == 0x2026)
|
||||
ellipsis = cp;
|
||||
}
|
||||
}
|
||||
|
||||
if (ellipsis)
|
||||
{
|
||||
/* Look at the character before ellipsis. */
|
||||
ucs4_t uc = 0xfffd;
|
||||
for (const char *cp = ellipsis - 1; cp >= str; cp--)
|
||||
{
|
||||
u8_mbtouc (&uc, (const unsigned char *) cp, ellipsis - cp);
|
||||
if (uc != 0xfffd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (uc != 0xfffd && uc_is_space (uc))
|
||||
{
|
||||
po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
|
||||
_("space before ellipsis found in user visible strings"));
|
||||
seen_errors++;
|
||||
}
|
||||
}
|
||||
|
||||
str = end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return seen_errors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Implementation of the sc_quote_unicode syntax check. */
|
||||
/* ----- Implementation of the sc_quote_unicode syntax check. ----- */
|
||||
|
||||
struct callback_arg
|
||||
{
|
||||
@ -189,20 +220,23 @@ syntax_check_quote_unicode_callback (char quote, const char *quoted,
|
||||
}
|
||||
|
||||
static int
|
||||
syntax_check_quote_unicode (const message_ty *mp, const char *msgid)
|
||||
syntax_check_quote_unicode (const message_ty *mp)
|
||||
{
|
||||
struct callback_arg arg;
|
||||
arg.mp = mp;
|
||||
arg.seen_errors = 0;
|
||||
|
||||
scan_quoted (msgid, strlen (msgid),
|
||||
scan_quoted (mp->msgid, strlen (mp->msgid),
|
||||
syntax_check_quote_unicode_callback, &arg);
|
||||
if (mp->msgid_plural != NULL)
|
||||
scan_quoted (mp->msgid_plural, strlen (mp->msgid_plural),
|
||||
syntax_check_quote_unicode_callback, &arg);
|
||||
|
||||
return arg.seen_errors;
|
||||
}
|
||||
|
||||
|
||||
/* Implementation of the sc_bullet_unicode syntax check. */
|
||||
/* ----- Implementation of the sc_bullet_unicode syntax check. ----- */
|
||||
|
||||
struct bullet_ty
|
||||
{
|
||||
@ -220,7 +254,7 @@ struct bullet_stack_ty
|
||||
static struct bullet_stack_ty bullet_stack;
|
||||
|
||||
static int
|
||||
syntax_check_bullet_unicode (const message_ty *mp, const char *msgid)
|
||||
syntax_check_bullet_unicode_string (const message_ty *mp, const char *msgid)
|
||||
{
|
||||
bool seen_error = false;
|
||||
|
||||
@ -306,110 +340,18 @@ syntax_check_bullet_unicode (const message_ty *mp, const char *msgid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* List of all syntax checks. */
|
||||
static const syntax_check_function sc_funcs[NSYNTAXCHECKS] =
|
||||
{
|
||||
syntax_check_ellipsis_unicode,
|
||||
syntax_check_space_ellipsis,
|
||||
syntax_check_quote_unicode,
|
||||
syntax_check_bullet_unicode
|
||||
};
|
||||
|
||||
|
||||
/* Perform all syntax checks on a non-obsolete message.
|
||||
Return the number of errors that were seen. */
|
||||
static int
|
||||
syntax_check_message (const message_ty *mp)
|
||||
syntax_check_bullet_unicode (const message_ty *mp)
|
||||
{
|
||||
int seen_errors = 0;
|
||||
|
||||
for (int i = 0; i < NSYNTAXCHECKS; i++)
|
||||
{
|
||||
if (mp->do_syntax_check[i] == yes)
|
||||
{
|
||||
seen_errors += sc_funcs[i] (mp, mp->msgid);
|
||||
if (mp->msgid_plural)
|
||||
seen_errors += sc_funcs[i] (mp, mp->msgid_plural);
|
||||
}
|
||||
}
|
||||
|
||||
return seen_errors;
|
||||
return syntax_check_bullet_unicode_string (mp, mp->msgid)
|
||||
+ (mp->msgid_plural != NULL
|
||||
? syntax_check_bullet_unicode_string (mp, mp->msgid_plural)
|
||||
: 0);
|
||||
}
|
||||
|
||||
|
||||
/* Signal an error when checking format strings. */
|
||||
struct formatstring_error_logger_locals
|
||||
{
|
||||
const lex_pos_ty *pos;
|
||||
};
|
||||
static void
|
||||
formatstring_error_logger (void *data, const char *format, ...)
|
||||
#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 7) || __GNUC__ > 2)
|
||||
__attribute__ ((__format__ (__printf__, 2, 3)))
|
||||
#endif
|
||||
;
|
||||
static void
|
||||
formatstring_error_logger (void *data, const char *format, ...)
|
||||
{
|
||||
struct formatstring_error_logger_locals *l =
|
||||
(struct formatstring_error_logger_locals *) data;
|
||||
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
if_verror (IF_SEVERITY_ERROR,
|
||||
l->pos->file_name, l->pos->line_number, (size_t)(-1), false,
|
||||
format, args);
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
|
||||
/* Perform all format checks on a non-obsolete message.
|
||||
Return the number of errors that were seen. */
|
||||
static int
|
||||
format_check_message (const message_ty *mp)
|
||||
{
|
||||
int seen_errors = 0;
|
||||
|
||||
if (mp->msgid_plural != NULL)
|
||||
{
|
||||
/* Look for format string incompatibilities between msgid and
|
||||
msgid_plural. */
|
||||
for (size_t i = 0; i < NFORMATS; i++)
|
||||
if (possible_format_p (mp->is_format[i]))
|
||||
{
|
||||
struct formatstring_parser *parser = formatstring_parsers[i];
|
||||
char *invalid_reason1 = NULL;
|
||||
void *descr1 =
|
||||
parser->parse (mp->msgid, false, NULL, &invalid_reason1);
|
||||
char *invalid_reason2 = NULL;
|
||||
void *descr2 =
|
||||
parser->parse (mp->msgid_plural, false, NULL, &invalid_reason2);
|
||||
|
||||
if (descr1 != NULL && descr2 != NULL)
|
||||
{
|
||||
struct formatstring_error_logger_locals locals;
|
||||
locals.pos = &mp->pos;
|
||||
if (parser->check (descr2, descr1, false,
|
||||
formatstring_error_logger, &locals,
|
||||
"msgid_plural", "msgid"))
|
||||
seen_errors++;
|
||||
}
|
||||
|
||||
if (descr2 != NULL)
|
||||
parser->free (descr2);
|
||||
else
|
||||
free (invalid_reason2);
|
||||
if (descr1 != NULL)
|
||||
parser->free (descr1);
|
||||
else
|
||||
free (invalid_reason1);
|
||||
}
|
||||
}
|
||||
|
||||
return seen_errors;
|
||||
}
|
||||
|
||||
/* ----- Implementation of the sc_url syntax check. ----- */
|
||||
/* This check is enabled by default. It produces a warning, not an error. */
|
||||
|
||||
/* Determine whether a string (msgid or msgid_plural) contains a URL. */
|
||||
static bool
|
||||
@ -419,7 +361,12 @@ string_has_url (const char *string)
|
||||
(not "file:"). */
|
||||
static const char *patterns[] =
|
||||
{
|
||||
/* We can afford to be silent about 'mailto:' here, because it is
|
||||
almost always followed by an email address, that we report though
|
||||
the sc_email check. */
|
||||
#if 0
|
||||
"mailto:",
|
||||
#endif
|
||||
"http://", "https://",
|
||||
"ftp://",
|
||||
"irc://", "ircs://"
|
||||
@ -473,6 +420,20 @@ message_has_url (const message_ty *mp)
|
||||
|| (mp->msgid_plural != NULL && string_has_url (mp->msgid_plural));
|
||||
}
|
||||
|
||||
static int
|
||||
syntax_check_url (const message_ty *mp)
|
||||
{
|
||||
if (message_has_url (mp))
|
||||
if_error (IF_SEVERITY_WARNING,
|
||||
mp->pos.file_name, mp->pos.line_number, (size_t)(-1), false,
|
||||
_("Message contains an embedded URL. Better move it out of the translatable string, see %s"),
|
||||
"https://www.gnu.org/software/gettext/manual/html_node/No-embedded-URLs.html");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* ----- Implementation of the sc_email syntax check. ----- */
|
||||
/* This check is enabled by default. It produces a warning, not an error. */
|
||||
|
||||
/* Determine whether a string (msgid or msgid_plural) contains an
|
||||
email address. */
|
||||
@ -551,21 +512,116 @@ message_has_email (const message_ty *mp)
|
||||
|| (mp->msgid_plural != NULL && string_has_email (mp->msgid_plural));
|
||||
}
|
||||
|
||||
|
||||
/* Perform the URL check on a non-obsolete message. */
|
||||
static void
|
||||
url_check_message (const message_ty *mp)
|
||||
static int
|
||||
syntax_check_email (const message_ty *mp)
|
||||
{
|
||||
if (message_has_url (mp))
|
||||
if_error (IF_SEVERITY_WARNING,
|
||||
mp->pos.file_name, mp->pos.line_number, (size_t)(-1), false,
|
||||
_("Message contains an embedded URL. Better move it out of the translatable string, see %s"),
|
||||
"https://www.gnu.org/software/gettext/manual/html_node/No-embedded-URLs.html");
|
||||
else if (message_has_email (mp))
|
||||
if (message_has_email (mp))
|
||||
if_error (IF_SEVERITY_WARNING,
|
||||
mp->pos.file_name, mp->pos.line_number, (size_t)(-1), false,
|
||||
_("Message contains an embedded email address. Better move it out of the translatable string, see %s"),
|
||||
"https://www.gnu.org/software/gettext/manual/html_node/No-embedded-URLs.html");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------- List of all syntax checks. ---------------------- */
|
||||
static const syntax_check_function sc_funcs[NSYNTAXCHECKS] =
|
||||
{
|
||||
syntax_check_ellipsis_unicode,
|
||||
syntax_check_space_ellipsis,
|
||||
syntax_check_quote_unicode,
|
||||
syntax_check_bullet_unicode,
|
||||
syntax_check_url,
|
||||
syntax_check_email
|
||||
};
|
||||
|
||||
|
||||
/* Perform all syntax checks on a non-obsolete message.
|
||||
Return the number of errors that were seen. */
|
||||
static int
|
||||
syntax_check_message (const message_ty *mp)
|
||||
{
|
||||
int seen_errors = 0;
|
||||
|
||||
for (int i = 0; i < NSYNTAXCHECKS; i++)
|
||||
if (mp->do_syntax_check[i] == yes)
|
||||
seen_errors += sc_funcs[i] (mp);
|
||||
|
||||
return seen_errors;
|
||||
}
|
||||
|
||||
|
||||
/* Signal an error when checking format strings. */
|
||||
struct formatstring_error_logger_locals
|
||||
{
|
||||
const lex_pos_ty *pos;
|
||||
};
|
||||
static void
|
||||
formatstring_error_logger (void *data, const char *format, ...)
|
||||
#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 7) || __GNUC__ > 2)
|
||||
__attribute__ ((__format__ (__printf__, 2, 3)))
|
||||
#endif
|
||||
;
|
||||
static void
|
||||
formatstring_error_logger (void *data, const char *format, ...)
|
||||
{
|
||||
struct formatstring_error_logger_locals *l =
|
||||
(struct formatstring_error_logger_locals *) data;
|
||||
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
if_verror (IF_SEVERITY_ERROR,
|
||||
l->pos->file_name, l->pos->line_number, (size_t)(-1), false,
|
||||
format, args);
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
|
||||
/* Perform all format checks on a non-obsolete message.
|
||||
Return the number of errors that were seen. */
|
||||
static int
|
||||
format_check_message (const message_ty *mp)
|
||||
{
|
||||
int seen_errors = 0;
|
||||
|
||||
if (mp->msgid_plural != NULL)
|
||||
{
|
||||
/* Look for format string incompatibilities between msgid and
|
||||
msgid_plural. */
|
||||
for (size_t i = 0; i < NFORMATS; i++)
|
||||
if (possible_format_p (mp->is_format[i]))
|
||||
{
|
||||
struct formatstring_parser *parser = formatstring_parsers[i];
|
||||
char *invalid_reason1 = NULL;
|
||||
void *descr1 =
|
||||
parser->parse (mp->msgid, false, NULL, &invalid_reason1);
|
||||
char *invalid_reason2 = NULL;
|
||||
void *descr2 =
|
||||
parser->parse (mp->msgid_plural, false, NULL, &invalid_reason2);
|
||||
|
||||
if (descr1 != NULL && descr2 != NULL)
|
||||
{
|
||||
struct formatstring_error_logger_locals locals;
|
||||
locals.pos = &mp->pos;
|
||||
if (parser->check (descr2, descr1, false,
|
||||
formatstring_error_logger, &locals,
|
||||
"msgid_plural", "msgid"))
|
||||
seen_errors++;
|
||||
}
|
||||
|
||||
if (descr2 != NULL)
|
||||
parser->free (descr2);
|
||||
else
|
||||
free (invalid_reason2);
|
||||
if (descr1 != NULL)
|
||||
parser->free (descr1);
|
||||
else
|
||||
free (invalid_reason1);
|
||||
}
|
||||
}
|
||||
|
||||
return seen_errors;
|
||||
}
|
||||
|
||||
|
||||
@ -583,7 +639,6 @@ xgettext_check_message_list (message_list_ty *mlp)
|
||||
if (!is_header (mp))
|
||||
{
|
||||
seen_errors += syntax_check_message (mp) + format_check_message (mp);
|
||||
url_check_message (mp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* Extracting a message. Accumulating the message list.
|
||||
Copyright (C) 2001-2025 Free Software Foundation, Inc.
|
||||
Copyright (C) 2001-2026 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -231,7 +231,7 @@ decide_syntax_check (message_ty *mp)
|
||||
{
|
||||
for (size_t i = 0; i < NSYNTAXCHECKS; i++)
|
||||
if (mp->do_syntax_check[i] == undecided)
|
||||
mp->do_syntax_check[i] = default_syntax_check[i] == yes ? yes : no;
|
||||
mp->do_syntax_check[i] = default_syntax_check[i];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* Extracts strings from C source file to Uniforum style .po file.
|
||||
Copyright (C) 1995-2025 Free Software Foundation, Inc.
|
||||
Copyright (C) 1995-2026 Free Software Foundation, Inc.
|
||||
Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
@ -239,8 +239,16 @@ static bool recognize_format_kde;
|
||||
/* If true, recognize Boost format strings. */
|
||||
static bool recognize_format_boost;
|
||||
|
||||
/* Syntax checks enabled by default. */
|
||||
enum is_syntax_check default_syntax_check[NSYNTAXCHECKS];
|
||||
/* Syntax checks enabled through a command-line option or by default. */
|
||||
enum is_syntax_check default_syntax_check[NSYNTAXCHECKS] =
|
||||
{
|
||||
/* sc_ellipsis_unicode */ no,
|
||||
/* sc_space_ellipsis */ no,
|
||||
/* sc_quote_unicode */ no,
|
||||
/* sc_bullet_unicode */ no,
|
||||
/* sc_url */ yes,
|
||||
/* sc_email */ yes
|
||||
};
|
||||
|
||||
static locating_rule_list_ty *its_locating_rules;
|
||||
|
||||
@ -391,11 +399,11 @@ main (int argc, char *argv[])
|
||||
{ "force-po", 0, no_argument, &force_po, 1 },
|
||||
{ "foreign-user", CHAR_MAX + 2, no_argument },
|
||||
{ "from-code", CHAR_MAX + 3, required_argument },
|
||||
{ "generated", CHAR_MAX + 24, required_argument },
|
||||
{ "generated", CHAR_MAX + 25, required_argument },
|
||||
{ "help", 'h', no_argument },
|
||||
{ "indent", 'i', no_argument },
|
||||
{ "its", CHAR_MAX + 20, required_argument },
|
||||
{ "itstool", CHAR_MAX + 19, no_argument },
|
||||
{ "its", CHAR_MAX + 21, required_argument },
|
||||
{ "itstool", CHAR_MAX + 20, no_argument },
|
||||
{ "join-existing", 'j', no_argument },
|
||||
{ "kde", CHAR_MAX + 10, no_argument },
|
||||
{ "keyword", 'k', optional_argument },
|
||||
@ -403,8 +411,9 @@ main (int argc, char *argv[])
|
||||
{ "msgid-bugs-address", CHAR_MAX + 5, required_argument },
|
||||
{ "msgstr-prefix", 'm', optional_argument },
|
||||
{ "msgstr-suffix", 'M', optional_argument },
|
||||
{ "no-check", CHAR_MAX + 18, required_argument },
|
||||
{ "no-escape", 'e', no_argument },
|
||||
{ "no-git", CHAR_MAX + 23, no_argument },
|
||||
{ "no-git", CHAR_MAX + 24, no_argument },
|
||||
{ "no-location", CHAR_MAX + 16, no_argument },
|
||||
{ "no-wrap", CHAR_MAX + 4, no_argument },
|
||||
{ "omit-header", 0, no_argument, &xgettext_omit_header, 1 },
|
||||
@ -414,15 +423,15 @@ main (int argc, char *argv[])
|
||||
{ "package-version", CHAR_MAX + 13, required_argument },
|
||||
{ "properties-output", CHAR_MAX + 6, no_argument },
|
||||
{ "qt", CHAR_MAX + 9, no_argument },
|
||||
{ "reference", CHAR_MAX + 22, required_argument },
|
||||
{ "sentence-end", CHAR_MAX + 18, required_argument },
|
||||
{ "reference", CHAR_MAX + 23, required_argument },
|
||||
{ "sentence-end", CHAR_MAX + 19, required_argument },
|
||||
{ "sort-by-file", 'F', no_argument },
|
||||
{ "sort-output", 's', no_argument },
|
||||
{ "strict", CHAR_MAX + 25, no_argument },
|
||||
{ "strict", CHAR_MAX + 26, no_argument },
|
||||
{ "string-limit", 'l', required_argument },
|
||||
{ "stringtable-output", CHAR_MAX + 7, no_argument },
|
||||
{ "style", CHAR_MAX + 15, required_argument },
|
||||
{ "tag", CHAR_MAX + 21, required_argument },
|
||||
{ "tag", CHAR_MAX + 22, required_argument },
|
||||
{ "trigraphs", 'T', no_argument },
|
||||
{ "verbose", 'v', no_argument },
|
||||
{ "version", 'V', no_argument },
|
||||
@ -604,7 +613,7 @@ main (int argc, char *argv[])
|
||||
sort_by_msgid = true;
|
||||
break;
|
||||
|
||||
case CHAR_MAX + 25: /* --strict */
|
||||
case CHAR_MAX + 26: /* --strict */
|
||||
message_print_style_uniforum ();
|
||||
break;
|
||||
|
||||
@ -722,7 +731,23 @@ main (int argc, char *argv[])
|
||||
}
|
||||
break;
|
||||
|
||||
case CHAR_MAX + 18: /* --sentence-end */
|
||||
case CHAR_MAX + 18: /* --no-check */
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < NSYNTAXCHECKS; i++)
|
||||
{
|
||||
if (strcmp (optarg, syntax_check_name[i]) == 0)
|
||||
{
|
||||
default_syntax_check[i] = no;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == NSYNTAXCHECKS)
|
||||
error (EXIT_FAILURE, 0, _("syntax check '%s' unknown"), optarg);
|
||||
}
|
||||
break;
|
||||
|
||||
case CHAR_MAX + 19: /* --sentence-end */
|
||||
if (strcmp (optarg, "single-space") == 0)
|
||||
sentence_end_required_spaces = 1;
|
||||
else if (strcmp (optarg, "double-space") == 0)
|
||||
@ -731,27 +756,27 @@ main (int argc, char *argv[])
|
||||
error (EXIT_FAILURE, 0, _("sentence end type '%s' unknown"), optarg);
|
||||
break;
|
||||
|
||||
case CHAR_MAX + 19: /* --itstool */
|
||||
case CHAR_MAX + 20: /* --itstool */
|
||||
add_itstool_comments = true;
|
||||
break;
|
||||
|
||||
case CHAR_MAX + 20: /* --its */
|
||||
case CHAR_MAX + 21: /* --its */
|
||||
explicit_its_filename = optarg;
|
||||
break;
|
||||
|
||||
case CHAR_MAX + 21: /* --tag */
|
||||
case CHAR_MAX + 22: /* --tag */
|
||||
x_javascript_tag (optarg);
|
||||
break;
|
||||
|
||||
case CHAR_MAX + 22: /* --reference */
|
||||
case CHAR_MAX + 23: /* --reference */
|
||||
string_list_append (&files_for_vc_mtime, optarg);
|
||||
break;
|
||||
|
||||
case CHAR_MAX + 23: /* --no-git */
|
||||
case CHAR_MAX + 24: /* --no-git */
|
||||
xgettext_no_git = true;
|
||||
break;
|
||||
|
||||
case CHAR_MAX + 24: /* --generated */
|
||||
case CHAR_MAX + 25: /* --generated */
|
||||
gl_set_add (generated_files, optarg);
|
||||
break;
|
||||
|
||||
@ -1212,6 +1237,9 @@ Operation mode:\n"));
|
||||
(ellipsis-unicode, space-ellipsis,\n\
|
||||
quote-unicode, bullet-unicode)\n"));
|
||||
printf (_("\
|
||||
--no-check=NAME don't perform syntax check on messages\n\
|
||||
(url, email)\n"));
|
||||
printf (_("\
|
||||
--sentence-end=TYPE type describing the end of sentence\n\
|
||||
(single-space, which is the default, \n\
|
||||
or double-space)\n"));
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* xgettext common functions.
|
||||
Copyright (C) 2001-2003, 2005-2006, 2008-2009, 2011, 2013-2014, 2018, 2020, 2023 Free Software Foundation, Inc.
|
||||
Copyright (C) 2001-2026 Free Software Foundation, Inc.
|
||||
Written by Peter Miller <millerp@canb.auug.org.au>
|
||||
and Bruno Haible <haible@clisp.cons.org>, 2001.
|
||||
|
||||
@ -53,6 +53,7 @@ extern int xgettext_omit_header;
|
||||
/* Be more verbose. */
|
||||
extern int verbose;
|
||||
|
||||
/* Syntax checks enabled through a command-line option or by default. */
|
||||
extern enum is_syntax_check default_syntax_check[NSYNTAXCHECKS];
|
||||
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ EOF
|
||||
: ${XGETTEXT=xgettext}
|
||||
LANGUAGE= LC_ALL=C ${XGETTEXT} --omit-header --add-comments --check=ellipsis-unicode -d xg-ellipsis-u.tmp xg-ellipsis-u.c 2>xg-ellipsis-u.err
|
||||
|
||||
test `grep -c 'ASCII ellipsis' xg-ellipsis-u.err` = 4 || Exit 1
|
||||
test `grep -c 'ASCII ellipsis' xg-ellipsis-u.err` = 3 || Exit 1
|
||||
|
||||
LANGUAGE= LC_ALL=C ${XGETTEXT} --omit-header --add-comments --check=ellipsis-unicode --sentence-end=double-space -d xg-ellipsis-ud.tmp xg-ellipsis-u.c 2>xg-ellipsis-ud.err
|
||||
|
||||
@ -40,7 +40,7 @@ EOF
|
||||
|
||||
LANGUAGE= LC_ALL=C ${XGETTEXT} --omit-header --add-comments --check=space-ellipsis -d xg-space-e.tmp xg-space-e.c 2>xg-space-e.err
|
||||
|
||||
test `grep -c 'space before ellipsis' xg-space-e.err` = 3 || Exit 1
|
||||
test `grep -c 'space before ellipsis' xg-space-e.err` = 2 || Exit 1
|
||||
|
||||
# --check=quote-unicode
|
||||
cat <<\EOF > xg-quote-u.c
|
||||
|
||||
@ -9,16 +9,67 @@ cat <<\EOF > xg-test20.c
|
||||
gettext ("Report bugs to <mailto:foobar@example.com>");
|
||||
gettext ("Report bugs to: bug-foobar@gnu.org");
|
||||
gettext ("Report bugs in the bug tracker at <https://savannah.gnu.org/projects/foobar>");
|
||||
/* xgettext: no-email-check */
|
||||
gettext ("M2: Report bugs to <mailto:foobar@example.com>");
|
||||
/* xgettext: no-email-check */
|
||||
gettext ("M2: Report bugs to: bug-foobar@gnu.org");
|
||||
/* xgettext: no-url-check */
|
||||
gettext ("M2: Report bugs in the bug tracker at <https://savannah.gnu.org/projects/foobar>");
|
||||
/* xgettext: no-url-check */
|
||||
gettext ("M3: Report bugs to: bug-foobar@gnu.org");
|
||||
EOF
|
||||
|
||||
: ${XGETTEXT=xgettext}
|
||||
LANGUAGE= LC_ALL=C ${XGETTEXT} --omit-header --add-comments -d xg-test20.tmp xg-test20.c 2>xg-test20.err \
|
||||
|| Exit 1
|
||||
cat xg-test20.err; echo
|
||||
|
||||
if grep "xg-test20.c:1:.*No-embedded-URLs.html" xg-test20.err; then
|
||||
if grep "xg-test20.c:1:.*No-embedded-URLs.html" xg-test20.err >/dev/null; then
|
||||
Exit 1
|
||||
fi
|
||||
|
||||
grep "xg-test20.c:2:.*No-embedded-URLs.html" xg-test20.err || Exit 1
|
||||
grep "xg-test20.c:3:.*No-embedded-URLs.html" xg-test20.err || Exit 1
|
||||
grep "xg-test20.c:4:.*No-embedded-URLs.html" xg-test20.err || Exit 1
|
||||
grep "xg-test20.c:2:.*No-embedded-URLs.html" xg-test20.err >/dev/null || Exit 1
|
||||
grep "xg-test20.c:3:.*No-embedded-URLs.html" xg-test20.err >/dev/null || Exit 1
|
||||
grep "xg-test20.c:4:.*No-embedded-URLs.html" xg-test20.err >/dev/null || Exit 1
|
||||
|
||||
if grep "xg-test20.c:6:.*No-embedded-URLs.html" xg-test20.err >/dev/null; then
|
||||
Exit 1
|
||||
fi
|
||||
if grep "xg-test20.c:8:.*No-embedded-URLs.html" xg-test20.err >/dev/null; then
|
||||
Exit 1
|
||||
fi
|
||||
if grep "xg-test20.c:10:.*No-embedded-URLs.html" xg-test20.err >/dev/null; then
|
||||
Exit 1
|
||||
fi
|
||||
|
||||
grep "xg-test20.c:12:.*No-embedded-URLs.html" xg-test20.err >/dev/null || Exit 1
|
||||
|
||||
# Likewise, with --no-check=url option:
|
||||
|
||||
LANGUAGE= LC_ALL=C ${XGETTEXT} --omit-header --add-comments --no-check=url -d xg-test20.tmp xg-test20.c 2>xg-test20a.err \
|
||||
|| Exit 1
|
||||
cat xg-test20a.err; echo
|
||||
|
||||
grep "xg-test20.c:2:.*No-embedded-URLs.html" xg-test20a.err >/dev/null || Exit 1
|
||||
grep "xg-test20.c:3:.*No-embedded-URLs.html" xg-test20a.err >/dev/null || Exit 1
|
||||
if grep "xg-test20.c:4:.*No-embedded-URLs.html" xg-test20a.err >/dev/null; then
|
||||
Exit 1
|
||||
fi
|
||||
grep "xg-test20.c:12:.*No-embedded-URLs.html" xg-test20a.err >/dev/null || Exit 1
|
||||
|
||||
# Likewise, with --no-check=email option:
|
||||
|
||||
LANGUAGE= LC_ALL=C ${XGETTEXT} --omit-header --add-comments --no-check=email -d xg-test20.tmp xg-test20.c 2>xg-test20b.err \
|
||||
|| Exit 1
|
||||
cat xg-test20b.err; echo
|
||||
|
||||
if grep "xg-test20.c:2:.*No-embedded-URLs.html" xg-test20b.err >/dev/null; then
|
||||
Exit 1
|
||||
fi
|
||||
if grep "xg-test20.c:3:.*No-embedded-URLs.html" xg-test20b.err >/dev/null; then
|
||||
Exit 1
|
||||
fi
|
||||
grep "xg-test20.c:4:.*No-embedded-URLs.html" xg-test20b.err >/dev/null || Exit 1
|
||||
if grep "xg-test20.c:12:.*No-embedded-URLs.html" xg-test20b.err >/dev/null; then
|
||||
Exit 1
|
||||
fi
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user