From: Bruno Haible Date: Sun, 4 Nov 2018 19:20:19 +0000 (+0100) Subject: xgettext: Split source code into smaller files. X-Git-Tag: v0.20~266 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c1c57702bcf034bbc5f2735f0445f6456158da5f;p=thirdparty%2Fgettext.git xgettext: Split source code into smaller files. * gettext-tools/src/rc-str-list.h: New file, extracted from gettext-tools/src/xgettext.h. * gettext-tools/src/xg-encoding.h: Likewise. * gettext-tools/src/xg-mixed-string.h: Likewise. * gettext-tools/src/xg-arglist-context.h: Likewise. * gettext-tools/src/xg-arglist-callshape.h: Likewise. * gettext-tools/src/xg-arglist-parser.h: Likewise. * gettext-tools/src/xg-message.h: Likewise. * gettext-tools/src/xg-encoding.c: New file, extracted from gettext-tools/src/xgettext.c. * gettext-tools/src/xg-mixed-string.c: Likewise. * gettext-tools/src/xg-arglist-context.c: Likewise. * gettext-tools/src/xg-arglist-callshape.c: Likewise. * gettext-tools/src/xg-arglist-parser.c: Likewise. * gettext-tools/src/xg-message.c: Likewise. * gettext-tools/src/xgettext.h: Remove declarations that moved to rc-str-list.h and xg-*.h. (add_all_comments, comment_tag, msgstr_prefix, msgstr_suffix, default_syntax_check, current_formatstring_parser1, current_formatstring_parser2, current_formatstring_parser3): New declarations. (xgettext_comment, xgettext_comment_reset, savable_comment_to_xgettext_comment, recognize_qt_formatstrings): New declarations. (substring_match): Remove obsolete declaration. * gettext-tools/src/xgettext.c: Include xgettext.h first. Include , rc-str-list.h, xg-encoding.h, xg-arglist-context.h, xg-message.h. Don't include xsize.h, po-xerror.h, unistr.h. (add_all_comments, comment_tag, msgstr_prefix, msgstr_suffix, default_syntax_check): Make global. (split_keywordspec, insert_keyword_callshape): Moved to xg-arglist-callshape.c. (null_context, passthrough_context, inherited_context, null_context_list_iterator, passthrough_context_circular_list, passthrough_context_list_iterator, flag_context_list_iterator, flag_context_list_iterator_advance, flag_context_list_table_lookup): Move to xg-arglist-context.c. (flag_context_list_table_insert): Move bulk of body to new function flag_context_list_table_add in xg-arglist-context.c. (xgettext_comment, xgettext_comment_reset, savable_comment_to_xgettext_comment): Make global. (current_formatstring_parser1, current_formatstring_parser2, current_formatstring_parser3): Make global. (non_ascii_error_message, from_current_source_encoding): Move to xg-encoding.c. (CONVERT_STRING, set_format_flags_from_context, warn_format_string, remember_a_message, remember_a_message_plural): Move to xg-message.c. (arglist_parser_alloc, arglist_parser_clone, arglist_parser_remember, arglist_parser_remember_msgctxt, arglist_parser_decidedp, arglist_parser_done): Move to xg-arglist-parser.c. (mixed_string_buffer_alloc, mixed_string_buffer_append_to_curr_buffer, mixed_string_buffer_grow_utf8_buffer, mixed_string_buffer_append_to_utf8_buffer, mixed_string_buffer_flush_utf16_surr, mixed_string_buffer_flush_curr_buffer, mixed_string_buffer_append_char, mixed_string_buffer_append_unicode, mixed_string_buffer_done): Move to xg-mixed-string.c. (recognize_qt_formatstrings): New function. * gettext-tools/src/x-*.h: Include xg-arglist-context.h instead of xgettext.h. * gettext-tools/src/x-*.c: Update include directives. * gettext-tools/src/its.h: Likewise. * gettext-tools/src/Makefile.am (noinst_HEADERS): Add the new .h files. (xgettext_SOURCES): Add the new .c files. * gettext-tools/src/FILES: Update. --- diff --git a/gettext-tools/src/FILES b/gettext-tools/src/FILES index b647a84b9..0a9bee97a 100644 --- a/gettext-tools/src/FILES +++ b/gettext-tools/src/FILES @@ -285,6 +285,30 @@ msgl-check.c +-------------- The 'xgettext' program | xgettext.h | Declarations used by the backends. +| rc-str-list.h +| A reference-counted list-of-immutable-strings type. +| xg-encoding.h +| xg-encoding.c +| Keeping track of the encoding of strings to be extracted. +| xg-mixed-string.h +| xg-mixed-string.c +| Handling strings that are given partially in the source +| encoding and partially in Unicode. +| xg-arglist-context.h +| xg-arglist-context.c +| Keeping track of the flags that apply to a string extracted +| in a certain context. +| xg-arglist-callshape.h +| xg-arglist-callshape.c +| Resolving ambiguity of argument lists: Information given +| through command-line options. +| xg-arglist-parser.h +| xg-arglist-parser.c +| Resolving ambiguity of argument lists: Progressive parsing +| of an argument list, keeping track of all possibilities. +| xg-message.h +| xg-message.c +| Extracting a message. Accumulating the message list. | x-c.h | x-c.c | String extractor for C. diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am index c94f7f3f3..bf5ea0e88 100644 --- a/gettext-tools/src/Makefile.am +++ b/gettext-tools/src/Makefile.am @@ -52,7 +52,10 @@ write-qt.h \ read-desktop.h write-desktop.h \ write-xml.h \ po-time.h plural-table.h lang-table.h format.h filters.h \ -xgettext.h x-c.h x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h \ +xgettext.h \ +rc-str-list.h xg-encoding.h xg-mixed-string.h xg-arglist-context.h \ +xg-arglist-callshape.h xg-arglist-parser.h xg-message.h \ +x-c.h x-po.h x-sh.h x-python.h x-lisp.h x-elisp.h x-librep.h \ x-scheme.h x-smalltalk.h x-java.h x-properties.h x-csharp.h x-awk.h x-ycp.h \ x-tcl.h x-perl.h x-php.h x-stringtable.h x-rst.h x-glade.h x-lua.h \ x-javascript.h x-vala.h x-gsettings.h x-desktop.h x-appdata.h @@ -177,6 +180,10 @@ else xgettext_SOURCES = ../woe32dll/c++xgettext.cc endif xgettext_SOURCES += \ + xg-encoding.c \ + xg-mixed-string.c \ + xg-arglist-context.c xg-arglist-callshape.c xg-arglist-parser.c \ + xg-message.c \ x-c.c x-po.c x-sh.c x-python.c x-lisp.c x-elisp.c x-librep.c x-scheme.c \ x-smalltalk.c x-java.c x-csharp.c x-awk.c x-ycp.c x-tcl.c x-perl.c x-php.c \ x-rst.c x-lua.c x-javascript.c x-vala.c \ diff --git a/gettext-tools/src/its.h b/gettext-tools/src/its.h index 242f079a4..72c30c992 100644 --- a/gettext-tools/src/its.h +++ b/gettext-tools/src/its.h @@ -1,5 +1,5 @@ /* Internationalization Tag Set (ITS) handling - Copyright (C) 2015-2016 Free Software Foundation, Inc. + Copyright (C) 2015, 2018 Free Software Foundation, Inc. This file was written by Daiki Ueno , 2015. @@ -19,8 +19,11 @@ #ifndef _ITS_H_ #define _ITS_H_ +#include + #include "message.h" -#include "xgettext.h" +#include "pos.h" +#include "xg-arglist-context.h" #ifdef __cplusplus extern "C" { diff --git a/gettext-tools/src/rc-str-list.h b/gettext-tools/src/rc-str-list.h new file mode 100644 index 000000000..3c4284446 --- /dev/null +++ b/gettext-tools/src/rc-str-list.h @@ -0,0 +1,67 @@ +/* Reference-counted string list. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _RC_STR_LIST_H +#define _RC_STR_LIST_H + +#include + +#include "str-list.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* A reference-counted list-of-immutable-strings type. */ + +typedef struct refcounted_string_list_ty refcounted_string_list_ty; +struct refcounted_string_list_ty +{ + unsigned int refcount; + struct string_list_ty contents; +}; + +static inline refcounted_string_list_ty * +add_reference (refcounted_string_list_ty *rslp) +{ + if (rslp != NULL) + rslp->refcount++; + return rslp; +} + +static inline void +drop_reference (refcounted_string_list_ty *rslp) +{ + if (rslp != NULL) + { + if (rslp->refcount > 1) + rslp->refcount--; + else + { + string_list_destroy (&rslp->contents); + free (rslp); + } + } +} + + +#ifdef __cplusplus +} +#endif + + +#endif /* _RC_STR_LIST_H */ diff --git a/gettext-tools/src/x-awk.c b/gettext-tools/src/x-awk.c index 36a8a4bdd..b918b1b17 100644 --- a/gettext-tools/src/x-awk.c +++ b/gettext-tools/src/x-awk.c @@ -1,5 +1,5 @@ /* xgettext awk backend. - Copyright (C) 2002-2003, 2005-2009, 2015-2016 Free Software Foundation, Inc. + Copyright (C) 2002-2003, 2005-2009, 2018 Free Software Foundation, Inc. This file was written by Bruno Haible , 2002. @@ -31,6 +31,10 @@ #include "message.h" #include "xgettext.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "error-progname.h" #include "xalloc.h" diff --git a/gettext-tools/src/x-awk.h b/gettext-tools/src/x-awk.h index f818d5582..af35d3711 100644 --- a/gettext-tools/src/x-awk.h +++ b/gettext-tools/src/x-awk.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-c.c b/gettext-tools/src/x-c.c index 3522bc8af..b13d77e7c 100644 --- a/gettext-tools/src/x-c.c +++ b/gettext-tools/src/x-c.c @@ -31,7 +31,14 @@ #include #include "message.h" +#include "rc-str-list.h" #include "xgettext.h" +#include "xg-encoding.h" +#include "xg-mixed-string.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "error-progname.h" #include "xalloc.h" diff --git a/gettext-tools/src/x-c.h b/gettext-tools/src/x-c.h index 5413aea6c..323c6187e 100644 --- a/gettext-tools/src/x-c.h +++ b/gettext-tools/src/x-c.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-csharp.c b/gettext-tools/src/x-csharp.c index f34b92b2d..8a2686601 100644 --- a/gettext-tools/src/x-csharp.c +++ b/gettext-tools/src/x-csharp.c @@ -1,6 +1,5 @@ /* xgettext C# backend. - Copyright (C) 2003, 2005-2009, 2011, 2015-2016 Free Software Foundation, - Inc. + Copyright (C) 2003, 2005-2009, 2011, 2014, 2018 Free Software Foundation, Inc. Written by Bruno Haible , 2003. This program is free software: you can redistribute it and/or modify @@ -30,7 +29,14 @@ #include #include "message.h" +#include "rc-str-list.h" #include "xgettext.h" +#include "xg-encoding.h" +#include "xg-mixed-string.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "c-ctype.h" #include "error.h" #include "error-progname.h" diff --git a/gettext-tools/src/x-csharp.h b/gettext-tools/src/x-csharp.h index 27f0b0241..a8744fb60 100644 --- a/gettext-tools/src/x-csharp.h +++ b/gettext-tools/src/x-csharp.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-desktop.c b/gettext-tools/src/x-desktop.c index c0b9fd284..f0987918d 100644 --- a/gettext-tools/src/x-desktop.c +++ b/gettext-tools/src/x-desktop.c @@ -1,5 +1,5 @@ /* xgettext Desktop Entry backend. - Copyright (C) 2014-2018 Free Software Foundation, Inc. + Copyright (C) 2014, 2018 Free Software Foundation, Inc. This file was written by Daiki Ueno , 2014. @@ -31,6 +31,7 @@ #include "message.h" #include "xgettext.h" +#include "xg-message.h" #include "error.h" #include "error-progname.h" #include "xalloc.h" diff --git a/gettext-tools/src/x-desktop.h b/gettext-tools/src/x-desktop.h index 47242e3a9..b60653055 100644 --- a/gettext-tools/src/x-desktop.h +++ b/gettext-tools/src/x-desktop.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-elisp.c b/gettext-tools/src/x-elisp.c index 0de3807a1..50582f22c 100644 --- a/gettext-tools/src/x-elisp.c +++ b/gettext-tools/src/x-elisp.c @@ -1,5 +1,5 @@ /* xgettext Emacs Lisp backend. - Copyright (C) 2001-2003, 2005-2009, 2015-2016 Free Software Foundation, Inc. + Copyright (C) 2001-2003, 2005-2009, 2018 Free Software Foundation, Inc. This file was written by Bruno Haible , 2001-2002. @@ -31,6 +31,10 @@ #include "message.h" #include "xgettext.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "xalloc.h" #include "hash.h" diff --git a/gettext-tools/src/x-elisp.h b/gettext-tools/src/x-elisp.h index 99490c258..f3806de60 100644 --- a/gettext-tools/src/x-elisp.h +++ b/gettext-tools/src/x-elisp.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-java.c b/gettext-tools/src/x-java.c index 8114e450a..3f65a5c1d 100644 --- a/gettext-tools/src/x-java.c +++ b/gettext-tools/src/x-java.c @@ -1,5 +1,5 @@ /* xgettext Java backend. - Copyright (C) 2003, 2005-2009, 2015-2016 Free Software Foundation, Inc. + Copyright (C) 2003, 2005-2009, 2018 Free Software Foundation, Inc. Written by Bruno Haible , 2003. This program is free software: you can redistribute it and/or modify @@ -29,8 +29,15 @@ #include #include "message.h" +#include "rc-str-list.h" #include "xgettext.h" +#include "xg-encoding.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" +#include "error-progname.h" #include "xalloc.h" #include "hash.h" #include "po-charset.h" diff --git a/gettext-tools/src/x-java.h b/gettext-tools/src/x-java.h index b2487e2dc..82a089278 100644 --- a/gettext-tools/src/x-java.h +++ b/gettext-tools/src/x-java.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-javascript.c b/gettext-tools/src/x-javascript.c index 09ddcfc17..fc144a036 100644 --- a/gettext-tools/src/x-javascript.c +++ b/gettext-tools/src/x-javascript.c @@ -1,6 +1,5 @@ /* xgettext JavaScript backend. - Copyright (C) 2002-2003, 2005-2009, 2013, 2015-2018 Free Software - Foundation, Inc. + Copyright (C) 2002-2003, 2005-2009, 2013-2014, 2018 Free Software Foundation, Inc. This file was written by Andreas Stricker , 2010 It's based on x-python from Bruno Haible. @@ -33,7 +32,14 @@ #include #include "message.h" +#include "rc-str-list.h" #include "xgettext.h" +#include "xg-encoding.h" +#include "xg-mixed-string.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "error-progname.h" #include "progname.h" diff --git a/gettext-tools/src/x-javascript.h b/gettext-tools/src/x-javascript.h index c59acc306..6188750a5 100644 --- a/gettext-tools/src/x-javascript.h +++ b/gettext-tools/src/x-javascript.h @@ -20,7 +20,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-librep.c b/gettext-tools/src/x-librep.c index 90724b882..fb5601004 100644 --- a/gettext-tools/src/x-librep.c +++ b/gettext-tools/src/x-librep.c @@ -1,5 +1,5 @@ /* xgettext librep backend. - Copyright (C) 2001-2003, 2005-2009, 2015-2016 Free Software Foundation, Inc. + Copyright (C) 2001-2003, 2005-2009, 2018 Free Software Foundation, Inc. This file was written by Bruno Haible , 2001. @@ -32,6 +32,10 @@ #include "c-ctype.h" #include "message.h" #include "xgettext.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "xalloc.h" #include "hash.h" diff --git a/gettext-tools/src/x-librep.h b/gettext-tools/src/x-librep.h index e1b735b6e..0da791f5e 100644 --- a/gettext-tools/src/x-librep.h +++ b/gettext-tools/src/x-librep.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-lisp.c b/gettext-tools/src/x-lisp.c index 5149b66c0..fd2802d6f 100644 --- a/gettext-tools/src/x-lisp.c +++ b/gettext-tools/src/x-lisp.c @@ -1,5 +1,5 @@ /* xgettext Lisp backend. - Copyright (C) 2001-2003, 2005-2009, 2015-2016 Free Software Foundation, Inc. + Copyright (C) 2001-2003, 2005-2009, 2018 Free Software Foundation, Inc. This file was written by Bruno Haible , 2001. @@ -31,6 +31,10 @@ #include "message.h" #include "xgettext.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "xalloc.h" #include "hash.h" diff --git a/gettext-tools/src/x-lisp.h b/gettext-tools/src/x-lisp.h index ea7512395..97b31dccf 100644 --- a/gettext-tools/src/x-lisp.h +++ b/gettext-tools/src/x-lisp.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-lua.c b/gettext-tools/src/x-lua.c index 88d6319b6..bd98adf25 100644 --- a/gettext-tools/src/x-lua.c +++ b/gettext-tools/src/x-lua.c @@ -1,5 +1,5 @@ /* xgettext Lua backend. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2013, 2016, 2018 Free Software Foundation, Inc. This file was written by Ľubomír Remák , 2012. @@ -29,7 +29,12 @@ #include #include "message.h" +#include "rc-str-list.h" #include "xgettext.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "xalloc.h" #include "gettext.h" diff --git a/gettext-tools/src/x-lua.h b/gettext-tools/src/x-lua.h index e30c751e2..dca3016aa 100644 --- a/gettext-tools/src/x-lua.h +++ b/gettext-tools/src/x-lua.h @@ -18,13 +18,15 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" + #ifdef __cplusplus extern "C" { #endif + #define EXTENSIONS_LUA \ { "lua", "Lua" }, \ @@ -43,6 +45,7 @@ extern "C" extern void init_flag_table_lua (void); + #ifdef __cplusplus } #endif diff --git a/gettext-tools/src/x-perl.c b/gettext-tools/src/x-perl.c index 7256dcbe8..ebae6d16b 100644 --- a/gettext-tools/src/x-perl.c +++ b/gettext-tools/src/x-perl.c @@ -1,5 +1,5 @@ /* xgettext Perl backend. - Copyright (C) 2002-2010, 2015-2018 Free Software Foundation, Inc. + Copyright (C) 2002-2010, 2013, 2016, 2018 Free Software Foundation, Inc. This file was written by Guido Flohr , 2002-2010. @@ -30,7 +30,13 @@ #include #include "message.h" +#include "rc-str-list.h" #include "xgettext.h" +#include "xg-encoding.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "error-progname.h" #include "xalloc.h" diff --git a/gettext-tools/src/x-perl.h b/gettext-tools/src/x-perl.h index 4832c9ba4..b375714c3 100644 --- a/gettext-tools/src/x-perl.h +++ b/gettext-tools/src/x-perl.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-php.c b/gettext-tools/src/x-php.c index 3ba171be8..dee1da4a4 100644 --- a/gettext-tools/src/x-php.c +++ b/gettext-tools/src/x-php.c @@ -1,5 +1,5 @@ /* xgettext PHP backend. - Copyright (C) 2001-2003, 2005-2010, 2015-2016 Free Software Foundation, Inc. + Copyright (C) 2001-2003, 2005-2010, 2014, 2018 Free Software Foundation, Inc. This file was written by Bruno Haible , 2002. @@ -29,7 +29,12 @@ #include #include "message.h" +#include "rc-str-list.h" #include "xgettext.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "xalloc.h" #include "gettext.h" diff --git a/gettext-tools/src/x-php.h b/gettext-tools/src/x-php.h index 2a0c216f9..f66abe64b 100644 --- a/gettext-tools/src/x-php.h +++ b/gettext-tools/src/x-php.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-po.c b/gettext-tools/src/x-po.c index ef9df200e..954dc9677 100644 --- a/gettext-tools/src/x-po.c +++ b/gettext-tools/src/x-po.c @@ -1,6 +1,5 @@ /* xgettext PO and JavaProperties backends. - Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2015-2016 Free - Software Foundation, Inc. + Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014, 2018 Free Software Foundation, Inc. This file was written by Peter Miller diff --git a/gettext-tools/src/x-po.h b/gettext-tools/src/x-po.h index c2f8ead40..41708db36 100644 --- a/gettext-tools/src/x-po.h +++ b/gettext-tools/src/x-po.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-properties.h b/gettext-tools/src/x-properties.h index 09a422ce0..20d486fcf 100644 --- a/gettext-tools/src/x-properties.h +++ b/gettext-tools/src/x-properties.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-python.c b/gettext-tools/src/x-python.c index 94b3c30a5..86e1b355b 100644 --- a/gettext-tools/src/x-python.c +++ b/gettext-tools/src/x-python.c @@ -1,5 +1,5 @@ /* xgettext Python backend. - Copyright (C) 2002-2003, 2005-2018 Free Software Foundation, Inc. + Copyright (C) 2002-2003, 2005-2011, 2013-2014, 2018 Free Software Foundation, Inc. This file was written by Bruno Haible , 2002. @@ -31,7 +31,14 @@ #include #include "message.h" +#include "rc-str-list.h" #include "xgettext.h" +#include "xg-encoding.h" +#include "xg-mixed-string.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "error-progname.h" #include "progname.h" diff --git a/gettext-tools/src/x-python.h b/gettext-tools/src/x-python.h index 8b0ff366c..d045ee603 100644 --- a/gettext-tools/src/x-python.h +++ b/gettext-tools/src/x-python.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-rst.c b/gettext-tools/src/x-rst.c index b3cf3a95d..512618d88 100644 --- a/gettext-tools/src/x-rst.c +++ b/gettext-tools/src/x-rst.c @@ -1,5 +1,5 @@ /* xgettext RST/RSJ backend. - Copyright (C) 2001-2003, 2005-2009, 2015-2016, 2018 Free Software Foundation, Inc. + Copyright (C) 2001-2003, 2005-2009, 2018 Free Software Foundation, Inc. This file was written by Bruno Haible , 2001. @@ -33,6 +33,9 @@ #include "po-charset.h" #include "message.h" #include "xgettext.h" +#include "xg-encoding.h" +#include "xg-mixed-string.h" +#include "xg-message.h" #include "error.h" #include "error-progname.h" #include "xalloc.h" diff --git a/gettext-tools/src/x-rst.h b/gettext-tools/src/x-rst.h index c470d0e4e..a5c2a0495 100644 --- a/gettext-tools/src/x-rst.h +++ b/gettext-tools/src/x-rst.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-scheme.c b/gettext-tools/src/x-scheme.c index d04e65568..3faec32c3 100644 --- a/gettext-tools/src/x-scheme.c +++ b/gettext-tools/src/x-scheme.c @@ -1,5 +1,5 @@ /* xgettext Scheme backend. - Copyright (C) 2004-2009, 2011, 2015-2018 Free Software Foundation, Inc. + Copyright (C) 2004-2009, 2011, 2014, 2018 Free Software Foundation, Inc. This file was written by Bruno Haible , 2004-2005. @@ -31,6 +31,10 @@ #include "message.h" #include "xgettext.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "xalloc.h" #include "hash.h" diff --git a/gettext-tools/src/x-scheme.h b/gettext-tools/src/x-scheme.h index b6ef0669a..6e5562b51 100644 --- a/gettext-tools/src/x-scheme.h +++ b/gettext-tools/src/x-scheme.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-sh.c b/gettext-tools/src/x-sh.c index 599f40b71..43cfdfda3 100644 --- a/gettext-tools/src/x-sh.c +++ b/gettext-tools/src/x-sh.c @@ -1,5 +1,5 @@ /* xgettext sh backend. - Copyright (C) 2003, 2005-2009, 2015-2016, 2018 Free Software Foundation, Inc. + Copyright (C) 2003, 2005-2009, 2014, 2018 Free Software Foundation, Inc. Written by Bruno Haible , 2003. This program is free software: you can redistribute it and/or modify @@ -31,7 +31,12 @@ #include "message.h" #include "xgettext.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" +#include "error-progname.h" #include "xalloc.h" #include "hash.h" #include "gettext.h" diff --git a/gettext-tools/src/x-sh.h b/gettext-tools/src/x-sh.h index 4bda07d2f..44b7e67ee 100644 --- a/gettext-tools/src/x-sh.h +++ b/gettext-tools/src/x-sh.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-smalltalk.c b/gettext-tools/src/x-smalltalk.c index d240577cd..628a5aac8 100644 --- a/gettext-tools/src/x-smalltalk.c +++ b/gettext-tools/src/x-smalltalk.c @@ -1,6 +1,5 @@ /* xgettext Smalltalk backend. - Copyright (C) 2002-2003, 2005-2009, 2011, 2015-2016 Free Software - Foundation, Inc. + Copyright (C) 2002-2003, 2005-2009, 2011, 2018 Free Software Foundation, Inc. This file was written by Bruno Haible , 2002. @@ -30,6 +29,7 @@ #include "message.h" #include "xgettext.h" +#include "xg-message.h" #include "error.h" #include "xalloc.h" #include "gettext.h" diff --git a/gettext-tools/src/x-smalltalk.h b/gettext-tools/src/x-smalltalk.h index f851b5607..14bd5efe5 100644 --- a/gettext-tools/src/x-smalltalk.h +++ b/gettext-tools/src/x-smalltalk.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-stringtable.h b/gettext-tools/src/x-stringtable.h index e9ec1755f..75505c48c 100644 --- a/gettext-tools/src/x-stringtable.h +++ b/gettext-tools/src/x-stringtable.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-tcl.c b/gettext-tools/src/x-tcl.c index 7da832814..5517bc7ac 100644 --- a/gettext-tools/src/x-tcl.c +++ b/gettext-tools/src/x-tcl.c @@ -1,5 +1,5 @@ /* xgettext Tcl backend. - Copyright (C) 2002-2003, 2005-2009, 2015-2016 Free Software Foundation, Inc. + Copyright (C) 2002-2003, 2005-2009, 2013, 2018 Free Software Foundation, Inc. This file was written by Bruno Haible , 2002. @@ -33,6 +33,11 @@ #include "message.h" #include "xgettext.h" +#include "xg-encoding.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "xalloc.h" #include "hash.h" diff --git a/gettext-tools/src/x-tcl.h b/gettext-tools/src/x-tcl.h index e802950eb..3efb09fff 100644 --- a/gettext-tools/src/x-tcl.h +++ b/gettext-tools/src/x-tcl.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-vala.c b/gettext-tools/src/x-vala.c index 7b9ae761f..b74b834de 100644 --- a/gettext-tools/src/x-vala.c +++ b/gettext-tools/src/x-vala.c @@ -31,7 +31,14 @@ #include #include "message.h" +#include "rc-str-list.h" #include "xgettext.h" +#include "xg-encoding.h" +#include "xg-mixed-string.h" +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" +#include "xg-arglist-parser.h" +#include "xg-message.h" #include "error.h" #include "error-progname.h" #include "xalloc.h" diff --git a/gettext-tools/src/x-vala.h b/gettext-tools/src/x-vala.h index 47ef5b5d8..686283422 100644 --- a/gettext-tools/src/x-vala.h +++ b/gettext-tools/src/x-vala.h @@ -18,7 +18,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/x-ycp.c b/gettext-tools/src/x-ycp.c index 019e85d95..3dc7b50a6 100644 --- a/gettext-tools/src/x-ycp.c +++ b/gettext-tools/src/x-ycp.c @@ -1,6 +1,5 @@ /* xgettext YCP backend. - Copyright (C) 2001-2003, 2005-2009, 2011, 2015-2016 Free Software - Foundation, Inc. + Copyright (C) 2001-2003, 2005-2009, 2011, 2018 Free Software Foundation, Inc. This file was written by Bruno Haible , 2001. @@ -31,7 +30,10 @@ #include #include "message.h" +#include "rc-str-list.h" #include "xgettext.h" +#include "xg-arglist-context.h" +#include "xg-message.h" #include "error.h" #include "xalloc.h" #include "gettext.h" diff --git a/gettext-tools/src/x-ycp.h b/gettext-tools/src/x-ycp.h index b6d139a22..d6f836b6b 100644 --- a/gettext-tools/src/x-ycp.h +++ b/gettext-tools/src/x-ycp.h @@ -19,7 +19,7 @@ #include #include "message.h" -#include "xgettext.h" +#include "xg-arglist-context.h" #ifdef __cplusplus diff --git a/gettext-tools/src/xg-arglist-callshape.c b/gettext-tools/src/xg-arglist-callshape.c new file mode 100644 index 000000000..06b70760b --- /dev/null +++ b/gettext-tools/src/xg-arglist-callshape.c @@ -0,0 +1,239 @@ +/* Resolving ambiguity of argument lists: Information given through + command-line options. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "xg-arglist-callshape.h" + +#include +#include +#include + +#include "xalloc.h" +#include "xsize.h" + + +void +split_keywordspec (const char *spec, + const char **endp, struct callshape *shapep) +{ + const char *p; + int argnum1 = 0; + int argnum2 = 0; + int argnumc = 0; + bool argnum1_glib_context = false; + bool argnum2_glib_context = false; + int argtotal = 0; + string_list_ty xcomments; + + string_list_init (&xcomments); + + /* Start parsing from the end. */ + p = spec + strlen (spec); + while (p > spec) + { + if (isdigit ((unsigned char) p[-1]) + || ((p[-1] == 'c' || p[-1] == 'g' || p[-1] == 't') + && p - 1 > spec && isdigit ((unsigned char) p[-2]))) + { + bool contextp = (p[-1] == 'c'); + bool glibp = (p[-1] == 'g'); + bool totalp = (p[-1] == 't'); + + do + p--; + while (p > spec && isdigit ((unsigned char) p[-1])); + + if (p > spec && (p[-1] == ',' || p[-1] == ':')) + { + char *dummy; + int arg = strtol (p, &dummy, 10); + + if (contextp) + { + if (argnumc != 0) + /* Only one context argument can be given. */ + break; + argnumc = arg; + } + else if (totalp) + { + if (argtotal != 0) + /* Only one total number of arguments can be given. */ + break; + argtotal = arg; + } + else + { + if (argnum2 != 0) + /* At most two normal arguments can be given. */ + break; + argnum2 = argnum1; + argnum2_glib_context = argnum1_glib_context; + argnum1 = arg; + argnum1_glib_context = glibp; + } + } + else + break; + } + else if (p[-1] == '"') + { + const char *xcomment_end; + + p--; + xcomment_end = p; + + while (p > spec && p[-1] != '"') + p--; + + if (p > spec /* && p[-1] == '"' */) + { + const char *xcomment_start; + + xcomment_start = p; + p--; + if (p > spec && (p[-1] == ',' || p[-1] == ':')) + { + size_t xcomment_len = xcomment_end - xcomment_start; + char *xcomment = XNMALLOC (xcomment_len + 1, char); + + memcpy (xcomment, xcomment_start, xcomment_len); + xcomment[xcomment_len] = '\0'; + string_list_append (&xcomments, xcomment); + } + else + break; + } + else + break; + } + else + break; + + /* Here an element of the comma-separated list has been parsed. */ + if (!(p > spec && (p[-1] == ',' || p[-1] == ':'))) + abort (); + p--; + if (*p == ':') + { + size_t i; + + if (argnum1 == 0 && argnum2 == 0) + /* At least one non-context argument must be given. */ + break; + if (argnumc != 0 + && (argnum1_glib_context || argnum2_glib_context)) + /* Incompatible ways to specify the context. */ + break; + *endp = p; + shapep->argnum1 = (argnum1 > 0 ? argnum1 : 1); + shapep->argnum2 = argnum2; + shapep->argnumc = argnumc; + shapep->argnum1_glib_context = argnum1_glib_context; + shapep->argnum2_glib_context = argnum2_glib_context; + shapep->argtotal = argtotal; + /* Reverse the order of the xcomments. */ + string_list_init (&shapep->xcomments); + for (i = xcomments.nitems; i > 0; ) + string_list_append (&shapep->xcomments, xcomments.item[--i]); + string_list_destroy (&xcomments); + return; + } + } + + /* Couldn't parse the desired syntax. */ + *endp = spec + strlen (spec); + shapep->argnum1 = 1; + shapep->argnum2 = 0; + shapep->argnumc = 0; + shapep->argnum1_glib_context = false; + shapep->argnum2_glib_context = false; + shapep->argtotal = 0; + string_list_init (&shapep->xcomments); + string_list_destroy (&xcomments); +} + + +void +insert_keyword_callshape (hash_table *table, + const char *keyword, size_t keyword_len, + const struct callshape *shape) +{ + void *old_value; + + if (hash_find_entry (table, keyword, keyword_len, &old_value)) + { + /* Create a one-element 'struct callshapes'. */ + struct callshapes *shapes = XMALLOC (struct callshapes); + shapes->nshapes = 1; + shapes->shapes[0] = *shape; + keyword = + (const char *) hash_insert_entry (table, keyword, keyword_len, shapes); + if (keyword == NULL) + abort (); + shapes->keyword = keyword; + shapes->keyword_len = keyword_len; + } + else + { + /* Found a 'struct callshapes'. See whether it already contains the + desired shape. */ + struct callshapes *old_shapes = (struct callshapes *) old_value; + bool found; + size_t i; + + found = false; + for (i = 0; i < old_shapes->nshapes; i++) + if (old_shapes->shapes[i].argnum1 == shape->argnum1 + && old_shapes->shapes[i].argnum2 == shape->argnum2 + && old_shapes->shapes[i].argnumc == shape->argnumc + && old_shapes->shapes[i].argnum1_glib_context + == shape->argnum1_glib_context + && old_shapes->shapes[i].argnum2_glib_context + == shape->argnum2_glib_context + && old_shapes->shapes[i].argtotal == shape->argtotal) + { + old_shapes->shapes[i].xcomments = shape->xcomments; + found = true; + break; + } + + if (!found) + { + /* Replace the existing 'struct callshapes' with a new one. */ + struct callshapes *shapes = + (struct callshapes *) + xmalloc (xsum (sizeof (struct callshapes), + xtimes (old_shapes->nshapes, + sizeof (struct callshape)))); + + shapes->keyword = old_shapes->keyword; + shapes->keyword_len = old_shapes->keyword_len; + shapes->nshapes = old_shapes->nshapes + 1; + for (i = 0; i < old_shapes->nshapes; i++) + shapes->shapes[i] = old_shapes->shapes[i]; + shapes->shapes[i] = *shape; + if (hash_set_value (table, keyword, keyword_len, shapes)) + abort (); + free (old_shapes); + } + } +} diff --git a/gettext-tools/src/xg-arglist-callshape.h b/gettext-tools/src/xg-arglist-callshape.h new file mode 100644 index 000000000..6659f45a4 --- /dev/null +++ b/gettext-tools/src/xg-arglist-callshape.h @@ -0,0 +1,69 @@ +/* Resolving ambiguity of argument lists: Information given through + command-line options. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _XGETTEXT_ARGLIST_CALLSHAPE_H +#define _XGETTEXT_ARGLIST_CALLSHAPE_H + +#include +#include + +#include "str-list.h" +#include "hash.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Calling convention for a given keyword. */ +struct callshape +{ + int argnum1; /* argument number to use for msgid */ + int argnum2; /* argument number to use for msgid_plural */ + int argnumc; /* argument number to use for msgctxt */ + bool argnum1_glib_context; /* argument argnum1 has the syntax "ctxt|msgid" */ + bool argnum2_glib_context; /* argument argnum2 has the syntax "ctxt|msgid" */ + int argtotal; /* total number of arguments */ + string_list_ty xcomments; /* auto-extracted comments */ +}; + +/* Split keyword spec into keyword, argnum1, argnum2, argnumc. */ +extern void split_keywordspec (const char *spec, const char **endp, + struct callshape *shapep); + +/* Set of alternative calling conventions for a given keyword. */ +struct callshapes +{ + const char *keyword; /* the keyword, not NUL terminated */ + size_t keyword_len; /* the keyword's length */ + size_t nshapes; + struct callshape shapes[1]; /* actually nshapes elements */ +}; + +/* Insert a (keyword, callshape) pair into a hash table mapping keyword to + 'struct callshapes *'. */ +extern void insert_keyword_callshape (hash_table *table, + const char *keyword, size_t keyword_len, + const struct callshape *shape); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _XGETTEXT_ARGLIST_CALLSHAPE_H */ diff --git a/gettext-tools/src/xg-arglist-context.c b/gettext-tools/src/xg-arglist-context.c new file mode 100644 index 000000000..68feba582 --- /dev/null +++ b/gettext-tools/src/xg-arglist-context.c @@ -0,0 +1,260 @@ +/* Keeping track of the flags that apply to a string extracted + in a certain context. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "xg-arglist-context.h" + +#include + +#include "xalloc.h" +#include "xmalloca.h" + + +/* Null context. */ +flag_context_ty null_context = { undecided, false, undecided, false }; + +/* Transparent context. */ +flag_context_ty passthrough_context = { undecided, true, undecided, true }; + + +flag_context_ty +inherited_context (flag_context_ty outer_context, + flag_context_ty modifier_context) +{ + flag_context_ty result = modifier_context; + + if (result.pass_format1) + { + result.is_format1 = outer_context.is_format1; + result.pass_format1 = false; + } + if (result.pass_format2) + { + result.is_format2 = outer_context.is_format2; + result.pass_format2 = false; + } + if (result.pass_format3) + { + result.is_format3 = outer_context.is_format3; + result.pass_format3 = false; + } + return result; +} + + +/* Null context list iterator. */ +flag_context_list_iterator_ty null_context_list_iterator = { 1, NULL }; + +/* Transparent context list iterator. */ +static flag_context_list_ty passthrough_context_circular_list = + { + 1, + { undecided, true, undecided, true }, + &passthrough_context_circular_list + }; +flag_context_list_iterator_ty passthrough_context_list_iterator = + { + 1, + &passthrough_context_circular_list + }; + + +flag_context_list_iterator_ty +flag_context_list_iterator (flag_context_list_ty *list) +{ + flag_context_list_iterator_ty result; + + result.argnum = 1; + result.head = list; + return result; +} + + +flag_context_ty +flag_context_list_iterator_advance (flag_context_list_iterator_ty *iter) +{ + if (iter->head == NULL) + return null_context; + if (iter->argnum == iter->head->argnum) + { + flag_context_ty result = iter->head->flags; + + /* Special casing of circular list. */ + if (iter->head != iter->head->next) + { + iter->head = iter->head->next; + iter->argnum++; + } + + return result; + } + else + { + iter->argnum++; + return null_context; + } +} + + +flag_context_list_ty * +flag_context_list_table_lookup (flag_context_list_table_ty *flag_table, + const void *key, size_t keylen) +{ + void *entry; + + if (flag_table->table != NULL + && hash_find_entry (flag_table, key, keylen, &entry) == 0) + return (flag_context_list_ty *) entry; + else + return NULL; +} + + +void +flag_context_list_table_add (flag_context_list_table_ty *table, + unsigned int index, + const char *name_start, const char *name_end, + int argnum, enum is_format value, bool pass) +{ + /* Insert the pair (VALUE, PASS) at INDEX in the element numbered ARGNUM + of the list corresponding to NAME in the TABLE. */ + if (table->table == NULL) + hash_init (table, 100); + { + void *entry; + + if (hash_find_entry (table, name_start, name_end - name_start, &entry) != 0) + { + /* Create new hash table entry. */ + flag_context_list_ty *list = XMALLOC (flag_context_list_ty); + list->argnum = argnum; + memset (&list->flags, '\0', sizeof (list->flags)); + switch (index) + { + case 0: + list->flags.is_format1 = value; + list->flags.pass_format1 = pass; + break; + case 1: + list->flags.is_format2 = value; + list->flags.pass_format2 = pass; + break; + case 2: + list->flags.is_format3 = value; + list->flags.pass_format3 = pass; + break; + default: + abort (); + } + list->next = NULL; + hash_insert_entry (table, name_start, name_end - name_start, list); + } + else + { + flag_context_list_ty *list = (flag_context_list_ty *)entry; + flag_context_list_ty **lastp = NULL; + /* Invariant: list == (lastp != NULL ? *lastp : entry). */ + + while (list != NULL && list->argnum < argnum) + { + lastp = &list->next; + list = *lastp; + } + if (list != NULL && list->argnum == argnum) + { + /* Add this flag to the current argument number. */ + switch (index) + { + case 0: + list->flags.is_format1 = value; + list->flags.pass_format1 = pass; + break; + case 1: + list->flags.is_format2 = value; + list->flags.pass_format2 = pass; + break; + case 2: + list->flags.is_format3 = value; + list->flags.pass_format3 = pass; + break; + default: + abort (); + } + } + else if (lastp != NULL) + { + /* Add a new list entry for this argument number. */ + list = XMALLOC (flag_context_list_ty); + list->argnum = argnum; + memset (&list->flags, '\0', sizeof (list->flags)); + switch (index) + { + case 0: + list->flags.is_format1 = value; + list->flags.pass_format1 = pass; + break; + case 1: + list->flags.is_format2 = value; + list->flags.pass_format2 = pass; + break; + case 2: + list->flags.is_format3 = value; + list->flags.pass_format3 = pass; + break; + default: + abort (); + } + list->next = *lastp; + *lastp = list; + } + else + { + /* Add a new list entry for this argument number, at the beginning + of the list. Since we don't have an API for replacing the + value of a key in the hash table, we have to copy the first + list element. */ + flag_context_list_ty *copy = XMALLOC (flag_context_list_ty); + *copy = *list; + + list->argnum = argnum; + memset (&list->flags, '\0', sizeof (list->flags)); + switch (index) + { + case 0: + list->flags.is_format1 = value; + list->flags.pass_format1 = pass; + break; + case 1: + list->flags.is_format2 = value; + list->flags.pass_format2 = pass; + break; + case 2: + list->flags.is_format3 = value; + list->flags.pass_format3 = pass; + break; + default: + abort (); + } + list->next = copy; + } + } + } +} diff --git a/gettext-tools/src/xg-arglist-context.h b/gettext-tools/src/xg-arglist-context.h new file mode 100644 index 000000000..16508c3c6 --- /dev/null +++ b/gettext-tools/src/xg-arglist-context.h @@ -0,0 +1,99 @@ +/* Keeping track of the flags that apply to a string extracted + in a certain context. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _XGETTEXT_ARGLIST_CONTEXT_H +#define _XGETTEXT_ARGLIST_CONTEXT_H + +#include + +#include "hash.h" +#include "message.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Context representing some flags. */ +typedef struct flag_context_ty flag_context_ty; +struct flag_context_ty +{ + /* Regarding the primary formatstring type. */ + /*enum is_format*/ unsigned int is_format1 : 3; + /*bool*/ unsigned int pass_format1 : 1; + /* Regarding the secondary formatstring type. */ + /*enum is_format*/ unsigned int is_format2 : 3; + /*bool*/ unsigned int pass_format2 : 1; + /* Regarding the tertiary formatstring type. */ + /*enum is_format*/ unsigned int is_format3 : 3; + /*bool*/ unsigned int pass_format3 : 1; +}; +/* Null context. */ +extern flag_context_ty null_context; +/* Transparent context. */ +extern flag_context_ty passthrough_context; +/* Compute an inherited context. + The outer_context is assumed to have all pass_format* flags = false. + The result will then also have all pass_format* flags = false. */ +extern flag_context_ty + inherited_context (flag_context_ty outer_context, + flag_context_ty modifier_context); + +/* Context representing some flags, for each possible argument number. + This is a linked list, sorted according to the argument number. */ +typedef struct flag_context_list_ty flag_context_list_ty; +struct flag_context_list_ty +{ + int argnum; /* current argument number, > 0 */ + flag_context_ty flags; /* flags for current argument */ + flag_context_list_ty *next; +}; + +/* Iterator through a flag_context_list_ty. */ +typedef struct flag_context_list_iterator_ty flag_context_list_iterator_ty; +struct flag_context_list_iterator_ty +{ + int argnum; /* current argument number, > 0 */ + const flag_context_list_ty* head; /* tail of list */ +}; +extern flag_context_list_iterator_ty null_context_list_iterator; +extern flag_context_list_iterator_ty passthrough_context_list_iterator; +extern flag_context_list_iterator_ty + flag_context_list_iterator (flag_context_list_ty *list); +extern flag_context_ty + flag_context_list_iterator_advance (flag_context_list_iterator_ty *iter); + +/* For nearly each backend, we have a separate table mapping a keyword to + a flag_context_list_ty *. */ +typedef hash_table /* char[] -> flag_context_list_ty * */ + flag_context_list_table_ty; +extern flag_context_list_ty * + flag_context_list_table_lookup (flag_context_list_table_ty *flag_table, + const void *key, size_t keylen); +extern void + flag_context_list_table_add (flag_context_list_table_ty *table, + unsigned int index, + const char *name_start, const char *name_end, + int argnum, enum is_format value, bool pass); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _XGETTEXT_ARGLIST_CONTEXT_H */ diff --git a/gettext-tools/src/xg-arglist-parser.c b/gettext-tools/src/xg-arglist-parser.c new file mode 100644 index 000000000..bb97b41ef --- /dev/null +++ b/gettext-tools/src/xg-arglist-parser.c @@ -0,0 +1,539 @@ +/* Resolving ambiguity of argument lists: Progressive parsing of an + argument list, keeping track of all possibilities. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "xg-arglist-parser.h" + +#include +#include + +#include "error.h" +#include "error-progname.h" +#include "xalloc.h" +#include "xsize.h" + +#include "xgettext.h" +#include "xg-message.h" + +#include "gettext.h" +#define _(str) gettext (str) + + +struct arglist_parser * +arglist_parser_alloc (message_list_ty *mlp, const struct callshapes *shapes) +{ + if (shapes == NULL || shapes->nshapes == 0) + { + struct arglist_parser *ap = + (struct arglist_parser *) + xmalloc (offsetof (struct arglist_parser, alternative[0])); + + ap->mlp = mlp; + ap->keyword = NULL; + ap->keyword_len = 0; + ap->next_is_msgctxt = false; + ap->nalternatives = 0; + + return ap; + } + else + { + struct arglist_parser *ap = + (struct arglist_parser *) + xmalloc (xsum (sizeof (struct arglist_parser), + xtimes (shapes->nshapes - 1, + sizeof (struct partial_call)))); + size_t i; + + ap->mlp = mlp; + ap->keyword = shapes->keyword; + ap->keyword_len = shapes->keyword_len; + ap->next_is_msgctxt = false; + ap->nalternatives = shapes->nshapes; + for (i = 0; i < shapes->nshapes; i++) + { + ap->alternative[i].argnumc = shapes->shapes[i].argnumc; + ap->alternative[i].argnum1 = shapes->shapes[i].argnum1; + ap->alternative[i].argnum2 = shapes->shapes[i].argnum2; + ap->alternative[i].argnum1_glib_context = + shapes->shapes[i].argnum1_glib_context; + ap->alternative[i].argnum2_glib_context = + shapes->shapes[i].argnum2_glib_context; + ap->alternative[i].argtotal = shapes->shapes[i].argtotal; + ap->alternative[i].xcomments = shapes->shapes[i].xcomments; + ap->alternative[i].msgctxt = NULL; + ap->alternative[i].msgctxt_pos.file_name = NULL; + ap->alternative[i].msgctxt_pos.line_number = (size_t)(-1); + ap->alternative[i].msgid = NULL; + ap->alternative[i].msgid_context = null_context; + ap->alternative[i].msgid_pos.file_name = NULL; + ap->alternative[i].msgid_pos.line_number = (size_t)(-1); + ap->alternative[i].msgid_comment = NULL; + ap->alternative[i].msgid_plural = NULL; + ap->alternative[i].msgid_plural_context = null_context; + ap->alternative[i].msgid_plural_pos.file_name = NULL; + ap->alternative[i].msgid_plural_pos.line_number = (size_t)(-1); + } + + return ap; + } +} + + +struct arglist_parser * +arglist_parser_clone (struct arglist_parser *ap) +{ + struct arglist_parser *copy = + (struct arglist_parser *) + xmalloc (xsum (sizeof (struct arglist_parser) - sizeof (struct partial_call), + xtimes (ap->nalternatives, sizeof (struct partial_call)))); + size_t i; + + copy->mlp = ap->mlp; + copy->keyword = ap->keyword; + copy->keyword_len = ap->keyword_len; + copy->next_is_msgctxt = ap->next_is_msgctxt; + copy->nalternatives = ap->nalternatives; + for (i = 0; i < ap->nalternatives; i++) + { + const struct partial_call *cp = &ap->alternative[i]; + struct partial_call *ccp = ©->alternative[i]; + + ccp->argnumc = cp->argnumc; + ccp->argnum1 = cp->argnum1; + ccp->argnum2 = cp->argnum2; + ccp->argnum1_glib_context = cp->argnum1_glib_context; + ccp->argnum2_glib_context = cp->argnum2_glib_context; + ccp->argtotal = cp->argtotal; + ccp->xcomments = cp->xcomments; + ccp->msgctxt = (cp->msgctxt != NULL ? xstrdup (cp->msgctxt) : NULL); + ccp->msgctxt_pos = cp->msgctxt_pos; + ccp->msgid = (cp->msgid != NULL ? xstrdup (cp->msgid) : NULL); + ccp->msgid_context = cp->msgid_context; + ccp->msgid_pos = cp->msgctxt_pos; + ccp->msgid_comment = add_reference (cp->msgid_comment); + ccp->msgid_plural = + (cp->msgid_plural != NULL ? xstrdup (cp->msgid_plural) : NULL); + ccp->msgid_plural_context = cp->msgid_plural_context; + ccp->msgid_plural_pos = cp->msgid_plural_pos; + } + + return copy; +} + + +void +arglist_parser_remember (struct arglist_parser *ap, + int argnum, char *string, + flag_context_ty context, + char *file_name, size_t line_number, + refcounted_string_list_ty *comment) +{ + bool stored_string = false; + size_t nalternatives = ap->nalternatives; + size_t i; + + if (!(argnum > 0)) + abort (); + for (i = 0; i < nalternatives; i++) + { + struct partial_call *cp = &ap->alternative[i]; + + if (argnum == cp->argnumc) + { + cp->msgctxt = string; + cp->msgctxt_pos.file_name = file_name; + cp->msgctxt_pos.line_number = line_number; + stored_string = true; + /* Mark msgctxt as done. */ + cp->argnumc = 0; + } + else + { + if (argnum == cp->argnum1) + { + cp->msgid = string; + cp->msgid_context = context; + cp->msgid_pos.file_name = file_name; + cp->msgid_pos.line_number = line_number; + cp->msgid_comment = add_reference (comment); + stored_string = true; + /* Mark msgid as done. */ + cp->argnum1 = 0; + } + if (argnum == cp->argnum2) + { + cp->msgid_plural = string; + cp->msgid_plural_context = context; + cp->msgid_plural_pos.file_name = file_name; + cp->msgid_plural_pos.line_number = line_number; + stored_string = true; + /* Mark msgid_plural as done. */ + cp->argnum2 = 0; + } + } + } + /* Note: There is a memory leak here: When string was stored but is later + not used by arglist_parser_done, we don't free it. */ + if (!stored_string) + free (string); +} + + +void +arglist_parser_remember_msgctxt (struct arglist_parser *ap, + char *string, + flag_context_ty context, + char *file_name, size_t line_number) +{ + bool stored_string = false; + size_t nalternatives = ap->nalternatives; + size_t i; + + for (i = 0; i < nalternatives; i++) + { + struct partial_call *cp = &ap->alternative[i]; + + cp->msgctxt = string; + cp->msgctxt_pos.file_name = file_name; + cp->msgctxt_pos.line_number = line_number; + stored_string = true; + /* Mark msgctxt as done. */ + cp->argnumc = 0; + } + /* Note: There is a memory leak here: When string was stored but is later + not used by arglist_parser_done, we don't free it. */ + if (!stored_string) + free (string); +} + + +bool +arglist_parser_decidedp (struct arglist_parser *ap, int argnum) +{ + size_t i; + + /* Test whether all alternatives are decided. + Note: A decided alternative can be complete + cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 + && cp->argtotal == 0 + or it can be failed if no literal strings were found at the specified + argument positions: + cp->argnumc <= argnum && cp->argnum1 <= argnum && cp->argnum2 <= argnum + or it can be failed if the number of arguments is exceeded: + cp->argtotal > 0 && cp->argtotal < argnum + */ + for (i = 0; i < ap->nalternatives; i++) + { + struct partial_call *cp = &ap->alternative[i]; + + if (!((cp->argnumc <= argnum + && cp->argnum1 <= argnum + && cp->argnum2 <= argnum) + || (cp->argtotal > 0 && cp->argtotal < argnum))) + /* cp is still undecided. */ + return false; + } + return true; +} + + +void +arglist_parser_done (struct arglist_parser *ap, int argnum) +{ + size_t ncomplete; + size_t i; + + /* Determine the number of complete calls. */ + ncomplete = 0; + for (i = 0; i < ap->nalternatives; i++) + { + struct partial_call *cp = &ap->alternative[i]; + + if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 + && (cp->argtotal == 0 || cp->argtotal == argnum)) + ncomplete++; + } + + if (ncomplete > 0) + { + struct partial_call *best_cp = NULL; + bool ambiguous = false; + + /* Find complete calls where msgctxt, msgid, msgid_plural are all + provided. */ + for (i = 0; i < ap->nalternatives; i++) + { + struct partial_call *cp = &ap->alternative[i]; + + if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 + && (cp->argtotal == 0 || cp->argtotal == argnum) + && cp->msgctxt != NULL + && cp->msgid != NULL + && cp->msgid_plural != NULL) + { + if (best_cp != NULL) + { + ambiguous = true; + break; + } + best_cp = cp; + } + } + + if (best_cp == NULL) + { + struct partial_call *best_cp1 = NULL; + struct partial_call *best_cp2 = NULL; + + /* Find complete calls where msgctxt, msgid are provided. */ + for (i = 0; i < ap->nalternatives; i++) + { + struct partial_call *cp = &ap->alternative[i]; + + if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 + && (cp->argtotal == 0 || cp->argtotal == argnum) + && cp->msgctxt != NULL + && cp->msgid != NULL) + { + if (best_cp1 != NULL) + { + ambiguous = true; + break; + } + best_cp1 = cp; + } + } + + /* Find complete calls where msgid, msgid_plural are provided. */ + for (i = 0; i < ap->nalternatives; i++) + { + struct partial_call *cp = &ap->alternative[i]; + + if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 + && (cp->argtotal == 0 || cp->argtotal == argnum) + && cp->msgid != NULL + && cp->msgid_plural != NULL) + { + if (best_cp2 != NULL) + { + ambiguous = true; + break; + } + best_cp2 = cp; + } + } + + if (best_cp1 != NULL) + best_cp = best_cp1; + if (best_cp2 != NULL) + { + if (best_cp != NULL) + ambiguous = true; + else + best_cp = best_cp2; + } + } + + if (best_cp == NULL) + { + /* Find complete calls where msgid is provided. */ + for (i = 0; i < ap->nalternatives; i++) + { + struct partial_call *cp = &ap->alternative[i]; + + if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 + && (cp->argtotal == 0 || cp->argtotal == argnum) + && cp->msgid != NULL) + { + if (best_cp != NULL) + { + ambiguous = true; + break; + } + best_cp = cp; + } + } + } + + if (ambiguous) + { + error_with_progname = false; + error_at_line (0, 0, + best_cp->msgid_pos.file_name, + best_cp->msgid_pos.line_number, + _("ambiguous argument specification for keyword '%.*s'"), + (int) ap->keyword_len, ap->keyword); + error_with_progname = true; + } + + if (best_cp != NULL) + { + /* best_cp indicates the best found complete call. + Now call remember_a_message. */ + message_ty *mp; + + /* Split strings in the GNOME glib syntax "msgctxt|msgid". */ + if (best_cp->argnum1_glib_context || best_cp->argnum2_glib_context) + /* split_keywordspec should not allow the context to be specified + in two different ways. */ + if (best_cp->msgctxt != NULL) + abort (); + if (best_cp->argnum1_glib_context) + { + const char *separator = strchr (best_cp->msgid, '|'); + + if (separator == NULL) + { + error_with_progname = false; + error_at_line (0, 0, + best_cp->msgid_pos.file_name, + best_cp->msgid_pos.line_number, + _("warning: missing context for keyword '%.*s'"), + (int) ap->keyword_len, ap->keyword); + error_with_progname = true; + } + else + { + size_t ctxt_len = separator - best_cp->msgid; + char *ctxt = XNMALLOC (ctxt_len + 1, char); + + memcpy (ctxt, best_cp->msgid, ctxt_len); + ctxt[ctxt_len] = '\0'; + best_cp->msgctxt = ctxt; + best_cp->msgid = xstrdup (separator + 1); + } + } + if (best_cp->msgid_plural != NULL && best_cp->argnum2_glib_context) + { + const char *separator = strchr (best_cp->msgid_plural, '|'); + + if (separator == NULL) + { + error_with_progname = false; + error_at_line (0, 0, + best_cp->msgid_plural_pos.file_name, + best_cp->msgid_plural_pos.line_number, + _("warning: missing context for plural argument of keyword '%.*s'"), + (int) ap->keyword_len, ap->keyword); + error_with_progname = true; + } + else + { + size_t ctxt_len = separator - best_cp->msgid_plural; + char *ctxt = XNMALLOC (ctxt_len + 1, char); + + memcpy (ctxt, best_cp->msgid_plural, ctxt_len); + ctxt[ctxt_len] = '\0'; + if (best_cp->msgctxt == NULL) + best_cp->msgctxt = ctxt; + else + { + if (strcmp (ctxt, best_cp->msgctxt) != 0) + { + error_with_progname = false; + error_at_line (0, 0, + best_cp->msgid_plural_pos.file_name, + best_cp->msgid_plural_pos.line_number, + _("context mismatch between singular and plural form")); + error_with_progname = true; + } + free (ctxt); + } + best_cp->msgid_plural = xstrdup (separator + 1); + } + } + + { + flag_context_ty msgid_context = best_cp->msgid_context; + flag_context_ty msgid_plural_context = best_cp->msgid_plural_context; + + /* Special support for the 3-argument tr operator in Qt: + When --qt and --keyword=tr:1,1,2c,3t are specified, add to the + context the information that the argument is expected to be a + qt-plural-format. */ + if (recognize_qt_formatstrings () + && best_cp->msgid_plural == best_cp->msgid) + { + msgid_context.is_format3 = yes_according_to_context; + msgid_plural_context.is_format3 = yes_according_to_context; + } + + mp = remember_a_message (ap->mlp, best_cp->msgctxt, best_cp->msgid, + msgid_context, + &best_cp->msgid_pos, + NULL, best_cp->msgid_comment); + if (mp != NULL && best_cp->msgid_plural != NULL) + remember_a_message_plural (mp, best_cp->msgid_plural, + msgid_plural_context, + &best_cp->msgid_plural_pos, + NULL); + } + + if (best_cp->xcomments.nitems > 0) + { + /* Add best_cp->xcomments to mp->comment_dot, unless already + present. */ + size_t i; + + for (i = 0; i < best_cp->xcomments.nitems; i++) + { + const char *xcomment = best_cp->xcomments.item[i]; + bool found = false; + + if (mp != NULL && mp->comment_dot != NULL) + { + size_t j; + + for (j = 0; j < mp->comment_dot->nitems; j++) + if (strcmp (xcomment, mp->comment_dot->item[j]) == 0) + { + found = true; + break; + } + } + if (!found) + message_comment_dot_append (mp, xcomment); + } + } + } + } + else + { + /* No complete call was parsed. */ + /* Note: There is a memory leak here: When there is more than one + alternative, the same string can be stored in multiple alternatives, + and it's not easy to free all strings reliably. */ + if (ap->nalternatives == 1) + { + if (ap->alternative[0].msgctxt != NULL) + free (ap->alternative[0].msgctxt); + if (ap->alternative[0].msgid != NULL) + free (ap->alternative[0].msgid); + if (ap->alternative[0].msgid_plural != NULL) + free (ap->alternative[0].msgid_plural); + } + } + + for (i = 0; i < ap->nalternatives; i++) + drop_reference (ap->alternative[i].msgid_comment); + free (ap); +} diff --git a/gettext-tools/src/xg-arglist-parser.h b/gettext-tools/src/xg-arglist-parser.h new file mode 100644 index 000000000..bad14fcad --- /dev/null +++ b/gettext-tools/src/xg-arglist-parser.h @@ -0,0 +1,108 @@ +/* Resolving ambiguity of argument lists: Progressive parsing of an + argument list, keeping track of all possibilities. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _XGETTEXT_ARGLIST_PARSER_H +#define _XGETTEXT_ARGLIST_PARSER_H + +#include +#include + +#include "pos.h" +#include "rc-str-list.h" +#include "str-list.h" + +#include "xg-arglist-context.h" +#include "xg-arglist-callshape.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Represents the progressive parsing of an argument list w.r.t. a single + 'struct callshape'. */ +struct partial_call +{ + int argnumc; /* number of context argument, 0 when seen */ + int argnum1; /* number of singular argument, 0 when seen */ + int argnum2; /* number of plural argument, 0 when seen */ + bool argnum1_glib_context; /* argument argnum1 has the syntax "ctxt|msgid" */ + bool argnum2_glib_context; /* argument argnum2 has the syntax "ctxt|msgid" */ + int argtotal; /* total number of arguments, 0 if unspecified */ + string_list_ty xcomments; /* auto-extracted comments */ + char *msgctxt; /* context - owned string, or NULL */ + lex_pos_ty msgctxt_pos; + char *msgid; /* msgid - owned string, or NULL */ + flag_context_ty msgid_context; + lex_pos_ty msgid_pos; + refcounted_string_list_ty *msgid_comment; + char *msgid_plural; /* msgid_plural - owned string, or NULL */ + flag_context_ty msgid_plural_context; + lex_pos_ty msgid_plural_pos; +}; + +/* Represents the progressive parsing of an argument list w.r.t. an entire + 'struct callshapes'. */ +struct arglist_parser +{ + message_list_ty *mlp; /* list where the message shall be added */ + const char *keyword; /* the keyword, not NUL terminated */ + size_t keyword_len; /* the keyword's length */ + bool next_is_msgctxt; /* true if the next argument is the msgctxt */ + size_t nalternatives; /* number of partial_call alternatives */ + struct partial_call alternative[1]; /* partial_call alternatives */ +}; + +/* Creates a fresh arglist_parser recognizing calls. + You can pass shapes = NULL for a parser not recognizing any calls. */ +extern struct arglist_parser * arglist_parser_alloc (message_list_ty *mlp, + const struct callshapes *shapes); +/* Clones an arglist_parser. */ +extern struct arglist_parser * arglist_parser_clone (struct arglist_parser *ap); +/* Adds a string argument to an arglist_parser. ARGNUM must be > 0. + STRING must be malloc()ed string; its ownership is passed to the callee. + FILE_NAME must be allocated with indefinite extent. + COMMENT may be savable_comment, or it may be a saved copy of savable_comment + (then add_reference must be used when saving it, and drop_reference while + dropping it). Clear savable_comment. */ +extern void arglist_parser_remember (struct arglist_parser *ap, + int argnum, char *string, + flag_context_ty context, + char *file_name, size_t line_number, + refcounted_string_list_ty *comment); +/* Adds a string argument as msgctxt to an arglist_parser, without incrementing + the current argument number. + STRING must be malloc()ed string; its ownership is passed to the callee. + FILE_NAME must be allocated with indefinite extent. */ +extern void arglist_parser_remember_msgctxt (struct arglist_parser *ap, + char *string, + flag_context_ty context, + char *file_name, size_t line_number); +/* Tests whether an arglist_parser has is not waiting for more arguments after + argument ARGNUM. */ +extern bool arglist_parser_decidedp (struct arglist_parser *ap, int argnum); +/* Terminates the processing of an arglist_parser after argument ARGNUM and + deletes it. */ +extern void arglist_parser_done (struct arglist_parser *ap, int argnum); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _XGETTEXT_ARGLIST_PARSER_H */ diff --git a/gettext-tools/src/xg-encoding.c b/gettext-tools/src/xg-encoding.c new file mode 100644 index 000000000..1180157a7 --- /dev/null +++ b/gettext-tools/src/xg-encoding.c @@ -0,0 +1,136 @@ +/* Keeping track of the encoding of strings to be extracted. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "xg-encoding.h" + +#include +#include + +#include "msgl-ascii.h" +#include "msgl-iconv.h" +#include "po-charset.h" +#include "xalloc.h" +#include "xerror.h" +#include "xvasprintf.h" + +#include "gettext.h" +#define _(str) gettext (str) + + +/* Canonicalized encoding name for all input files. */ +const char *xgettext_global_source_encoding; + +#if HAVE_ICONV +/* Converter from xgettext_global_source_encoding to UTF-8 (except from + ASCII or UTF-8, when this conversion is a no-op). */ +iconv_t xgettext_global_source_iconv; +#endif + +/* Canonicalized encoding name for the current input file. */ +const char *xgettext_current_source_encoding; + +#if HAVE_ICONV +/* Converter from xgettext_current_source_encoding to UTF-8 (except from + ASCII or UTF-8, when this conversion is a no-op). */ +iconv_t xgettext_current_source_iconv; +#endif + + +/* Error message about non-ASCII character in a specific lexical context. */ +char * +non_ascii_error_message (lexical_context_ty lcontext, + const char *file_name, size_t line_number) +{ + char buffer[21]; + char *errmsg; + + if (line_number == (size_t)(-1)) + buffer[0] = '\0'; + else + sprintf (buffer, ":%ld", (long) line_number); + + switch (lcontext) + { + case lc_outside: + errmsg = + xasprintf (_("Non-ASCII character at %s%s."), file_name, buffer); + break; + case lc_comment: + errmsg = + xasprintf (_("Non-ASCII comment at or before %s%s."), + file_name, buffer); + break; + case lc_string: + errmsg = + xasprintf (_("Non-ASCII string at %s%s."), file_name, buffer); + break; + default: + abort (); + } + return errmsg; +} + +/* Convert the given string from xgettext_current_source_encoding to + the output file encoding (i.e. ASCII or UTF-8). + The resulting string is either the argument string, or freshly allocated. + The file_name and line_number are only used for error message purposes. */ +char * +from_current_source_encoding (const char *string, + lexical_context_ty lcontext, + const char *file_name, size_t line_number) +{ + if (xgettext_current_source_encoding == po_charset_ascii) + { + if (!is_ascii_string (string)) + { + multiline_error (xstrdup (""), + xasprintf ("%s\n%s\n", + non_ascii_error_message (lcontext, + file_name, + line_number), + _("\ +Please specify the source encoding through --from-code."))); + exit (EXIT_FAILURE); + } + } + else if (xgettext_current_source_encoding != po_charset_utf8) + { +#if HAVE_ICONV + struct conversion_context context; + + context.from_code = xgettext_current_source_encoding; + context.to_code = po_charset_utf8; + context.from_filename = file_name; + context.message = NULL; + + string = convert_string_directly (xgettext_current_source_iconv, string, + &context); +#else + /* If we don't have iconv(), the only supported values for + xgettext_global_source_encoding and thus also for + xgettext_current_source_encoding are ASCII and UTF-8. + convert_string_directly() should not be called in this case. */ + abort (); +#endif + } + + return (char *) string; +} diff --git a/gettext-tools/src/xg-encoding.h b/gettext-tools/src/xg-encoding.h new file mode 100644 index 000000000..b2b571cb1 --- /dev/null +++ b/gettext-tools/src/xg-encoding.h @@ -0,0 +1,86 @@ +/* Keeping track of the encoding of strings to be extracted. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _XGETTEXT_ENCODING_H +#define _XGETTEXT_ENCODING_H + +#include + +#if HAVE_ICONV +#include +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Context while building up lexical tokens. */ +typedef enum + { + lc_outside, /* Initial context: outside of comments and strings. */ + lc_comment, /* Inside a comment. */ + lc_string, /* Inside a string literal. */ + + /* For embedded XML in programming code, like E4X in JavaScript. */ + lc_xml_open_tag, /* Inside an opening tag of an XML element. */ + lc_xml_close_tag, /* Inside a closing tag of an XML element. */ + lc_xml_content /* Inside an XML text node. */ + } +lexical_context_ty; + +/* Error message about non-ASCII character in a specific lexical context. */ +extern char *non_ascii_error_message (lexical_context_ty lcontext, + const char *file_name, + size_t line_number); + + +/* Canonicalized encoding name for all input files. */ +extern const char *xgettext_global_source_encoding; + +#if HAVE_ICONV +/* Converter from xgettext_global_source_encoding to UTF-8 (except from + ASCII or UTF-8, when this conversion is a no-op). */ +extern iconv_t xgettext_global_source_iconv; +#endif + +/* Canonicalized encoding name for the current input file. */ +extern const char *xgettext_current_source_encoding; + +#if HAVE_ICONV +/* Converter from xgettext_current_source_encoding to UTF-8 (except from + ASCII or UTF-8, when this conversion is a no-op). */ +extern iconv_t xgettext_current_source_iconv; +#endif + +/* Convert the given string from xgettext_current_source_encoding to + the output file encoding (i.e. ASCII or UTF-8). + The resulting string is either the argument string, or freshly allocated. + The lcontext, file_name and line_number are only used for error message + purposes. */ +extern char *from_current_source_encoding (const char *string, + lexical_context_ty lcontext, + const char *file_name, + size_t line_number); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _XGETTEXT_ENCODING_H */ diff --git a/gettext-tools/src/xg-message.c b/gettext-tools/src/xg-message.c new file mode 100644 index 000000000..caf58cf4d --- /dev/null +++ b/gettext-tools/src/xg-message.c @@ -0,0 +1,592 @@ +/* Extracting a message. Accumulating the message list. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "xg-message.h" + +#include + +#include "c-strstr.h" +#include "error-progname.h" +#include "format.h" +#include "read-catalog-abstract.h" +#include "xalloc.h" +#include "xerror.h" +#include "xvasprintf.h" + +#include "xgettext.h" + +#include "gettext.h" +#define _(str) gettext (str) + + +#define CONVERT_STRING(string, lcontext) \ + string = from_current_source_encoding (string, lcontext, pos->file_name, \ + pos->line_number); + + +/* Update the is_format[] flags depending on the information given in the + context. */ +static void +set_format_flags_from_context (enum is_format is_format[NFORMATS], + flag_context_ty context, const char *string, + lex_pos_ty *pos, const char *pretty_msgstr) +{ + size_t i; + + if (context.is_format1 != undecided + || context.is_format2 != undecided + || context.is_format3 != undecided) + for (i = 0; i < NFORMATS; i++) + { + if (is_format[i] == undecided) + { + if (formatstring_parsers[i] == current_formatstring_parser1 + && context.is_format1 != undecided) + is_format[i] = (enum is_format) context.is_format1; + if (formatstring_parsers[i] == current_formatstring_parser2 + && context.is_format2 != undecided) + is_format[i] = (enum is_format) context.is_format2; + if (formatstring_parsers[i] == current_formatstring_parser3 + && context.is_format3 != undecided) + is_format[i] = (enum is_format) context.is_format3; + } + if (possible_format_p (is_format[i])) + { + struct formatstring_parser *parser = formatstring_parsers[i]; + char *invalid_reason = NULL; + void *descr = parser->parse (string, false, NULL, &invalid_reason); + + if (descr != NULL) + parser->free (descr); + else + { + /* The string is not a valid format string. */ + if (is_format[i] != possible) + { + char buffer[21]; + + error_with_progname = false; + if (pos->line_number == (size_t)(-1)) + buffer[0] = '\0'; + else + sprintf (buffer, ":%ld", (long) pos->line_number); + multiline_warning (xasprintf (_("%s%s: warning: "), + pos->file_name, buffer), + xasprintf (is_format[i] == yes_according_to_context + ? _("Although being used in a format string position, the %s is not a valid %s format string. Reason: %s\n") + : _("Although declared as such, the %s is not a valid %s format string. Reason: %s\n"), + pretty_msgstr, + format_language_pretty[i], + invalid_reason)); + error_with_progname = true; + } + + is_format[i] = impossible; + free (invalid_reason); + } + } + } +} + + +static void +warn_format_string (enum is_format is_format[NFORMATS], const char *string, + lex_pos_ty *pos, const char *pretty_msgstr) +{ + if (possible_format_p (is_format[format_python]) + && get_python_format_unnamed_arg_count (string) > 1) + { + char buffer[21]; + + error_with_progname = false; + if (pos->line_number == (size_t)(-1)) + buffer[0] = '\0'; + else + sprintf (buffer, ":%ld", (long) pos->line_number); + multiline_warning (xasprintf (_("%s%s: warning: "), + pos->file_name, buffer), + xasprintf (_("\ +'%s' format string with unnamed arguments cannot be properly localized:\n\ +The translator cannot reorder the arguments.\n\ +Please consider using a format string with named arguments,\n\ +and a mapping instead of a tuple for the arguments.\n"), + pretty_msgstr)); + error_with_progname = true; + } +} + + +message_ty * +remember_a_message (message_list_ty *mlp, char *msgctxt, char *msgid, + flag_context_ty context, lex_pos_ty *pos, + const char *extracted_comment, + refcounted_string_list_ty *comment) +{ + enum is_format is_format[NFORMATS]; + struct argument_range range; + enum is_wrap do_wrap; + enum is_syntax_check do_syntax_check[NSYNTAXCHECKS]; + message_ty *mp; + char *msgstr; + size_t i; + + /* See whether we shall exclude this message. */ + if (exclude != NULL && message_list_search (exclude, msgctxt, msgid) != NULL) + { + /* Tell the lexer to reset its comment buffer, so that the next + message gets the correct comments. */ + xgettext_comment_reset (); + savable_comment_reset (); + + if (msgctxt != NULL) + free (msgctxt); + free (msgid); + + return NULL; + } + + savable_comment_to_xgettext_comment (comment); + + for (i = 0; i < NFORMATS; i++) + is_format[i] = undecided; + range.min = -1; + range.max = -1; + do_wrap = undecided; + for (i = 0; i < NSYNTAXCHECKS; i++) + do_syntax_check[i] = undecided; + + if (msgctxt != NULL) + CONVERT_STRING (msgctxt, lc_string); + CONVERT_STRING (msgid, lc_string); + + if (msgctxt == NULL && msgid[0] == '\0' && !xgettext_omit_header) + { + char buffer[21]; + + error_with_progname = false; + if (pos->line_number == (size_t)(-1)) + buffer[0] = '\0'; + else + sprintf (buffer, ":%ld", (long) pos->line_number); + multiline_warning (xasprintf (_("%s%s: warning: "), pos->file_name, + buffer), + xstrdup (_("\ +Empty msgid. It is reserved by GNU gettext:\n\ +gettext(\"\") returns the header entry with\n\ +meta information, not the empty string.\n"))); + error_with_progname = true; + } + + /* See if we have seen this message before. */ + mp = message_list_search (mlp, msgctxt, msgid); + if (mp != NULL) + { + if (msgctxt != NULL) + free (msgctxt); + free (msgid); + for (i = 0; i < NFORMATS; i++) + is_format[i] = mp->is_format[i]; + do_wrap = mp->do_wrap; + for (i = 0; i < NSYNTAXCHECKS; i++) + do_syntax_check[i] = mp->do_syntax_check[i]; + } + else + { + /* Construct the msgstr from the prefix and suffix, otherwise use the + empty string. */ + if (msgstr_prefix) + msgstr = xasprintf ("%s%s%s", msgstr_prefix, msgid, msgstr_suffix); + else + msgstr = ""; + + /* Allocate a new message and append the message to the list. */ + mp = message_alloc (msgctxt, msgid, NULL, msgstr, strlen (msgstr) + 1, + pos); + /* Do not free msgctxt and msgid. */ + message_list_append (mlp, mp); + } + + /* Determine whether the context specifies that the msgid is a format + string. */ + set_format_flags_from_context (is_format, context, mp->msgid, pos, "msgid"); + + /* Ask the lexer for the comments it has seen. */ + { + size_t nitems_before; + size_t nitems_after; + int j; + bool add_all_remaining_comments; + /* The string before the comment tag. For example, If "** TRANSLATORS:" + is seen and the comment tag is "TRANSLATORS:", + then comment_tag_prefix is set to "** ". */ + const char *comment_tag_prefix = ""; + size_t comment_tag_prefix_length = 0; + + nitems_before = (mp->comment_dot != NULL ? mp->comment_dot->nitems : 0); + + if (extracted_comment != NULL) + { + char *copy = xstrdup (extracted_comment); + char *rest; + + rest = copy; + while (*rest != '\0') + { + char *newline = strchr (rest, '\n'); + + if (newline != NULL) + { + *newline = '\0'; + message_comment_dot_append (mp, rest); + rest = newline + 1; + } + else + { + message_comment_dot_append (mp, rest); + break; + } + } + free (copy); + } + + add_all_remaining_comments = add_all_comments; + for (j = 0; ; ++j) + { + const char *s = xgettext_comment (j); + const char *t; + if (s == NULL) + break; + + CONVERT_STRING (s, lc_comment); + + /* To reduce the possibility of unwanted matches we do a two + step match: the line must contain 'xgettext:' and one of + the possible format description strings. */ + if ((t = c_strstr (s, "xgettext:")) != NULL) + { + bool tmp_fuzzy; + enum is_format tmp_format[NFORMATS]; + struct argument_range tmp_range; + enum is_wrap tmp_wrap; + enum is_syntax_check tmp_syntax_check[NSYNTAXCHECKS]; + bool interesting; + + t += strlen ("xgettext:"); + + po_parse_comment_special (t, &tmp_fuzzy, tmp_format, &tmp_range, + &tmp_wrap, tmp_syntax_check); + + interesting = false; + for (i = 0; i < NFORMATS; i++) + if (tmp_format[i] != undecided) + { + is_format[i] = tmp_format[i]; + interesting = true; + } + if (has_range_p (tmp_range)) + { + range = tmp_range; + interesting = true; + } + if (tmp_wrap != undecided) + { + do_wrap = tmp_wrap; + interesting = true; + } + for (i = 0; i < NSYNTAXCHECKS; i++) + if (tmp_syntax_check[i] != undecided) + { + do_syntax_check[i] = tmp_syntax_check[i]; + interesting = true; + } + + /* If the "xgettext:" marker was followed by an interesting + keyword, and we updated our is_format/do_wrap variables, + we don't print the comment as a #. comment. */ + if (interesting) + continue; + } + + if (!add_all_remaining_comments && comment_tag != NULL) + { + /* When the comment tag is seen, it drags in not only the line + which it starts, but all remaining comment lines. */ + if ((t = c_strstr (s, comment_tag)) != NULL) + { + add_all_remaining_comments = true; + comment_tag_prefix = s; + comment_tag_prefix_length = t - s; + } + } + + if (add_all_remaining_comments) + { + if (strncmp (s, comment_tag_prefix, comment_tag_prefix_length) == 0) + s += comment_tag_prefix_length; + message_comment_dot_append (mp, s); + } + } + + nitems_after = (mp->comment_dot != NULL ? mp->comment_dot->nitems : 0); + + /* Don't add the comments if they are a repetition of the tail of the + already present comments. This avoids unneeded duplication if the + same message appears several times, each time with the same comment. */ + if (nitems_before < nitems_after) + { + size_t added = nitems_after - nitems_before; + + if (added <= nitems_before) + { + bool repeated = true; + + for (i = 0; i < added; i++) + if (strcmp (mp->comment_dot->item[nitems_before - added + i], + mp->comment_dot->item[nitems_before + i]) != 0) + { + repeated = false; + break; + } + + if (repeated) + { + for (i = 0; i < added; i++) + free ((char *) mp->comment_dot->item[nitems_before + i]); + mp->comment_dot->nitems = nitems_before; + } + } + } + } + + /* If it is not already decided, through programmer comments, whether the + msgid is a format string, examine the msgid. This is a heuristic. */ + for (i = 0; i < NFORMATS; i++) + { + if (is_format[i] == undecided + && (formatstring_parsers[i] == current_formatstring_parser1 + || formatstring_parsers[i] == current_formatstring_parser2 + || formatstring_parsers[i] == current_formatstring_parser3) + /* But avoid redundancy: objc-format is stronger than c-format. */ + && !(i == format_c && possible_format_p (is_format[format_objc])) + && !(i == format_objc && possible_format_p (is_format[format_c])) + /* Avoid flagging a string as c-format when it's known to be a + qt-format or qt-plural-format or kde-format or boost-format + string. */ + && !(i == format_c + && (possible_format_p (is_format[format_qt]) + || possible_format_p (is_format[format_qt_plural]) + || possible_format_p (is_format[format_kde]) + || possible_format_p (is_format[format_kde_kuit]) + || possible_format_p (is_format[format_boost]))) + /* Avoid flagging a string as kde-format when it's known to + be a kde-kuit-format string. */ + && !(i == format_kde + && possible_format_p (is_format[format_kde_kuit])) + /* Avoid flagging a string as kde-kuit-format when it's + known to be a kde-format string. Note that this relies + on the fact that format_kde < format_kde_kuit, so a + string will be marked as kde-format if both are + undecided. */ + && !(i == format_kde_kuit + && possible_format_p (is_format[format_kde]))) + { + struct formatstring_parser *parser = formatstring_parsers[i]; + char *invalid_reason = NULL; + void *descr = parser->parse (mp->msgid, false, NULL, &invalid_reason); + + if (descr != NULL) + { + /* msgid is a valid format string. We mark only those msgids + as format strings which contain at least one format directive + and thus are format strings with a high probability. We + don't mark strings without directives as format strings, + because that would force the programmer to add + "xgettext: no-c-format" anywhere where a translator wishes + to use a percent sign. So, the msgfmt checking will not be + perfect. Oh well. */ + if (parser->get_number_of_directives (descr) > 0 + && !(parser->is_unlikely_intentional != NULL + && parser->is_unlikely_intentional (descr))) + is_format[i] = possible; + + parser->free (descr); + } + else + { + /* msgid is not a valid format string. */ + is_format[i] = impossible; + free (invalid_reason); + } + } + mp->is_format[i] = is_format[i]; + } + + if (has_range_p (range)) + { + if (has_range_p (mp->range)) + { + if (range.min < mp->range.min) + mp->range.min = range.min; + if (range.max > mp->range.max) + mp->range.max = range.max; + } + else + mp->range = range; + } + + mp->do_wrap = do_wrap == no ? no : yes; /* By default we wrap. */ + + for (i = 0; i < NSYNTAXCHECKS; i++) + { + if (do_syntax_check[i] == undecided) + do_syntax_check[i] = default_syntax_check[i] == yes ? yes : no; + + mp->do_syntax_check[i] = do_syntax_check[i]; + } + + /* Warn about the use of non-reorderable format strings when the programming + language also provides reorderable format strings. */ + warn_format_string (is_format, mp->msgid, pos, "msgid"); + + /* Remember where we saw this msgid. */ + message_comment_filepos (mp, pos->file_name, pos->line_number); + + /* Tell the lexer to reset its comment buffer, so that the next + message gets the correct comments. */ + xgettext_comment_reset (); + savable_comment_reset (); + + return mp; +} + + +void +remember_a_message_plural (message_ty *mp, char *string, + flag_context_ty context, lex_pos_ty *pos, + refcounted_string_list_ty *comment) +{ + char *msgid_plural; + char *msgstr1; + size_t msgstr1_len; + char *msgstr; + size_t i; + + msgid_plural = string; + + savable_comment_to_xgettext_comment (comment); + + CONVERT_STRING (msgid_plural, lc_string); + + /* See if the message is already a plural message. */ + if (mp->msgid_plural == NULL) + { + mp->msgid_plural = msgid_plural; + + /* Construct the first plural form from the prefix and suffix, + otherwise use the empty string. The translator will have to + provide additional plural forms. */ + if (msgstr_prefix) + msgstr1 = + xasprintf ("%s%s%s", msgstr_prefix, msgid_plural, msgstr_suffix); + else + msgstr1 = ""; + msgstr1_len = strlen (msgstr1) + 1; + msgstr = XNMALLOC (mp->msgstr_len + msgstr1_len, char); + memcpy (msgstr, mp->msgstr, mp->msgstr_len); + memcpy (msgstr + mp->msgstr_len, msgstr1, msgstr1_len); + mp->msgstr = msgstr; + mp->msgstr_len = mp->msgstr_len + msgstr1_len; + if (msgstr_prefix) + free (msgstr1); + + /* Determine whether the context specifies that the msgid_plural is a + format string. */ + set_format_flags_from_context (mp->is_format, context, mp->msgid_plural, + pos, "msgid_plural"); + + /* If it is not already decided, through programmer comments or + the msgid, whether the msgid is a format string, examine the + msgid_plural. This is a heuristic. */ + for (i = 0; i < NFORMATS; i++) + if ((formatstring_parsers[i] == current_formatstring_parser1 + || formatstring_parsers[i] == current_formatstring_parser2 + || formatstring_parsers[i] == current_formatstring_parser3) + && (mp->is_format[i] == undecided || mp->is_format[i] == possible) + /* But avoid redundancy: objc-format is stronger than c-format. */ + && !(i == format_c + && possible_format_p (mp->is_format[format_objc])) + && !(i == format_objc + && possible_format_p (mp->is_format[format_c])) + /* Avoid flagging a string as c-format when it's known to be a + qt-format or qt-plural-format or boost-format string. */ + && !(i == format_c + && (possible_format_p (mp->is_format[format_qt]) + || possible_format_p (mp->is_format[format_qt_plural]) + || possible_format_p (mp->is_format[format_kde]) + || possible_format_p (mp->is_format[format_kde_kuit]) + || possible_format_p (mp->is_format[format_boost]))) + /* Avoid flagging a string as kde-format when it's known + to be a kde-kuit-format string. */ + && !(i == format_kde + && possible_format_p (mp->is_format[format_kde_kuit])) + /* Avoid flagging a string as kde-kuit-format when it's + known to be a kde-format string. Note that this relies + on the fact that format_kde < format_kde_kuit, so a + string will be marked as kde-format if both are + undecided. */ + && !(i == format_kde_kuit + && possible_format_p (mp->is_format[format_kde]))) + { + struct formatstring_parser *parser = formatstring_parsers[i]; + char *invalid_reason = NULL; + void *descr = + parser->parse (mp->msgid_plural, false, NULL, &invalid_reason); + + if (descr != NULL) + { + /* Same heuristic as in remember_a_message. */ + if (parser->get_number_of_directives (descr) > 0 + && !(parser->is_unlikely_intentional != NULL + && parser->is_unlikely_intentional (descr))) + mp->is_format[i] = possible; + + parser->free (descr); + } + else + { + /* msgid_plural is not a valid format string. */ + mp->is_format[i] = impossible; + free (invalid_reason); + } + } + + /* Warn about the use of non-reorderable format strings when the programming + language also provides reorderable format strings. */ + warn_format_string (mp->is_format, mp->msgid_plural, pos, "msgid_plural"); + } + else + free (msgid_plural); + + /* Tell the lexer to reset its comment buffer, so that the next + message gets the correct comments. */ + xgettext_comment_reset (); + savable_comment_reset (); +} diff --git a/gettext-tools/src/xg-message.h b/gettext-tools/src/xg-message.h new file mode 100644 index 000000000..621cbbca1 --- /dev/null +++ b/gettext-tools/src/xg-message.h @@ -0,0 +1,70 @@ +/* Extracting a message. Accumulating the message list. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _XGETTEXT_MESSAGE_H +#define _XGETTEXT_MESSAGE_H + +#include "message.h" +#include "pos.h" +#include "rc-str-list.h" + +#include "xg-arglist-context.h" +#include "xg-encoding.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Add a message to the list of extracted messages. + msgctxt must be either NULL or a malloc()ed string; its ownership is passed + to the callee. + MSGID must be a malloc()ed string; its ownership is passed to the callee. + POS->file_name must be allocated with indefinite extent. + EXTRACTED_COMMENT is a comment that needs to be copied into the POT file, + or NULL. + COMMENT may be savable_comment, or it may be a saved copy of savable_comment + (then add_reference must be used when saving it, and drop_reference while + dropping it). Clear savable_comment. + Return the new or found message, or NULL if the message is excluded. */ +extern message_ty *remember_a_message (message_list_ty *mlp, + char *msgctxt, + char *msgid, + flag_context_ty context, + lex_pos_ty *pos, + const char *extracted_comment, + refcounted_string_list_ty *comment); + +/* Add an msgid_plural to a message previously returned by + remember_a_message. + STRING must be a malloc()ed string; its ownership is passed to the callee. + POS->file_name must be allocated with indefinite extent. + COMMENT may be savable_comment, or it may be a saved copy of savable_comment + (then add_reference must be used when saving it, and drop_reference while + dropping it). Clear savable_comment. */ +extern void remember_a_message_plural (message_ty *mp, + char *string, + flag_context_ty context, + lex_pos_ty *pos, + refcounted_string_list_ty *comment); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _XGETTEXT_MESSAGE_H */ diff --git a/gettext-tools/src/xg-mixed-string.c b/gettext-tools/src/xg-mixed-string.c new file mode 100644 index 000000000..9a4485617 --- /dev/null +++ b/gettext-tools/src/xg-mixed-string.c @@ -0,0 +1,209 @@ +/* Handling strings that are given partially in the source encoding and + partially in Unicode. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "xg-mixed-string.h" + +#include +#include + +#include "unistr.h" +#include "xalloc.h" + + +struct mixed_string_buffer * +mixed_string_buffer_alloc (lexical_context_ty lcontext, + const char *logical_file_name, + int line_number) +{ + struct mixed_string_buffer *bp = XMALLOC (struct mixed_string_buffer); + bp->utf8_buffer = NULL; + bp->utf8_buflen = 0; + bp->utf8_allocated = 0; + bp->utf16_surr = 0; + bp->curr_buffer = NULL; + bp->curr_buflen = 0; + bp->curr_allocated = 0; + bp->lcontext = lcontext; + bp->logical_file_name = logical_file_name; + bp->line_number = line_number; + return bp; +} + +/* Auxiliary function: Append a byte to bp->curr. */ +static inline void +mixed_string_buffer_append_to_curr_buffer (struct mixed_string_buffer *bp, + unsigned char c) +{ + if (bp->curr_buflen == bp->curr_allocated) + { + bp->curr_allocated = 2 * bp->curr_allocated + 10; + bp->curr_buffer = xrealloc (bp->curr_buffer, bp->curr_allocated); + } + bp->curr_buffer[bp->curr_buflen++] = c; +} + +/* Auxiliary function: Ensure count more bytes are available in bp->utf8. */ +static inline void +mixed_string_buffer_grow_utf8_buffer (struct mixed_string_buffer *bp, + size_t count) +{ + if (bp->utf8_buflen + count > bp->utf8_allocated) + { + size_t new_allocated = 2 * bp->utf8_allocated + 10; + if (new_allocated < bp->utf8_buflen + count) + new_allocated = bp->utf8_buflen + count; + bp->utf8_allocated = new_allocated; + bp->utf8_buffer = xrealloc (bp->utf8_buffer, new_allocated); + } +} + +/* Auxiliary function: Append a Unicode character to bp->utf8. + uc must be < 0x110000. */ +static inline void +mixed_string_buffer_append_to_utf8_buffer (struct mixed_string_buffer *bp, + ucs4_t uc) +{ + unsigned char utf8buf[6]; + int count = u8_uctomb (utf8buf, uc, 6); + + if (count < 0) + /* The caller should have ensured that uc is not out-of-range. */ + abort (); + + mixed_string_buffer_grow_utf8_buffer (bp, count); + memcpy (bp->utf8_buffer + bp->utf8_buflen, utf8buf, count); + bp->utf8_buflen += count; +} + +/* Auxiliary function: Flush bp->utf16_surr into bp->utf8_buffer. */ +static inline void +mixed_string_buffer_flush_utf16_surr (struct mixed_string_buffer *bp) +{ + if (bp->utf16_surr != 0) + { + /* A half surrogate is invalid, therefore use U+FFFD instead. */ + mixed_string_buffer_append_to_utf8_buffer (bp, 0xfffd); + bp->utf16_surr = 0; + } +} + +/* Auxiliary function: Flush bp->curr_buffer into bp->utf8_buffer. */ +static inline void +mixed_string_buffer_flush_curr_buffer (struct mixed_string_buffer *bp, + int line_number) +{ + if (bp->curr_buflen > 0) + { + char *curr; + size_t count; + + mixed_string_buffer_append_to_curr_buffer (bp, '\0'); + + /* Convert from the source encoding to UTF-8. */ + curr = from_current_source_encoding (bp->curr_buffer, bp->lcontext, + bp->logical_file_name, + line_number); + + /* Append it to bp->utf8_buffer. */ + count = strlen (curr); + mixed_string_buffer_grow_utf8_buffer (bp, count); + memcpy (bp->utf8_buffer + bp->utf8_buflen, curr, count); + bp->utf8_buflen += count; + + if (curr != bp->curr_buffer) + free (curr); + bp->curr_buflen = 0; + } +} + +void +mixed_string_buffer_append_char (struct mixed_string_buffer *bp, int c) +{ + /* Switch from Unicode character mode to multibyte character mode. */ + mixed_string_buffer_flush_utf16_surr (bp); + + /* When a newline is seen, convert the accumulated multibyte sequence. + This ensures a correct line number in the error message in case of + a conversion error. The "- 1" is to account for the newline. */ + if (c == '\n') + mixed_string_buffer_flush_curr_buffer (bp, bp->line_number - 1); + + mixed_string_buffer_append_to_curr_buffer (bp, (unsigned char) c); +} + +void +mixed_string_buffer_append_unicode (struct mixed_string_buffer *bp, int c) +{ + /* Switch from multibyte character mode to Unicode character mode. */ + mixed_string_buffer_flush_curr_buffer (bp, bp->line_number); + + /* Test whether this character and the previous one form a Unicode + surrogate character pair. */ + if (bp->utf16_surr != 0 && (c >= 0xdc00 && c < 0xe000)) + { + unsigned short utf16buf[2]; + ucs4_t uc; + + utf16buf[0] = bp->utf16_surr; + utf16buf[1] = c; + if (u16_mbtouc (&uc, utf16buf, 2) != 2) + abort (); + + mixed_string_buffer_append_to_utf8_buffer (bp, uc); + bp->utf16_surr = 0; + } + else + { + mixed_string_buffer_flush_utf16_surr (bp); + + if (c >= 0xd800 && c < 0xdc00) + bp->utf16_surr = c; + else if (c >= 0xdc00 && c < 0xe000) + { + /* A half surrogate is invalid, therefore use U+FFFD instead. */ + mixed_string_buffer_append_to_utf8_buffer (bp, 0xfffd); + } + else + mixed_string_buffer_append_to_utf8_buffer (bp, c); + } +} + +char * +mixed_string_buffer_done (struct mixed_string_buffer *bp) +{ + char *utf8_buffer; + + /* Flush all into bp->utf8_buffer. */ + mixed_string_buffer_flush_utf16_surr (bp); + mixed_string_buffer_flush_curr_buffer (bp, bp->line_number); + /* NUL-terminate it. */ + mixed_string_buffer_grow_utf8_buffer (bp, 1); + bp->utf8_buffer[bp->utf8_buflen] = '\0'; + + /* Free curr_buffer and bp itself. */ + utf8_buffer = bp->utf8_buffer; + free (bp->curr_buffer); + free (bp); + + /* Return it. */ + return utf8_buffer; +} diff --git a/gettext-tools/src/xg-mixed-string.h b/gettext-tools/src/xg-mixed-string.h new file mode 100644 index 000000000..9e82bbf26 --- /dev/null +++ b/gettext-tools/src/xg-mixed-string.h @@ -0,0 +1,75 @@ +/* Handling strings that are given partially in the source encoding and + partially in Unicode. + Copyright (C) 2001-2018 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _XGETTEXT_MIXED_STRING_H +#define _XGETTEXT_MIXED_STRING_H + +#include + +#include "xg-encoding.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* A string buffer type that allows appending bytes (in the + xgettext_current_source_encoding) or Unicode characters. + Returns the entire string in UTF-8 encoding. */ + +struct mixed_string_buffer +{ + /* The part of the string that has already been converted to UTF-8. */ + char *utf8_buffer; + size_t utf8_buflen; + size_t utf8_allocated; + /* The first half of an UTF-16 surrogate character. */ + unsigned short utf16_surr; + /* The part of the string that is still in the source encoding. */ + char *curr_buffer; + size_t curr_buflen; + size_t curr_allocated; + /* The lexical context. Used only for error message purposes. */ + lexical_context_ty lcontext; + const char *logical_file_name; + int line_number; +}; + +/* Creates a fresh mixed_string_buffer. */ +extern struct mixed_string_buffer * + mixed_string_buffer_alloc (lexical_context_ty lcontext, + const char *logical_file_name, + int line_number); + +/* Appends a character to a mixed_string_buffer. */ +extern void mixed_string_buffer_append_char (struct mixed_string_buffer *bp, + int c); + +/* Appends a Unicode character to a mixed_string_buffer. */ +extern void mixed_string_buffer_append_unicode (struct mixed_string_buffer *bp, + int c); + +/* Frees mixed_string_buffer and returns the accumulated string in UTF-8. */ +extern char * mixed_string_buffer_done (struct mixed_string_buffer *bp); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _XGETTEXT_MIXED_STRING_H */ diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c index adec0dff7..9c9c03841 100644 --- a/gettext-tools/src/xgettext.c +++ b/gettext-tools/src/xgettext.c @@ -20,6 +20,9 @@ #endif #include +/* Specification. */ +#include "xgettext.h" + #include #include #include @@ -32,7 +35,14 @@ #include #include -#include "xgettext.h" +#if HAVE_ICONV +#include +#endif + +#include "rc-str-list.h" +#include "xg-encoding.h" +#include "xg-arglist-context.h" +#include "xg-message.h" #include "closeout.h" #include "dir-list.h" #include "file-list.h" @@ -44,7 +54,6 @@ #include "basename.h" #include "xerror.h" #include "xvasprintf.h" -#include "xsize.h" #include "xalloc.h" #include "xmalloca.h" #include "c-strstr.h" @@ -60,7 +69,6 @@ #include "msgl-iconv.h" #include "msgl-ascii.h" #include "msgl-check.h" -#include "po-xerror.h" #include "po-time.h" #include "write-catalog.h" #include "write-po.h" @@ -70,7 +78,6 @@ #include "format.h" #include "propername.h" #include "sentence.h" -#include "unistr.h" #include "its.h" #include "locating-rule.h" #include "search-path.h" @@ -112,11 +119,11 @@ #define ENDOF(a) ((a) + SIZEOF(a)) -/* If nonzero add all comments immediately preceding one of the keywords. */ -static bool add_all_comments = false; +/* If true, add all comments immediately preceding one of the keywords. */ +bool add_all_comments = false; /* Tag used in comment of prevailing domain. */ -static char *comment_tag; +char *comment_tag; /* Name of default domain file. If not set defaults to messages.po. */ static const char *default_domain; @@ -144,10 +151,10 @@ static const char *package_version = NULL; static const char *msgid_bugs_address = NULL; /* String used as prefix for msgstr. */ -static const char *msgstr_prefix; +const char *msgstr_prefix; /* String used as suffix for msgstr. */ -static const char *msgstr_suffix; +const char *msgstr_suffix; /* Directory in which output files are created. */ static char *output_dir; @@ -192,25 +199,7 @@ static bool recognize_format_kde; static bool recognize_format_boost; /* Syntax checks enabled by default. */ -static enum is_syntax_check default_syntax_check[NSYNTAXCHECKS]; - -/* Canonicalized encoding name for all input files. */ -const char *xgettext_global_source_encoding; - -#if HAVE_ICONV -/* Converter from xgettext_global_source_encoding to UTF-8 (except from - ASCII or UTF-8, when this conversion is a no-op). */ -iconv_t xgettext_global_source_iconv; -#endif - -/* Canonicalized encoding name for the current input file. */ -const char *xgettext_current_source_encoding; - -#if HAVE_ICONV -/* Converter from xgettext_current_source_encoding to UTF-8 (except from - ASCII or UTF-8, when this conversion is a no-op). */ -iconv_t xgettext_current_source_iconv; -#endif +enum is_syntax_check default_syntax_check[NSYNTAXCHECKS]; static locating_rule_list_ty *its_locating_rules; @@ -1316,315 +1305,6 @@ read_exclusion_file (char *filename) } -void -split_keywordspec (const char *spec, - const char **endp, struct callshape *shapep) -{ - const char *p; - int argnum1 = 0; - int argnum2 = 0; - int argnumc = 0; - bool argnum1_glib_context = false; - bool argnum2_glib_context = false; - int argtotal = 0; - string_list_ty xcomments; - - string_list_init (&xcomments); - - /* Start parsing from the end. */ - p = spec + strlen (spec); - while (p > spec) - { - if (isdigit ((unsigned char) p[-1]) - || ((p[-1] == 'c' || p[-1] == 'g' || p[-1] == 't') - && p - 1 > spec && isdigit ((unsigned char) p[-2]))) - { - bool contextp = (p[-1] == 'c'); - bool glibp = (p[-1] == 'g'); - bool totalp = (p[-1] == 't'); - - do - p--; - while (p > spec && isdigit ((unsigned char) p[-1])); - - if (p > spec && (p[-1] == ',' || p[-1] == ':')) - { - char *dummy; - int arg = strtol (p, &dummy, 10); - - if (contextp) - { - if (argnumc != 0) - /* Only one context argument can be given. */ - break; - argnumc = arg; - } - else if (totalp) - { - if (argtotal != 0) - /* Only one total number of arguments can be given. */ - break; - argtotal = arg; - } - else - { - if (argnum2 != 0) - /* At most two normal arguments can be given. */ - break; - argnum2 = argnum1; - argnum2_glib_context = argnum1_glib_context; - argnum1 = arg; - argnum1_glib_context = glibp; - } - } - else - break; - } - else if (p[-1] == '"') - { - const char *xcomment_end; - - p--; - xcomment_end = p; - - while (p > spec && p[-1] != '"') - p--; - - if (p > spec /* && p[-1] == '"' */) - { - const char *xcomment_start; - - xcomment_start = p; - p--; - if (p > spec && (p[-1] == ',' || p[-1] == ':')) - { - size_t xcomment_len = xcomment_end - xcomment_start; - char *xcomment = XNMALLOC (xcomment_len + 1, char); - - memcpy (xcomment, xcomment_start, xcomment_len); - xcomment[xcomment_len] = '\0'; - string_list_append (&xcomments, xcomment); - } - else - break; - } - else - break; - } - else - break; - - /* Here an element of the comma-separated list has been parsed. */ - if (!(p > spec && (p[-1] == ',' || p[-1] == ':'))) - abort (); - p--; - if (*p == ':') - { - size_t i; - - if (argnum1 == 0 && argnum2 == 0) - /* At least one non-context argument must be given. */ - break; - if (argnumc != 0 - && (argnum1_glib_context || argnum2_glib_context)) - /* Incompatible ways to specify the context. */ - break; - *endp = p; - shapep->argnum1 = (argnum1 > 0 ? argnum1 : 1); - shapep->argnum2 = argnum2; - shapep->argnumc = argnumc; - shapep->argnum1_glib_context = argnum1_glib_context; - shapep->argnum2_glib_context = argnum2_glib_context; - shapep->argtotal = argtotal; - /* Reverse the order of the xcomments. */ - string_list_init (&shapep->xcomments); - for (i = xcomments.nitems; i > 0; ) - string_list_append (&shapep->xcomments, xcomments.item[--i]); - string_list_destroy (&xcomments); - return; - } - } - - /* Couldn't parse the desired syntax. */ - *endp = spec + strlen (spec); - shapep->argnum1 = 1; - shapep->argnum2 = 0; - shapep->argnumc = 0; - shapep->argnum1_glib_context = false; - shapep->argnum2_glib_context = false; - shapep->argtotal = 0; - string_list_init (&shapep->xcomments); - string_list_destroy (&xcomments); -} - - -void -insert_keyword_callshape (hash_table *table, - const char *keyword, size_t keyword_len, - const struct callshape *shape) -{ - void *old_value; - - if (hash_find_entry (table, keyword, keyword_len, &old_value)) - { - /* Create a one-element 'struct callshapes'. */ - struct callshapes *shapes = XMALLOC (struct callshapes); - shapes->nshapes = 1; - shapes->shapes[0] = *shape; - keyword = - (const char *) hash_insert_entry (table, keyword, keyword_len, shapes); - if (keyword == NULL) - abort (); - shapes->keyword = keyword; - shapes->keyword_len = keyword_len; - } - else - { - /* Found a 'struct callshapes'. See whether it already contains the - desired shape. */ - struct callshapes *old_shapes = (struct callshapes *) old_value; - bool found; - size_t i; - - found = false; - for (i = 0; i < old_shapes->nshapes; i++) - if (old_shapes->shapes[i].argnum1 == shape->argnum1 - && old_shapes->shapes[i].argnum2 == shape->argnum2 - && old_shapes->shapes[i].argnumc == shape->argnumc - && old_shapes->shapes[i].argnum1_glib_context - == shape->argnum1_glib_context - && old_shapes->shapes[i].argnum2_glib_context - == shape->argnum2_glib_context - && old_shapes->shapes[i].argtotal == shape->argtotal) - { - old_shapes->shapes[i].xcomments = shape->xcomments; - found = true; - break; - } - - if (!found) - { - /* Replace the existing 'struct callshapes' with a new one. */ - struct callshapes *shapes = - (struct callshapes *) - xmalloc (xsum (sizeof (struct callshapes), - xtimes (old_shapes->nshapes, - sizeof (struct callshape)))); - - shapes->keyword = old_shapes->keyword; - shapes->keyword_len = old_shapes->keyword_len; - shapes->nshapes = old_shapes->nshapes + 1; - for (i = 0; i < old_shapes->nshapes; i++) - shapes->shapes[i] = old_shapes->shapes[i]; - shapes->shapes[i] = *shape; - if (hash_set_value (table, keyword, keyword_len, shapes)) - abort (); - free (old_shapes); - } - } -} - - -/* Null context. */ -flag_context_ty null_context = { undecided, false, undecided, false }; - -/* Transparent context. */ -flag_context_ty passthrough_context = { undecided, true, undecided, true }; - - -flag_context_ty -inherited_context (flag_context_ty outer_context, - flag_context_ty modifier_context) -{ - flag_context_ty result = modifier_context; - - if (result.pass_format1) - { - result.is_format1 = outer_context.is_format1; - result.pass_format1 = false; - } - if (result.pass_format2) - { - result.is_format2 = outer_context.is_format2; - result.pass_format2 = false; - } - if (result.pass_format3) - { - result.is_format3 = outer_context.is_format3; - result.pass_format3 = false; - } - return result; -} - - -/* Null context list iterator. */ -flag_context_list_iterator_ty null_context_list_iterator = { 1, NULL }; - -/* Transparent context list iterator. */ -static flag_context_list_ty passthrough_context_circular_list = - { - 1, - { undecided, true, undecided, true }, - &passthrough_context_circular_list - }; -flag_context_list_iterator_ty passthrough_context_list_iterator = - { - 1, - &passthrough_context_circular_list - }; - - -flag_context_list_iterator_ty -flag_context_list_iterator (flag_context_list_ty *list) -{ - flag_context_list_iterator_ty result; - - result.argnum = 1; - result.head = list; - return result; -} - - -flag_context_ty -flag_context_list_iterator_advance (flag_context_list_iterator_ty *iter) -{ - if (iter->head == NULL) - return null_context; - if (iter->argnum == iter->head->argnum) - { - flag_context_ty result = iter->head->flags; - - /* Special casing of circular list. */ - if (iter->head != iter->head->next) - { - iter->head = iter->head->next; - iter->argnum++; - } - - return result; - } - else - { - iter->argnum++; - return null_context; - } -} - - -flag_context_list_ty * -flag_context_list_table_lookup (flag_context_list_table_ty *flag_table, - const void *key, size_t keylen) -{ - void *entry; - - if (flag_table->table != NULL - && hash_find_entry (flag_table, key, keylen, &entry) == 0) - return (flag_context_list_ty *) entry; - else - return NULL; -} - - static void flag_context_list_table_insert (flag_context_list_table_ty *table, unsigned int index, @@ -1655,135 +1335,13 @@ flag_context_list_table_insert (flag_context_list_table_ty *table, name_start += 2; } - /* Insert the pair (VALUE, PASS) at INDEX in the element numbered ARGNUM - of the list corresponding to NAME in the TABLE. */ - if (table->table == NULL) - hash_init (table, 100); - { - void *entry; - - if (hash_find_entry (table, name_start, name_end - name_start, &entry) != 0) - { - /* Create new hash table entry. */ - flag_context_list_ty *list = XMALLOC (flag_context_list_ty); - list->argnum = argnum; - memset (&list->flags, '\0', sizeof (list->flags)); - switch (index) - { - case 0: - list->flags.is_format1 = value; - list->flags.pass_format1 = pass; - break; - case 1: - list->flags.is_format2 = value; - list->flags.pass_format2 = pass; - break; - case 2: - list->flags.is_format3 = value; - list->flags.pass_format3 = pass; - break; - default: - abort (); - } - list->next = NULL; - hash_insert_entry (table, name_start, name_end - name_start, list); - } - else - { - flag_context_list_ty *list = (flag_context_list_ty *)entry; - flag_context_list_ty **lastp = NULL; - /* Invariant: list == (lastp != NULL ? *lastp : entry). */ - - while (list != NULL && list->argnum < argnum) - { - lastp = &list->next; - list = *lastp; - } - if (list != NULL && list->argnum == argnum) - { - /* Add this flag to the current argument number. */ - switch (index) - { - case 0: - list->flags.is_format1 = value; - list->flags.pass_format1 = pass; - break; - case 1: - list->flags.is_format2 = value; - list->flags.pass_format2 = pass; - break; - case 2: - list->flags.is_format3 = value; - list->flags.pass_format3 = pass; - break; - default: - abort (); - } - } - else if (lastp != NULL) - { - /* Add a new list entry for this argument number. */ - list = XMALLOC (flag_context_list_ty); - list->argnum = argnum; - memset (&list->flags, '\0', sizeof (list->flags)); - switch (index) - { - case 0: - list->flags.is_format1 = value; - list->flags.pass_format1 = pass; - break; - case 1: - list->flags.is_format2 = value; - list->flags.pass_format2 = pass; - break; - case 2: - list->flags.is_format3 = value; - list->flags.pass_format3 = pass; - break; - default: - abort (); - } - list->next = *lastp; - *lastp = list; - } - else - { - /* Add a new list entry for this argument number, at the beginning - of the list. Since we don't have an API for replacing the - value of a key in the hash table, we have to copy the first - list element. */ - flag_context_list_ty *copy = XMALLOC (flag_context_list_ty); - *copy = *list; - - list->argnum = argnum; - memset (&list->flags, '\0', sizeof (list->flags)); - switch (index) - { - case 0: - list->flags.is_format1 = value; - list->flags.pass_format1 = pass; - break; - case 1: - list->flags.is_format2 = value; - list->flags.pass_format2 = pass; - break; - case 2: - list->flags.is_format3 = value; - list->flags.pass_format3 = pass; - break; - default: - abort (); - } - list->next = copy; - } - } - } + flag_context_list_table_add (table, index, name_start, name_end, + argnum, value, pass); if (allocated_name != NULL) freea (allocated_name); } - void xgettext_record_flag (const char *optionstring) { @@ -2058,7 +1616,7 @@ xgettext_comment_add (const char *str) string_list_append (comment, str); } -static const char * +const char * xgettext_comment (size_t n) { if (comment == NULL || n >= comment->nitems) @@ -2066,8 +1624,8 @@ xgettext_comment (size_t n) return comment->item[n]; } -static void -xgettext_comment_reset () +void +xgettext_comment_reset (void) { if (comment != NULL) { @@ -2113,7 +1671,7 @@ savable_comment_reset () savable_comment = NULL; } -static void +void savable_comment_to_xgettext_comment (refcounted_string_list_ty *rslp) { xgettext_comment_reset (); @@ -2191,9 +1749,9 @@ error while opening \"%s\" for reading"), new_name); /* Language dependent format string parser. NULL if the language has no notion of format strings. */ -static struct formatstring_parser *current_formatstring_parser1; -static struct formatstring_parser *current_formatstring_parser2; -static struct formatstring_parser *current_formatstring_parser3; +struct formatstring_parser *current_formatstring_parser1; +struct formatstring_parser *current_formatstring_parser2; +struct formatstring_parser *current_formatstring_parser3; static void @@ -2283,1323 +1841,11 @@ extract_from_xml_file (const char *file_name, } - -/* Error message about non-ASCII character in a specific lexical context. */ -char * -non_ascii_error_message (lexical_context_ty lcontext, - const char *file_name, size_t line_number) -{ - char buffer[21]; - char *errmsg; - - if (line_number == (size_t)(-1)) - buffer[0] = '\0'; - else - sprintf (buffer, ":%ld", (long) line_number); - - switch (lcontext) - { - case lc_outside: - errmsg = - xasprintf (_("Non-ASCII character at %s%s."), file_name, buffer); - break; - case lc_comment: - errmsg = - xasprintf (_("Non-ASCII comment at or before %s%s."), - file_name, buffer); - break; - case lc_string: - errmsg = - xasprintf (_("Non-ASCII string at %s%s."), file_name, buffer); - break; - default: - abort (); - } - return errmsg; -} - -/* Convert the given string from xgettext_current_source_encoding to - the output file encoding (i.e. ASCII or UTF-8). - The resulting string is either the argument string, or freshly allocated. - The file_name and line_number are only used for error message purposes. */ -char * -from_current_source_encoding (const char *string, - lexical_context_ty lcontext, - const char *file_name, size_t line_number) -{ - if (xgettext_current_source_encoding == po_charset_ascii) - { - if (!is_ascii_string (string)) - { - multiline_error (xstrdup (""), - xasprintf ("%s\n%s\n", - non_ascii_error_message (lcontext, - file_name, - line_number), - _("\ -Please specify the source encoding through --from-code."))); - exit (EXIT_FAILURE); - } - } - else if (xgettext_current_source_encoding != po_charset_utf8) - { -#if HAVE_ICONV - struct conversion_context context; - - context.from_code = xgettext_current_source_encoding; - context.to_code = po_charset_utf8; - context.from_filename = file_name; - context.message = NULL; - - string = convert_string_directly (xgettext_current_source_iconv, string, - &context); -#else - /* If we don't have iconv(), the only supported values for - xgettext_global_source_encoding and thus also for - xgettext_current_source_encoding are ASCII and UTF-8. - convert_string_directly() should not be called in this case. */ - abort (); -#endif - } - - return (char *) string; -} - -#define CONVERT_STRING(string, lcontext) \ - string = from_current_source_encoding (string, lcontext, pos->file_name, \ - pos->line_number); - - -/* Update the is_format[] flags depending on the information given in the - context. */ -static void -set_format_flags_from_context (enum is_format is_format[NFORMATS], - flag_context_ty context, const char *string, - lex_pos_ty *pos, const char *pretty_msgstr) -{ - size_t i; - - if (context.is_format1 != undecided - || context.is_format2 != undecided - || context.is_format3 != undecided) - for (i = 0; i < NFORMATS; i++) - { - if (is_format[i] == undecided) - { - if (formatstring_parsers[i] == current_formatstring_parser1 - && context.is_format1 != undecided) - is_format[i] = (enum is_format) context.is_format1; - if (formatstring_parsers[i] == current_formatstring_parser2 - && context.is_format2 != undecided) - is_format[i] = (enum is_format) context.is_format2; - if (formatstring_parsers[i] == current_formatstring_parser3 - && context.is_format3 != undecided) - is_format[i] = (enum is_format) context.is_format3; - } - if (possible_format_p (is_format[i])) - { - struct formatstring_parser *parser = formatstring_parsers[i]; - char *invalid_reason = NULL; - void *descr = parser->parse (string, false, NULL, &invalid_reason); - - if (descr != NULL) - parser->free (descr); - else - { - /* The string is not a valid format string. */ - if (is_format[i] != possible) - { - char buffer[21]; - - error_with_progname = false; - if (pos->line_number == (size_t)(-1)) - buffer[0] = '\0'; - else - sprintf (buffer, ":%ld", (long) pos->line_number); - multiline_warning (xasprintf (_("%s%s: warning: "), - pos->file_name, buffer), - xasprintf (is_format[i] == yes_according_to_context - ? _("Although being used in a format string position, the %s is not a valid %s format string. Reason: %s\n") - : _("Although declared as such, the %s is not a valid %s format string. Reason: %s\n"), - pretty_msgstr, - format_language_pretty[i], - invalid_reason)); - error_with_progname = true; - } - - is_format[i] = impossible; - free (invalid_reason); - } - } - } -} - - -static void -warn_format_string (enum is_format is_format[NFORMATS], const char *string, - lex_pos_ty *pos, const char *pretty_msgstr) -{ - if (possible_format_p (is_format[format_python]) - && get_python_format_unnamed_arg_count (string) > 1) - { - char buffer[21]; - - error_with_progname = false; - if (pos->line_number == (size_t)(-1)) - buffer[0] = '\0'; - else - sprintf (buffer, ":%ld", (long) pos->line_number); - multiline_warning (xasprintf (_("%s%s: warning: "), - pos->file_name, buffer), - xasprintf (_("\ -'%s' format string with unnamed arguments cannot be properly localized:\n\ -The translator cannot reorder the arguments.\n\ -Please consider using a format string with named arguments,\n\ -and a mapping instead of a tuple for the arguments.\n"), - pretty_msgstr)); - error_with_progname = true; - } -} - - -message_ty * -remember_a_message (message_list_ty *mlp, char *msgctxt, char *msgid, - flag_context_ty context, lex_pos_ty *pos, - const char *extracted_comment, - refcounted_string_list_ty *comment) -{ - enum is_format is_format[NFORMATS]; - struct argument_range range; - enum is_wrap do_wrap; - enum is_syntax_check do_syntax_check[NSYNTAXCHECKS]; - message_ty *mp; - char *msgstr; - size_t i; - - /* See whether we shall exclude this message. */ - if (exclude != NULL && message_list_search (exclude, msgctxt, msgid) != NULL) - { - /* Tell the lexer to reset its comment buffer, so that the next - message gets the correct comments. */ - xgettext_comment_reset (); - savable_comment_reset (); - - if (msgctxt != NULL) - free (msgctxt); - free (msgid); - - return NULL; - } - - savable_comment_to_xgettext_comment (comment); - - for (i = 0; i < NFORMATS; i++) - is_format[i] = undecided; - range.min = -1; - range.max = -1; - do_wrap = undecided; - for (i = 0; i < NSYNTAXCHECKS; i++) - do_syntax_check[i] = undecided; - - if (msgctxt != NULL) - CONVERT_STRING (msgctxt, lc_string); - CONVERT_STRING (msgid, lc_string); - - if (msgctxt == NULL && msgid[0] == '\0' && !xgettext_omit_header) - { - char buffer[21]; - - error_with_progname = false; - if (pos->line_number == (size_t)(-1)) - buffer[0] = '\0'; - else - sprintf (buffer, ":%ld", (long) pos->line_number); - multiline_warning (xasprintf (_("%s%s: warning: "), pos->file_name, - buffer), - xstrdup (_("\ -Empty msgid. It is reserved by GNU gettext:\n\ -gettext(\"\") returns the header entry with\n\ -meta information, not the empty string.\n"))); - error_with_progname = true; - } - - /* See if we have seen this message before. */ - mp = message_list_search (mlp, msgctxt, msgid); - if (mp != NULL) - { - if (msgctxt != NULL) - free (msgctxt); - free (msgid); - for (i = 0; i < NFORMATS; i++) - is_format[i] = mp->is_format[i]; - do_wrap = mp->do_wrap; - for (i = 0; i < NSYNTAXCHECKS; i++) - do_syntax_check[i] = mp->do_syntax_check[i]; - } - else - { - /* Construct the msgstr from the prefix and suffix, otherwise use the - empty string. */ - if (msgstr_prefix) - msgstr = xasprintf ("%s%s%s", msgstr_prefix, msgid, msgstr_suffix); - else - msgstr = ""; - - /* Allocate a new message and append the message to the list. */ - mp = message_alloc (msgctxt, msgid, NULL, msgstr, strlen (msgstr) + 1, - pos); - /* Do not free msgctxt and msgid. */ - message_list_append (mlp, mp); - } - - /* Determine whether the context specifies that the msgid is a format - string. */ - set_format_flags_from_context (is_format, context, mp->msgid, pos, "msgid"); - - /* Ask the lexer for the comments it has seen. */ - { - size_t nitems_before; - size_t nitems_after; - int j; - bool add_all_remaining_comments; - /* The string before the comment tag. For example, If "** TRANSLATORS:" - is seen and the comment tag is "TRANSLATORS:", - then comment_tag_prefix is set to "** ". */ - const char *comment_tag_prefix = ""; - size_t comment_tag_prefix_length = 0; - - nitems_before = (mp->comment_dot != NULL ? mp->comment_dot->nitems : 0); - - if (extracted_comment != NULL) - { - char *copy = xstrdup (extracted_comment); - char *rest; - - rest = copy; - while (*rest != '\0') - { - char *newline = strchr (rest, '\n'); - - if (newline != NULL) - { - *newline = '\0'; - message_comment_dot_append (mp, rest); - rest = newline + 1; - } - else - { - message_comment_dot_append (mp, rest); - break; - } - } - free (copy); - } - - add_all_remaining_comments = add_all_comments; - for (j = 0; ; ++j) - { - const char *s = xgettext_comment (j); - const char *t; - if (s == NULL) - break; - - CONVERT_STRING (s, lc_comment); - - /* To reduce the possibility of unwanted matches we do a two - step match: the line must contain 'xgettext:' and one of - the possible format description strings. */ - if ((t = c_strstr (s, "xgettext:")) != NULL) - { - bool tmp_fuzzy; - enum is_format tmp_format[NFORMATS]; - struct argument_range tmp_range; - enum is_wrap tmp_wrap; - enum is_syntax_check tmp_syntax_check[NSYNTAXCHECKS]; - bool interesting; - - t += strlen ("xgettext:"); - - po_parse_comment_special (t, &tmp_fuzzy, tmp_format, &tmp_range, - &tmp_wrap, tmp_syntax_check); - - interesting = false; - for (i = 0; i < NFORMATS; i++) - if (tmp_format[i] != undecided) - { - is_format[i] = tmp_format[i]; - interesting = true; - } - if (has_range_p (tmp_range)) - { - range = tmp_range; - interesting = true; - } - if (tmp_wrap != undecided) - { - do_wrap = tmp_wrap; - interesting = true; - } - for (i = 0; i < NSYNTAXCHECKS; i++) - if (tmp_syntax_check[i] != undecided) - { - do_syntax_check[i] = tmp_syntax_check[i]; - interesting = true; - } - - /* If the "xgettext:" marker was followed by an interesting - keyword, and we updated our is_format/do_wrap variables, - we don't print the comment as a #. comment. */ - if (interesting) - continue; - } - - if (!add_all_remaining_comments && comment_tag != NULL) - { - /* When the comment tag is seen, it drags in not only the line - which it starts, but all remaining comment lines. */ - if ((t = c_strstr (s, comment_tag)) != NULL) - { - add_all_remaining_comments = true; - comment_tag_prefix = s; - comment_tag_prefix_length = t - s; - } - } - - if (add_all_remaining_comments) - { - if (strncmp (s, comment_tag_prefix, comment_tag_prefix_length) == 0) - s += comment_tag_prefix_length; - message_comment_dot_append (mp, s); - } - } - - nitems_after = (mp->comment_dot != NULL ? mp->comment_dot->nitems : 0); - - /* Don't add the comments if they are a repetition of the tail of the - already present comments. This avoids unneeded duplication if the - same message appears several times, each time with the same comment. */ - if (nitems_before < nitems_after) - { - size_t added = nitems_after - nitems_before; - - if (added <= nitems_before) - { - bool repeated = true; - - for (i = 0; i < added; i++) - if (strcmp (mp->comment_dot->item[nitems_before - added + i], - mp->comment_dot->item[nitems_before + i]) != 0) - { - repeated = false; - break; - } - - if (repeated) - { - for (i = 0; i < added; i++) - free ((char *) mp->comment_dot->item[nitems_before + i]); - mp->comment_dot->nitems = nitems_before; - } - } - } - } - - /* If it is not already decided, through programmer comments, whether the - msgid is a format string, examine the msgid. This is a heuristic. */ - for (i = 0; i < NFORMATS; i++) - { - if (is_format[i] == undecided - && (formatstring_parsers[i] == current_formatstring_parser1 - || formatstring_parsers[i] == current_formatstring_parser2 - || formatstring_parsers[i] == current_formatstring_parser3) - /* But avoid redundancy: objc-format is stronger than c-format. */ - && !(i == format_c && possible_format_p (is_format[format_objc])) - && !(i == format_objc && possible_format_p (is_format[format_c])) - /* Avoid flagging a string as c-format when it's known to be a - qt-format or qt-plural-format or kde-format or boost-format - string. */ - && !(i == format_c - && (possible_format_p (is_format[format_qt]) - || possible_format_p (is_format[format_qt_plural]) - || possible_format_p (is_format[format_kde]) - || possible_format_p (is_format[format_kde_kuit]) - || possible_format_p (is_format[format_boost]))) - /* Avoid flagging a string as kde-format when it's known to - be a kde-kuit-format string. */ - && !(i == format_kde - && possible_format_p (is_format[format_kde_kuit])) - /* Avoid flagging a string as kde-kuit-format when it's - known to be a kde-format string. Note that this relies - on the fact that format_kde < format_kde_kuit, so a - string will be marked as kde-format if both are - undecided. */ - && !(i == format_kde_kuit - && possible_format_p (is_format[format_kde]))) - { - struct formatstring_parser *parser = formatstring_parsers[i]; - char *invalid_reason = NULL; - void *descr = parser->parse (mp->msgid, false, NULL, &invalid_reason); - - if (descr != NULL) - { - /* msgid is a valid format string. We mark only those msgids - as format strings which contain at least one format directive - and thus are format strings with a high probability. We - don't mark strings without directives as format strings, - because that would force the programmer to add - "xgettext: no-c-format" anywhere where a translator wishes - to use a percent sign. So, the msgfmt checking will not be - perfect. Oh well. */ - if (parser->get_number_of_directives (descr) > 0 - && !(parser->is_unlikely_intentional != NULL - && parser->is_unlikely_intentional (descr))) - is_format[i] = possible; - - parser->free (descr); - } - else - { - /* msgid is not a valid format string. */ - is_format[i] = impossible; - free (invalid_reason); - } - } - mp->is_format[i] = is_format[i]; - } - - if (has_range_p (range)) - { - if (has_range_p (mp->range)) - { - if (range.min < mp->range.min) - mp->range.min = range.min; - if (range.max > mp->range.max) - mp->range.max = range.max; - } - else - mp->range = range; - } - - mp->do_wrap = do_wrap == no ? no : yes; /* By default we wrap. */ - - for (i = 0; i < NSYNTAXCHECKS; i++) - { - if (do_syntax_check[i] == undecided) - do_syntax_check[i] = default_syntax_check[i] == yes ? yes : no; - - mp->do_syntax_check[i] = do_syntax_check[i]; - } - - /* Warn about the use of non-reorderable format strings when the programming - language also provides reorderable format strings. */ - warn_format_string (is_format, mp->msgid, pos, "msgid"); - - /* Remember where we saw this msgid. */ - message_comment_filepos (mp, pos->file_name, pos->line_number); - - /* Tell the lexer to reset its comment buffer, so that the next - message gets the correct comments. */ - xgettext_comment_reset (); - savable_comment_reset (); - - return mp; -} - - -void -remember_a_message_plural (message_ty *mp, char *string, - flag_context_ty context, lex_pos_ty *pos, - refcounted_string_list_ty *comment) -{ - char *msgid_plural; - char *msgstr1; - size_t msgstr1_len; - char *msgstr; - size_t i; - - msgid_plural = string; - - savable_comment_to_xgettext_comment (comment); - - CONVERT_STRING (msgid_plural, lc_string); - - /* See if the message is already a plural message. */ - if (mp->msgid_plural == NULL) - { - mp->msgid_plural = msgid_plural; - - /* Construct the first plural form from the prefix and suffix, - otherwise use the empty string. The translator will have to - provide additional plural forms. */ - if (msgstr_prefix) - msgstr1 = - xasprintf ("%s%s%s", msgstr_prefix, msgid_plural, msgstr_suffix); - else - msgstr1 = ""; - msgstr1_len = strlen (msgstr1) + 1; - msgstr = XNMALLOC (mp->msgstr_len + msgstr1_len, char); - memcpy (msgstr, mp->msgstr, mp->msgstr_len); - memcpy (msgstr + mp->msgstr_len, msgstr1, msgstr1_len); - mp->msgstr = msgstr; - mp->msgstr_len = mp->msgstr_len + msgstr1_len; - if (msgstr_prefix) - free (msgstr1); - - /* Determine whether the context specifies that the msgid_plural is a - format string. */ - set_format_flags_from_context (mp->is_format, context, mp->msgid_plural, - pos, "msgid_plural"); - - /* If it is not already decided, through programmer comments or - the msgid, whether the msgid is a format string, examine the - msgid_plural. This is a heuristic. */ - for (i = 0; i < NFORMATS; i++) - if ((formatstring_parsers[i] == current_formatstring_parser1 - || formatstring_parsers[i] == current_formatstring_parser2 - || formatstring_parsers[i] == current_formatstring_parser3) - && (mp->is_format[i] == undecided || mp->is_format[i] == possible) - /* But avoid redundancy: objc-format is stronger than c-format. */ - && !(i == format_c - && possible_format_p (mp->is_format[format_objc])) - && !(i == format_objc - && possible_format_p (mp->is_format[format_c])) - /* Avoid flagging a string as c-format when it's known to be a - qt-format or qt-plural-format or boost-format string. */ - && !(i == format_c - && (possible_format_p (mp->is_format[format_qt]) - || possible_format_p (mp->is_format[format_qt_plural]) - || possible_format_p (mp->is_format[format_kde]) - || possible_format_p (mp->is_format[format_kde_kuit]) - || possible_format_p (mp->is_format[format_boost]))) - /* Avoid flagging a string as kde-format when it's known - to be a kde-kuit-format string. */ - && !(i == format_kde - && possible_format_p (mp->is_format[format_kde_kuit])) - /* Avoid flagging a string as kde-kuit-format when it's - known to be a kde-format string. Note that this relies - on the fact that format_kde < format_kde_kuit, so a - string will be marked as kde-format if both are - undecided. */ - && !(i == format_kde_kuit - && possible_format_p (mp->is_format[format_kde]))) - { - struct formatstring_parser *parser = formatstring_parsers[i]; - char *invalid_reason = NULL; - void *descr = - parser->parse (mp->msgid_plural, false, NULL, &invalid_reason); - - if (descr != NULL) - { - /* Same heuristic as in remember_a_message. */ - if (parser->get_number_of_directives (descr) > 0 - && !(parser->is_unlikely_intentional != NULL - && parser->is_unlikely_intentional (descr))) - mp->is_format[i] = possible; - - parser->free (descr); - } - else - { - /* msgid_plural is not a valid format string. */ - mp->is_format[i] = impossible; - free (invalid_reason); - } - } - - /* Warn about the use of non-reorderable format strings when the programming - language also provides reorderable format strings. */ - warn_format_string (mp->is_format, mp->msgid_plural, pos, "msgid_plural"); - } - else - free (msgid_plural); - - /* Tell the lexer to reset its comment buffer, so that the next - message gets the correct comments. */ - xgettext_comment_reset (); - savable_comment_reset (); -} - - -struct arglist_parser * -arglist_parser_alloc (message_list_ty *mlp, const struct callshapes *shapes) -{ - if (shapes == NULL || shapes->nshapes == 0) - { - struct arglist_parser *ap = - (struct arglist_parser *) - xmalloc (offsetof (struct arglist_parser, alternative[0])); - - ap->mlp = mlp; - ap->keyword = NULL; - ap->keyword_len = 0; - ap->next_is_msgctxt = false; - ap->nalternatives = 0; - - return ap; - } - else - { - struct arglist_parser *ap = - (struct arglist_parser *) - xmalloc (xsum (sizeof (struct arglist_parser), - xtimes (shapes->nshapes - 1, - sizeof (struct partial_call)))); - size_t i; - - ap->mlp = mlp; - ap->keyword = shapes->keyword; - ap->keyword_len = shapes->keyword_len; - ap->next_is_msgctxt = false; - ap->nalternatives = shapes->nshapes; - for (i = 0; i < shapes->nshapes; i++) - { - ap->alternative[i].argnumc = shapes->shapes[i].argnumc; - ap->alternative[i].argnum1 = shapes->shapes[i].argnum1; - ap->alternative[i].argnum2 = shapes->shapes[i].argnum2; - ap->alternative[i].argnum1_glib_context = - shapes->shapes[i].argnum1_glib_context; - ap->alternative[i].argnum2_glib_context = - shapes->shapes[i].argnum2_glib_context; - ap->alternative[i].argtotal = shapes->shapes[i].argtotal; - ap->alternative[i].xcomments = shapes->shapes[i].xcomments; - ap->alternative[i].msgctxt = NULL; - ap->alternative[i].msgctxt_pos.file_name = NULL; - ap->alternative[i].msgctxt_pos.line_number = (size_t)(-1); - ap->alternative[i].msgid = NULL; - ap->alternative[i].msgid_context = null_context; - ap->alternative[i].msgid_pos.file_name = NULL; - ap->alternative[i].msgid_pos.line_number = (size_t)(-1); - ap->alternative[i].msgid_comment = NULL; - ap->alternative[i].msgid_plural = NULL; - ap->alternative[i].msgid_plural_context = null_context; - ap->alternative[i].msgid_plural_pos.file_name = NULL; - ap->alternative[i].msgid_plural_pos.line_number = (size_t)(-1); - } - - return ap; - } -} - - -struct arglist_parser * -arglist_parser_clone (struct arglist_parser *ap) -{ - struct arglist_parser *copy = - (struct arglist_parser *) - xmalloc (xsum (sizeof (struct arglist_parser) - sizeof (struct partial_call), - xtimes (ap->nalternatives, sizeof (struct partial_call)))); - size_t i; - - copy->mlp = ap->mlp; - copy->keyword = ap->keyword; - copy->keyword_len = ap->keyword_len; - copy->next_is_msgctxt = ap->next_is_msgctxt; - copy->nalternatives = ap->nalternatives; - for (i = 0; i < ap->nalternatives; i++) - { - const struct partial_call *cp = &ap->alternative[i]; - struct partial_call *ccp = ©->alternative[i]; - - ccp->argnumc = cp->argnumc; - ccp->argnum1 = cp->argnum1; - ccp->argnum2 = cp->argnum2; - ccp->argnum1_glib_context = cp->argnum1_glib_context; - ccp->argnum2_glib_context = cp->argnum2_glib_context; - ccp->argtotal = cp->argtotal; - ccp->xcomments = cp->xcomments; - ccp->msgctxt = (cp->msgctxt != NULL ? xstrdup (cp->msgctxt) : NULL); - ccp->msgctxt_pos = cp->msgctxt_pos; - ccp->msgid = (cp->msgid != NULL ? xstrdup (cp->msgid) : NULL); - ccp->msgid_context = cp->msgid_context; - ccp->msgid_pos = cp->msgctxt_pos; - ccp->msgid_comment = add_reference (cp->msgid_comment); - ccp->msgid_plural = - (cp->msgid_plural != NULL ? xstrdup (cp->msgid_plural) : NULL); - ccp->msgid_plural_context = cp->msgid_plural_context; - ccp->msgid_plural_pos = cp->msgid_plural_pos; - } - - return copy; -} - - -void -arglist_parser_remember (struct arglist_parser *ap, - int argnum, char *string, - flag_context_ty context, - char *file_name, size_t line_number, - refcounted_string_list_ty *comment) -{ - bool stored_string = false; - size_t nalternatives = ap->nalternatives; - size_t i; - - if (!(argnum > 0)) - abort (); - for (i = 0; i < nalternatives; i++) - { - struct partial_call *cp = &ap->alternative[i]; - - if (argnum == cp->argnumc) - { - cp->msgctxt = string; - cp->msgctxt_pos.file_name = file_name; - cp->msgctxt_pos.line_number = line_number; - stored_string = true; - /* Mark msgctxt as done. */ - cp->argnumc = 0; - } - else - { - if (argnum == cp->argnum1) - { - cp->msgid = string; - cp->msgid_context = context; - cp->msgid_pos.file_name = file_name; - cp->msgid_pos.line_number = line_number; - cp->msgid_comment = add_reference (comment); - stored_string = true; - /* Mark msgid as done. */ - cp->argnum1 = 0; - } - if (argnum == cp->argnum2) - { - cp->msgid_plural = string; - cp->msgid_plural_context = context; - cp->msgid_plural_pos.file_name = file_name; - cp->msgid_plural_pos.line_number = line_number; - stored_string = true; - /* Mark msgid_plural as done. */ - cp->argnum2 = 0; - } - } - } - /* Note: There is a memory leak here: When string was stored but is later - not used by arglist_parser_done, we don't free it. */ - if (!stored_string) - free (string); -} - - -void -arglist_parser_remember_msgctxt (struct arglist_parser *ap, - char *string, - flag_context_ty context, - char *file_name, size_t line_number) -{ - bool stored_string = false; - size_t nalternatives = ap->nalternatives; - size_t i; - - for (i = 0; i < nalternatives; i++) - { - struct partial_call *cp = &ap->alternative[i]; - - cp->msgctxt = string; - cp->msgctxt_pos.file_name = file_name; - cp->msgctxt_pos.line_number = line_number; - stored_string = true; - /* Mark msgctxt as done. */ - cp->argnumc = 0; - } - /* Note: There is a memory leak here: When string was stored but is later - not used by arglist_parser_done, we don't free it. */ - if (!stored_string) - free (string); -} - - bool -arglist_parser_decidedp (struct arglist_parser *ap, int argnum) -{ - size_t i; - - /* Test whether all alternatives are decided. - Note: A decided alternative can be complete - cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 - && cp->argtotal == 0 - or it can be failed if no literal strings were found at the specified - argument positions: - cp->argnumc <= argnum && cp->argnum1 <= argnum && cp->argnum2 <= argnum - or it can be failed if the number of arguments is exceeded: - cp->argtotal > 0 && cp->argtotal < argnum - */ - for (i = 0; i < ap->nalternatives; i++) - { - struct partial_call *cp = &ap->alternative[i]; - - if (!((cp->argnumc <= argnum - && cp->argnum1 <= argnum - && cp->argnum2 <= argnum) - || (cp->argtotal > 0 && cp->argtotal < argnum))) - /* cp is still undecided. */ - return false; - } - return true; -} - - -void -arglist_parser_done (struct arglist_parser *ap, int argnum) -{ - size_t ncomplete; - size_t i; - - /* Determine the number of complete calls. */ - ncomplete = 0; - for (i = 0; i < ap->nalternatives; i++) - { - struct partial_call *cp = &ap->alternative[i]; - - if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 - && (cp->argtotal == 0 || cp->argtotal == argnum)) - ncomplete++; - } - - if (ncomplete > 0) - { - struct partial_call *best_cp = NULL; - bool ambiguous = false; - - /* Find complete calls where msgctxt, msgid, msgid_plural are all - provided. */ - for (i = 0; i < ap->nalternatives; i++) - { - struct partial_call *cp = &ap->alternative[i]; - - if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 - && (cp->argtotal == 0 || cp->argtotal == argnum) - && cp->msgctxt != NULL - && cp->msgid != NULL - && cp->msgid_plural != NULL) - { - if (best_cp != NULL) - { - ambiguous = true; - break; - } - best_cp = cp; - } - } - - if (best_cp == NULL) - { - struct partial_call *best_cp1 = NULL; - struct partial_call *best_cp2 = NULL; - - /* Find complete calls where msgctxt, msgid are provided. */ - for (i = 0; i < ap->nalternatives; i++) - { - struct partial_call *cp = &ap->alternative[i]; - - if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 - && (cp->argtotal == 0 || cp->argtotal == argnum) - && cp->msgctxt != NULL - && cp->msgid != NULL) - { - if (best_cp1 != NULL) - { - ambiguous = true; - break; - } - best_cp1 = cp; - } - } - - /* Find complete calls where msgid, msgid_plural are provided. */ - for (i = 0; i < ap->nalternatives; i++) - { - struct partial_call *cp = &ap->alternative[i]; - - if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 - && (cp->argtotal == 0 || cp->argtotal == argnum) - && cp->msgid != NULL - && cp->msgid_plural != NULL) - { - if (best_cp2 != NULL) - { - ambiguous = true; - break; - } - best_cp2 = cp; - } - } - - if (best_cp1 != NULL) - best_cp = best_cp1; - if (best_cp2 != NULL) - { - if (best_cp != NULL) - ambiguous = true; - else - best_cp = best_cp2; - } - } - - if (best_cp == NULL) - { - /* Find complete calls where msgid is provided. */ - for (i = 0; i < ap->nalternatives; i++) - { - struct partial_call *cp = &ap->alternative[i]; - - if (cp->argnumc == 0 && cp->argnum1 == 0 && cp->argnum2 == 0 - && (cp->argtotal == 0 || cp->argtotal == argnum) - && cp->msgid != NULL) - { - if (best_cp != NULL) - { - ambiguous = true; - break; - } - best_cp = cp; - } - } - } - - if (ambiguous) - { - error_with_progname = false; - error_at_line (0, 0, - best_cp->msgid_pos.file_name, - best_cp->msgid_pos.line_number, - _("ambiguous argument specification for keyword '%.*s'"), - (int) ap->keyword_len, ap->keyword); - error_with_progname = true; - } - - if (best_cp != NULL) - { - /* best_cp indicates the best found complete call. - Now call remember_a_message. */ - message_ty *mp; - - /* Split strings in the GNOME glib syntax "msgctxt|msgid". */ - if (best_cp->argnum1_glib_context || best_cp->argnum2_glib_context) - /* split_keywordspec should not allow the context to be specified - in two different ways. */ - if (best_cp->msgctxt != NULL) - abort (); - if (best_cp->argnum1_glib_context) - { - const char *separator = strchr (best_cp->msgid, '|'); - - if (separator == NULL) - { - error_with_progname = false; - error_at_line (0, 0, - best_cp->msgid_pos.file_name, - best_cp->msgid_pos.line_number, - _("warning: missing context for keyword '%.*s'"), - (int) ap->keyword_len, ap->keyword); - error_with_progname = true; - } - else - { - size_t ctxt_len = separator - best_cp->msgid; - char *ctxt = XNMALLOC (ctxt_len + 1, char); - - memcpy (ctxt, best_cp->msgid, ctxt_len); - ctxt[ctxt_len] = '\0'; - best_cp->msgctxt = ctxt; - best_cp->msgid = xstrdup (separator + 1); - } - } - if (best_cp->msgid_plural != NULL && best_cp->argnum2_glib_context) - { - const char *separator = strchr (best_cp->msgid_plural, '|'); - - if (separator == NULL) - { - error_with_progname = false; - error_at_line (0, 0, - best_cp->msgid_plural_pos.file_name, - best_cp->msgid_plural_pos.line_number, - _("warning: missing context for plural argument of keyword '%.*s'"), - (int) ap->keyword_len, ap->keyword); - error_with_progname = true; - } - else - { - size_t ctxt_len = separator - best_cp->msgid_plural; - char *ctxt = XNMALLOC (ctxt_len + 1, char); - - memcpy (ctxt, best_cp->msgid_plural, ctxt_len); - ctxt[ctxt_len] = '\0'; - if (best_cp->msgctxt == NULL) - best_cp->msgctxt = ctxt; - else - { - if (strcmp (ctxt, best_cp->msgctxt) != 0) - { - error_with_progname = false; - error_at_line (0, 0, - best_cp->msgid_plural_pos.file_name, - best_cp->msgid_plural_pos.line_number, - _("context mismatch between singular and plural form")); - error_with_progname = true; - } - free (ctxt); - } - best_cp->msgid_plural = xstrdup (separator + 1); - } - } - - { - flag_context_ty msgid_context = best_cp->msgid_context; - flag_context_ty msgid_plural_context = best_cp->msgid_plural_context; - - /* Special support for the 3-argument tr operator in Qt: - When --qt and --keyword=tr:1,1,2c,3t are specified, add to the - context the information that the argument is expeected to be a - qt-plural-format. */ - if (recognize_format_qt - && current_formatstring_parser3 == &formatstring_qt_plural - && best_cp->msgid_plural == best_cp->msgid) - { - msgid_context.is_format3 = yes_according_to_context; - msgid_plural_context.is_format3 = yes_according_to_context; - } - - mp = remember_a_message (ap->mlp, best_cp->msgctxt, best_cp->msgid, - msgid_context, - &best_cp->msgid_pos, - NULL, best_cp->msgid_comment); - if (mp != NULL && best_cp->msgid_plural != NULL) - remember_a_message_plural (mp, best_cp->msgid_plural, - msgid_plural_context, - &best_cp->msgid_plural_pos, - NULL); - } - - if (best_cp->xcomments.nitems > 0) - { - /* Add best_cp->xcomments to mp->comment_dot, unless already - present. */ - size_t i; - - for (i = 0; i < best_cp->xcomments.nitems; i++) - { - const char *xcomment = best_cp->xcomments.item[i]; - bool found = false; - - if (mp != NULL && mp->comment_dot != NULL) - { - size_t j; - - for (j = 0; j < mp->comment_dot->nitems; j++) - if (strcmp (xcomment, mp->comment_dot->item[j]) == 0) - { - found = true; - break; - } - } - if (!found) - message_comment_dot_append (mp, xcomment); - } - } - } - } - else - { - /* No complete call was parsed. */ - /* Note: There is a memory leak here: When there is more than one - alternative, the same string can be stored in multiple alternatives, - and it's not easy to free all strings reliably. */ - if (ap->nalternatives == 1) - { - if (ap->alternative[0].msgctxt != NULL) - free (ap->alternative[0].msgctxt); - if (ap->alternative[0].msgid != NULL) - free (ap->alternative[0].msgid); - if (ap->alternative[0].msgid_plural != NULL) - free (ap->alternative[0].msgid_plural); - } - } - - for (i = 0; i < ap->nalternatives; i++) - drop_reference (ap->alternative[i].msgid_comment); - free (ap); -} - - -struct mixed_string_buffer * -mixed_string_buffer_alloc (lexical_context_ty lcontext, - const char *logical_file_name, - int line_number) -{ - struct mixed_string_buffer *bp = XMALLOC (struct mixed_string_buffer); - bp->utf8_buffer = NULL; - bp->utf8_buflen = 0; - bp->utf8_allocated = 0; - bp->utf16_surr = 0; - bp->curr_buffer = NULL; - bp->curr_buflen = 0; - bp->curr_allocated = 0; - bp->lcontext = lcontext; - bp->logical_file_name = logical_file_name; - bp->line_number = line_number; - return bp; -} - -/* Auxiliary function: Append a byte to bp->curr. */ -static inline void -mixed_string_buffer_append_to_curr_buffer (struct mixed_string_buffer *bp, - unsigned char c) -{ - if (bp->curr_buflen == bp->curr_allocated) - { - bp->curr_allocated = 2 * bp->curr_allocated + 10; - bp->curr_buffer = xrealloc (bp->curr_buffer, bp->curr_allocated); - } - bp->curr_buffer[bp->curr_buflen++] = c; -} - -/* Auxiliary function: Ensure count more bytes are available in bp->utf8. */ -static inline void -mixed_string_buffer_grow_utf8_buffer (struct mixed_string_buffer *bp, - size_t count) -{ - if (bp->utf8_buflen + count > bp->utf8_allocated) - { - size_t new_allocated = 2 * bp->utf8_allocated + 10; - if (new_allocated < bp->utf8_buflen + count) - new_allocated = bp->utf8_buflen + count; - bp->utf8_allocated = new_allocated; - bp->utf8_buffer = xrealloc (bp->utf8_buffer, new_allocated); - } -} - -/* Auxiliary function: Append a Unicode character to bp->utf8. - uc must be < 0x110000. */ -static inline void -mixed_string_buffer_append_to_utf8_buffer (struct mixed_string_buffer *bp, - ucs4_t uc) -{ - unsigned char utf8buf[6]; - int count = u8_uctomb (utf8buf, uc, 6); - - if (count < 0) - /* The caller should have ensured that uc is not out-of-range. */ - abort (); - - mixed_string_buffer_grow_utf8_buffer (bp, count); - memcpy (bp->utf8_buffer + bp->utf8_buflen, utf8buf, count); - bp->utf8_buflen += count; -} - -/* Auxiliary function: Flush bp->utf16_surr into bp->utf8_buffer. */ -static inline void -mixed_string_buffer_flush_utf16_surr (struct mixed_string_buffer *bp) -{ - if (bp->utf16_surr != 0) - { - /* A half surrogate is invalid, therefore use U+FFFD instead. */ - mixed_string_buffer_append_to_utf8_buffer (bp, 0xfffd); - bp->utf16_surr = 0; - } -} - -/* Auxiliary function: Flush bp->curr_buffer into bp->utf8_buffer. */ -static inline void -mixed_string_buffer_flush_curr_buffer (struct mixed_string_buffer *bp, - int line_number) -{ - if (bp->curr_buflen > 0) - { - char *curr; - size_t count; - - mixed_string_buffer_append_to_curr_buffer (bp, '\0'); - - /* Convert from the source encoding to UTF-8. */ - curr = from_current_source_encoding (bp->curr_buffer, bp->lcontext, - bp->logical_file_name, - line_number); - - /* Append it to bp->utf8_buffer. */ - count = strlen (curr); - mixed_string_buffer_grow_utf8_buffer (bp, count); - memcpy (bp->utf8_buffer + bp->utf8_buflen, curr, count); - bp->utf8_buflen += count; - - if (curr != bp->curr_buffer) - free (curr); - bp->curr_buflen = 0; - } -} - -void -mixed_string_buffer_append_char (struct mixed_string_buffer *bp, int c) -{ - /* Switch from Unicode character mode to multibyte character mode. */ - mixed_string_buffer_flush_utf16_surr (bp); - - /* When a newline is seen, convert the accumulated multibyte sequence. - This ensures a correct line number in the error message in case of - a conversion error. The "- 1" is to account for the newline. */ - if (c == '\n') - mixed_string_buffer_flush_curr_buffer (bp, bp->line_number - 1); - - mixed_string_buffer_append_to_curr_buffer (bp, (unsigned char) c); -} - -void -mixed_string_buffer_append_unicode (struct mixed_string_buffer *bp, int c) -{ - /* Switch from multibyte character mode to Unicode character mode. */ - mixed_string_buffer_flush_curr_buffer (bp, bp->line_number); - - /* Test whether this character and the previous one form a Unicode - surrogate character pair. */ - if (bp->utf16_surr != 0 && (c >= 0xdc00 && c < 0xe000)) - { - unsigned short utf16buf[2]; - ucs4_t uc; - - utf16buf[0] = bp->utf16_surr; - utf16buf[1] = c; - if (u16_mbtouc (&uc, utf16buf, 2) != 2) - abort (); - - mixed_string_buffer_append_to_utf8_buffer (bp, uc); - bp->utf16_surr = 0; - } - else - { - mixed_string_buffer_flush_utf16_surr (bp); - - if (c >= 0xd800 && c < 0xdc00) - bp->utf16_surr = c; - else if (c >= 0xdc00 && c < 0xe000) - { - /* A half surrogate is invalid, therefore use U+FFFD instead. */ - mixed_string_buffer_append_to_utf8_buffer (bp, 0xfffd); - } - else - mixed_string_buffer_append_to_utf8_buffer (bp, c); - } -} - -char * -mixed_string_buffer_done (struct mixed_string_buffer *bp) +recognize_qt_formatstrings (void) { - char *utf8_buffer; - - /* Flush all into bp->utf8_buffer. */ - mixed_string_buffer_flush_utf16_surr (bp); - mixed_string_buffer_flush_curr_buffer (bp, bp->line_number); - /* NUL-terminate it. */ - mixed_string_buffer_grow_utf8_buffer (bp, 1); - bp->utf8_buffer[bp->utf8_buflen] = '\0'; - - /* Free curr_buffer and bp itself. */ - utf8_buffer = bp->utf8_buffer; - free (bp->curr_buffer); - free (bp); - - /* Return it. */ - return utf8_buffer; + return recognize_format_qt + && current_formatstring_parser3 == &formatstring_qt_plural; } diff --git a/gettext-tools/src/xgettext.h b/gettext-tools/src/xgettext.h index 7c3712a9b..4d5df4ec2 100644 --- a/gettext-tools/src/xgettext.h +++ b/gettext-tools/src/xgettext.h @@ -21,177 +21,50 @@ #include #include -#include - -#if HAVE_ICONV -#include -#endif #include "message.h" -#include "pos.h" -#include "str-list.h" - -/* Declare 'line_comment' and 'input_syntax'. */ -#include "read-catalog.h" - +#include "rc-str-list.h" #ifdef __cplusplus extern "C" { #endif -/* If true, omit the header entry. - If false, keep the header entry present in the input. */ -extern int xgettext_omit_header; - -extern bool substring_match; - +/* If true, add all comments immediately preceding one of the keywords. */ +extern bool add_all_comments; -/* Calling convention for a given keyword. */ -struct callshape -{ - int argnum1; /* argument number to use for msgid */ - int argnum2; /* argument number to use for msgid_plural */ - int argnumc; /* argument number to use for msgctxt */ - bool argnum1_glib_context; /* argument argnum1 has the syntax "ctxt|msgid" */ - bool argnum2_glib_context; /* argument argnum2 has the syntax "ctxt|msgid" */ - int argtotal; /* total number of arguments */ - string_list_ty xcomments; /* auto-extracted comments */ -}; +/* Tag used in comment of prevailing domain. */ +extern char *comment_tag; -/* Split keyword spec into keyword, argnum1, argnum2, argnumc. */ -extern void split_keywordspec (const char *spec, const char **endp, - struct callshape *shapep); +/* List of messages whose msgids must not be extracted, or NULL. + Used by remember_a_message(). */ +extern message_list_ty *exclude; -/* Set of alternative calling conventions for a given keyword. */ -struct callshapes -{ - const char *keyword; /* the keyword, not NUL terminated */ - size_t keyword_len; /* the keyword's length */ - size_t nshapes; - struct callshape shapes[1]; /* actually nshapes elements */ -}; +/* String used as prefix for msgstr. */ +extern const char *msgstr_prefix; -/* Insert a (keyword, callshape) pair into a hash table mapping keyword to - 'struct callshapes *'. */ -extern void insert_keyword_callshape (hash_table *table, - const char *keyword, size_t keyword_len, - const struct callshape *shape); +/* String used as suffix for msgstr. */ +extern const char *msgstr_suffix; +/* If true, omit the header entry. + If false, keep the header entry present in the input. */ +extern int xgettext_omit_header; -/* Context representing some flags. */ -typedef struct flag_context_ty flag_context_ty; -struct flag_context_ty -{ - /* Regarding the primary formatstring type. */ - /*enum is_format*/ unsigned int is_format1 : 3; - /*bool*/ unsigned int pass_format1 : 1; - /* Regarding the secondary formatstring type. */ - /*enum is_format*/ unsigned int is_format2 : 3; - /*bool*/ unsigned int pass_format2 : 1; - /* Regarding the tertiary formatstring type. */ - /*enum is_format*/ unsigned int is_format3 : 3; - /*bool*/ unsigned int pass_format3 : 1; -}; -/* Null context. */ -extern flag_context_ty null_context; -/* Transparent context. */ -extern flag_context_ty passthrough_context; -/* Compute an inherited context. - The outer_context is assumed to have all pass_format* flags = false. - The result will then also have all pass_format* flags = false. */ -extern flag_context_ty - inherited_context (flag_context_ty outer_context, - flag_context_ty modifier_context); +extern enum is_syntax_check default_syntax_check[NSYNTAXCHECKS]; -/* Context representing some flags, for each possible argument number. - This is a linked list, sorted according to the argument number. */ -typedef struct flag_context_list_ty flag_context_list_ty; -struct flag_context_list_ty -{ - int argnum; /* current argument number, > 0 */ - flag_context_ty flags; /* flags for current argument */ - flag_context_list_ty *next; -}; +/* Language dependent format string parser. + NULL if the language has no notion of format strings. */ +extern struct formatstring_parser *current_formatstring_parser1; +extern struct formatstring_parser *current_formatstring_parser2; +extern struct formatstring_parser *current_formatstring_parser3; -/* Iterator through a flag_context_list_ty. */ -typedef struct flag_context_list_iterator_ty flag_context_list_iterator_ty; -struct flag_context_list_iterator_ty -{ - int argnum; /* current argument number, > 0 */ - const flag_context_list_ty* head; /* tail of list */ -}; -extern flag_context_list_iterator_ty null_context_list_iterator; -extern flag_context_list_iterator_ty passthrough_context_list_iterator; -extern flag_context_list_iterator_ty - flag_context_list_iterator (flag_context_list_ty *list); -extern flag_context_ty - flag_context_list_iterator_advance (flag_context_list_iterator_ty *iter); -/* For nearly each backend, we have a separate table mapping a keyword to - a flag_context_list_ty *. */ -typedef hash_table /* char[] -> flag_context_list_ty * */ - flag_context_list_table_ty; -extern flag_context_list_ty * - flag_context_list_table_lookup (flag_context_list_table_ty *flag_table, - const void *key, size_t keylen); /* Record a flag in the appropriate backend's table. */ extern void xgettext_record_flag (const char *optionstring); -/* Context while building up lexical tokens. */ -typedef enum - { - lc_outside, /* Initial context: outside of comments and strings. */ - lc_comment, /* Inside a comment. */ - lc_string, /* Inside a string literal. */ - - /* For embedded XML in programming code, like E4X in JavaScript. */ - lc_xml_open_tag, /* Inside an opening tag of an XML element. */ - lc_xml_close_tag, /* Inside a closing tag of an XML element. */ - lc_xml_content /* Inside an XML text node. */ - } - lexical_context_ty; - -/* Error message about non-ASCII character in a specific lexical context. */ -extern char *non_ascii_error_message (lexical_context_ty lcontext, - const char *file_name, - size_t line_number); - - -/* Canonicalized encoding name for all input files. */ -extern const char *xgettext_global_source_encoding; - -#if HAVE_ICONV -/* Converter from xgettext_global_source_encoding to UTF-8 (except from - ASCII or UTF-8, when this conversion is a no-op). */ -extern iconv_t xgettext_global_source_iconv; -#endif - -/* Canonicalized encoding name for the current input file. */ -extern const char *xgettext_current_source_encoding; - -#if HAVE_ICONV -/* Converter from xgettext_current_source_encoding to UTF-8 (except from - ASCII or UTF-8, when this conversion is a no-op). */ -extern iconv_t xgettext_current_source_iconv; -#endif - -/* Convert the given string from xgettext_current_source_encoding to - the output file encoding (i.e. ASCII or UTF-8). - The resulting string is either the argument string, or freshly allocated. - The lcontext, file_name and line_number are only used for error message - purposes. */ -extern char *from_current_source_encoding (const char *string, - lexical_context_ty lcontext, - const char *file_name, - size_t line_number); - - -/* List of messages whose msgids must not be extracted, or NULL. - Used by remember_a_message(). */ -extern message_list_ty *exclude; - +extern const char * xgettext_comment (size_t n); +extern void xgettext_comment_reset (void); /* Comment handling for backends which support combining adjacent strings even across lines. @@ -208,179 +81,14 @@ extern message_list_ty *exclude; In order to avoid excessive copying of strings, we use reference counting. */ -typedef struct refcounted_string_list_ty refcounted_string_list_ty; -struct refcounted_string_list_ty -{ - unsigned int refcount; - struct string_list_ty contents; -}; - -static inline refcounted_string_list_ty * -add_reference (refcounted_string_list_ty *rslp) -{ - if (rslp != NULL) - rslp->refcount++; - return rslp; -} - -static inline void -drop_reference (refcounted_string_list_ty *rslp) -{ - if (rslp != NULL) - { - if (rslp->refcount > 1) - rslp->refcount--; - else - { - string_list_destroy (&rslp->contents); - free (rslp); - } - } -} - extern refcounted_string_list_ty *savable_comment; extern void savable_comment_add (const char *str); extern void savable_comment_reset (void); +extern void + savable_comment_to_xgettext_comment (refcounted_string_list_ty *rslp); -/* Add a message to the list of extracted messages. - msgctxt must be either NULL or a malloc()ed string; its ownership is passed - to the callee. - MSGID must be a malloc()ed string; its ownership is passed to the callee. - POS->file_name must be allocated with indefinite extent. - EXTRACTED_COMMENT is a comment that needs to be copied into the POT file, - or NULL. - COMMENT may be savable_comment, or it may be a saved copy of savable_comment - (then add_reference must be used when saving it, and drop_reference while - dropping it). Clear savable_comment. - Return the new or found message, or NULL if the message is excluded. */ -extern message_ty *remember_a_message (message_list_ty *mlp, - char *msgctxt, - char *msgid, - flag_context_ty context, - lex_pos_ty *pos, - const char *extracted_comment, - refcounted_string_list_ty *comment); - -/* Add an msgid_plural to a message previously returned by - remember_a_message. - STRING must be a malloc()ed string; its ownership is passed to the callee. - POS->file_name must be allocated with indefinite extent. - COMMENT may be savable_comment, or it may be a saved copy of savable_comment - (then add_reference must be used when saving it, and drop_reference while - dropping it). Clear savable_comment. */ -extern void remember_a_message_plural (message_ty *mp, - char *string, - flag_context_ty context, - lex_pos_ty *pos, - refcounted_string_list_ty *comment); - - -/* Represents the progressive parsing of an argument list w.r.t. a single - 'struct callshape'. */ -struct partial_call -{ - int argnumc; /* number of context argument, 0 when seen */ - int argnum1; /* number of singular argument, 0 when seen */ - int argnum2; /* number of plural argument, 0 when seen */ - bool argnum1_glib_context; /* argument argnum1 has the syntax "ctxt|msgid" */ - bool argnum2_glib_context; /* argument argnum2 has the syntax "ctxt|msgid" */ - int argtotal; /* total number of arguments, 0 if unspecified */ - string_list_ty xcomments; /* auto-extracted comments */ - char *msgctxt; /* context - owned string, or NULL */ - lex_pos_ty msgctxt_pos; - char *msgid; /* msgid - owned string, or NULL */ - flag_context_ty msgid_context; - lex_pos_ty msgid_pos; - refcounted_string_list_ty *msgid_comment; - char *msgid_plural; /* msgid_plural - owned string, or NULL */ - flag_context_ty msgid_plural_context; - lex_pos_ty msgid_plural_pos; -}; - -/* Represents the progressive parsing of an argument list w.r.t. an entire - 'struct callshapes'. */ -struct arglist_parser -{ - message_list_ty *mlp; /* list where the message shall be added */ - const char *keyword; /* the keyword, not NUL terminated */ - size_t keyword_len; /* the keyword's length */ - bool next_is_msgctxt; /* true if the next argument is the msgctxt */ - size_t nalternatives; /* number of partial_call alternatives */ - struct partial_call alternative[1]; /* partial_call alternatives */ -}; - -/* Creates a fresh arglist_parser recognizing calls. - You can pass shapes = NULL for a parser not recognizing any calls. */ -extern struct arglist_parser * arglist_parser_alloc (message_list_ty *mlp, - const struct callshapes *shapes); -/* Clones an arglist_parser. */ -extern struct arglist_parser * arglist_parser_clone (struct arglist_parser *ap); -/* Adds a string argument to an arglist_parser. ARGNUM must be > 0. - STRING must be malloc()ed string; its ownership is passed to the callee. - FILE_NAME must be allocated with indefinite extent. - COMMENT may be savable_comment, or it may be a saved copy of savable_comment - (then add_reference must be used when saving it, and drop_reference while - dropping it). Clear savable_comment. */ -extern void arglist_parser_remember (struct arglist_parser *ap, - int argnum, char *string, - flag_context_ty context, - char *file_name, size_t line_number, - refcounted_string_list_ty *comment); -/* Adds a string argument as msgctxt to an arglist_parser, without incrementing - the current argument number. - STRING must be malloc()ed string; its ownership is passed to the callee. - FILE_NAME must be allocated with indefinite extent. */ -extern void arglist_parser_remember_msgctxt (struct arglist_parser *ap, - char *string, - flag_context_ty context, - char *file_name, size_t line_number); -/* Tests whether an arglist_parser has is not waiting for more arguments after - argument ARGNUM. */ -extern bool arglist_parser_decidedp (struct arglist_parser *ap, int argnum); -/* Terminates the processing of an arglist_parser after argument ARGNUM and - deletes it. */ -extern void arglist_parser_done (struct arglist_parser *ap, int argnum); - - -/* A string buffer type that allows appending bytes (in the - xgettext_current_source_encoding) or Unicode characters. - Returns the entire string in UTF-8 encoding. */ - -struct mixed_string_buffer -{ - /* The part of the string that has already been converted to UTF-8. */ - char *utf8_buffer; - size_t utf8_buflen; - size_t utf8_allocated; - /* The first half of an UTF-16 surrogate character. */ - unsigned short utf16_surr; - /* The part of the string that is still in the source encoding. */ - char *curr_buffer; - size_t curr_buflen; - size_t curr_allocated; - /* The lexical context. Used only for error message purposes. */ - lexical_context_ty lcontext; - const char *logical_file_name; - int line_number; -}; - -/* Creates a fresh mixed_string_buffer. */ -extern struct mixed_string_buffer * - mixed_string_buffer_alloc (lexical_context_ty lcontext, - const char *logical_file_name, - int line_number); - -/* Appends a character to a mixed_string_buffer. */ -extern void mixed_string_buffer_append_char (struct mixed_string_buffer *bp, - int c); - -/* Appends a Unicode character to a mixed_string_buffer. */ -extern void mixed_string_buffer_append_unicode (struct mixed_string_buffer *bp, - int c); - -/* Frees mixed_string_buffer and returns the accumulated string in UTF-8. */ -extern char * mixed_string_buffer_done (struct mixed_string_buffer *bp); +extern bool recognize_qt_formatstrings (void); #ifdef __cplusplus