+-------------- 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.
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
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 \
/* 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 <ueno@gnu.org>, 2015.
#ifndef _ITS_H_
#define _ITS_H_
+#include <stdio.h>
+
#include "message.h"
-#include "xgettext.h"
+#include "pos.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
extern "C" {
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#ifndef _RC_STR_LIST_H
+#define _RC_STR_LIST_H
+
+#include <stdlib.h>
+
+#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 */
/* 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 <haible@clisp.cons.org>, 2002.
#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 <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
#include <string.h>
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <bruno@clisp.org>, 2003.
This program is free software: you can redistribute it and/or modify
#include <string.h>
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <ueno@gnu.org>, 2014.
#include "message.h"
#include "xgettext.h"
+#include "xg-message.h"
#include "error.h"
#include "error-progname.h"
#include "xalloc.h"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <haible@clisp.cons.org>, 2001-2002.
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <bruno@clisp.org>, 2003.
This program is free software: you can redistribute it and/or modify
#include <string.h>
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <andy@knitter.ch>, 2010
It's based on x-python from Bruno Haible.
#include <string.h>
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <haible@clisp.cons.org>, 2001.
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <haible@clisp.cons.org>, 2001.
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <lubomirr@lubomirr.eu>, 2012.
#include <stdlib.h>
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
+
#ifdef __cplusplus
extern "C"
{
#endif
+
#define EXTENSIONS_LUA \
{ "lua", "Lua" }, \
extern void init_flag_table_lua (void);
+
#ifdef __cplusplus
}
#endif
/* 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 <guido@imperia.net>, 2002-2010.
#include <string.h>
#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 <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <bruno@clisp.org>, 2002.
#include <stdlib.h>
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <millerp@canb.auug.org.au>
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <haible@clisp.cons.org>, 2002.
#include <string.h>
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <haible@clisp.cons.org>, 2001.
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <bruno@clisp.org>, 2004-2005.
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <bruno@clisp.org>, 2003.
This program is free software: you can redistribute it and/or modify
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <haible@clisp.cons.org>, 2002.
#include "message.h"
#include "xgettext.h"
+#include "xg-message.h"
#include "error.h"
#include "xalloc.h"
#include "gettext.h"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <haible@clisp.cons.org>, 2002.
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
#include <string.h>
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
/* 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 <haible@clisp.cons.org>, 2001.
#include <stdlib.h>
#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"
#include <stdio.h>
#include "message.h"
-#include "xgettext.h"
+#include "xg-arglist-context.h"
#ifdef __cplusplus
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "xg-arglist-callshape.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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);
+ }
+ }
+}
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#ifndef _XGETTEXT_ARGLIST_CALLSHAPE_H
+#define _XGETTEXT_ARGLIST_CALLSHAPE_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#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 */
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "xg-arglist-context.h"
+
+#include <stdlib.h>
+
+#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;
+ }
+ }
+ }
+}
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#ifndef _XGETTEXT_ARGLIST_CONTEXT_H
+#define _XGETTEXT_ARGLIST_CONTEXT_H
+
+#include <stdbool.h>
+
+#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 */
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "xg-arglist-parser.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#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);
+}
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#ifndef _XGETTEXT_ARGLIST_PARSER_H
+#define _XGETTEXT_ARGLIST_PARSER_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#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 */
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "xg-encoding.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#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;
+}
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#ifndef _XGETTEXT_ENCODING_H
+#define _XGETTEXT_ENCODING_H
+
+#include <stddef.h>
+
+#if HAVE_ICONV
+#include <iconv.h>
+#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 */
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "xg-message.h"
+
+#include <stdio.h>
+
+#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 ();
+}
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#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 */
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "xg-mixed-string.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#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;
+}
--- /dev/null
+/* 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 <https://www.gnu.org/licenses/>. */
+
+#ifndef _XGETTEXT_MIXED_STRING_H
+#define _XGETTEXT_MIXED_STRING_H
+
+#include <stddef.h>
+
+#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 */
#endif
#include <alloca.h>
+/* Specification. */
+#include "xgettext.h"
+
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <locale.h>
#include <limits.h>
-#include "xgettext.h"
+#if HAVE_ICONV
+#include <iconv.h>
+#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"
#include "basename.h"
#include "xerror.h"
#include "xvasprintf.h"
-#include "xsize.h"
#include "xalloc.h"
#include "xmalloca.h"
#include "c-strstr.h"
#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"
#include "format.h"
#include "propername.h"
#include "sentence.h"
-#include "unistr.h"
#include "its.h"
#include "locating-rule.h"
#include "search-path.h"
#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;
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;
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;
}
-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,
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)
{
string_list_append (comment, str);
}
-static const char *
+const char *
xgettext_comment (size_t n)
{
if (comment == NULL || n >= comment->nitems)
return comment->item[n];
}
-static void
-xgettext_comment_reset ()
+void
+xgettext_comment_reset (void)
{
if (comment != NULL)
{
savable_comment = NULL;
}
-static void
+void
savable_comment_to_xgettext_comment (refcounted_string_list_ty *rslp)
{
xgettext_comment_reset ();
/* 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
}
-
-/* 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;
}
#include <stdbool.h>
#include <stddef.h>
-#include <stdlib.h>
-
-#if HAVE_ICONV
-#include <iconv.h>
-#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.
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