1 /* Perl brace format strings.
2 Copyright (C) 2004, 2006-2007, 2015 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
30 #define _(str) gettext (str)
32 /* Perl brace format strings are supported by Guido Flohr's libintl-perl
33 package, more precisely by the __expand and __x functions therein.
34 A format string directive here consists of
35 - an opening brace '{',
36 - an identifier [_A-Za-z][_0-9A-Za-z]*,
37 - a closing brace '}'.
47 unsigned int directives;
48 unsigned int named_arg_count;
49 unsigned int allocated;
50 struct named_arg *named;
55 named_arg_compare (const void *p1, const void *p2)
57 return strcmp (((const struct named_arg *) p1)->name,
58 ((const struct named_arg *) p2)->name);
62 format_parse (const char *format, bool translated, char *fdi,
63 char **invalid_reason)
65 const char *const format_start = format;
70 spec.named_arg_count = 0;
74 for (; *format != '\0';)
77 const char *f = format;
81 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
85 while ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'
86 || (c >= '0' && c <= '9'));
91 const char *name_start = format;
92 const char *name_end = f;
93 size_t n = name_end - name_start;
95 FDI_SET (format - 1, FMTDIR_START);
97 name = XNMALLOC (n + 1, char);
98 memcpy (name, name_start, n);
103 if (spec.allocated == spec.named_arg_count)
105 spec.allocated = 2 * spec.allocated + 1;
106 spec.named = (struct named_arg *) xrealloc (spec.named, spec.allocated * sizeof (struct named_arg));
108 spec.named[spec.named_arg_count].name = name;
109 spec.named_arg_count++;
111 FDI_SET (f, FMTDIR_END);
118 /* Sort the named argument array, and eliminate duplicates. */
119 if (spec.named_arg_count > 1)
123 qsort (spec.named, spec.named_arg_count, sizeof (struct named_arg),
126 /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
127 for (i = j = 0; i < spec.named_arg_count; i++)
128 if (j > 0 && strcmp (spec.named[i].name, spec.named[j-1].name) == 0)
129 free (spec.named[i].name);
133 spec.named[j].name = spec.named[i].name;
136 spec.named_arg_count = j;
139 result = XMALLOC (struct spec);
145 format_free (void *descr)
147 struct spec *spec = (struct spec *) descr;
149 if (spec->named != NULL)
152 for (i = 0; i < spec->named_arg_count; i++)
153 free (spec->named[i].name);
160 format_get_number_of_directives (void *descr)
162 struct spec *spec = (struct spec *) descr;
164 return spec->directives;
168 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
169 formatstring_error_logger_t error_logger,
170 const char *pretty_msgid, const char *pretty_msgstr)
172 struct spec *spec1 = (struct spec *) msgid_descr;
173 struct spec *spec2 = (struct spec *) msgstr_descr;
176 if (spec1->named_arg_count + spec2->named_arg_count > 0)
179 unsigned int n1 = spec1->named_arg_count;
180 unsigned int n2 = spec2->named_arg_count;
182 /* Check the argument names in spec1 are contained in those of spec2.
183 Additional arguments in spec2 are allowed; they expand to themselves
184 (including the surrounding braces) at runtime.
185 Both arrays are sorted. We search for the differences. */
186 for (i = 0, j = 0; i < n1 || j < n2; )
188 int cmp = (i >= n1 ? 1 :
190 strcmp (spec1->named[i].name, spec2->named[j].name));
199 error_logger (_("a format specification for argument '%s' doesn't exist in '%s'"),
200 spec1->named[i].name, pretty_msgstr);
216 struct formatstring_parser formatstring_perl_brace =
220 format_get_number_of_directives,
228 /* Test program: Print the argument list specification returned by
229 format_parse for strings read from standard input. */
234 format_print (void *descr)
236 struct spec *spec = (struct spec *) descr;
246 for (i = 0; i < spec->named_arg_count; i++)
250 printf ("'%s'", spec->named[i].name);
261 size_t line_size = 0;
263 char *invalid_reason;
266 line_len = getline (&line, &line_size, stdin);
269 if (line_len > 0 && line[line_len - 1] == '\n')
270 line[--line_len] = '\0';
272 invalid_reason = NULL;
273 descr = format_parse (line, false, NULL, &invalid_reason);
275 format_print (descr);
278 printf ("%s\n", invalid_reason);
280 free (invalid_reason);
288 * For Emacs M-x compile
290 * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-perl-brace.c ../gnulib-lib/libgettextlib.la"