• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500-V1.0.1.40_1.0.68/ap/gpl/timemachine/gettext-0.17/gettext-tools/src/
1/* Perl brace format strings.
2   Copyright (C) 2004, 2006-2007 Free Software Foundation, Inc.
3   Written by Bruno Haible <bruno@clisp.org>, 2003.
4
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.
9
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.
14
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/>.  */
17
18#ifdef HAVE_CONFIG_H
19# include <config.h>
20#endif
21
22#include <stdbool.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include "format.h"
27#include "xalloc.h"
28#include "gettext.h"
29
30#define _(str) gettext (str)
31
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 '}'.
38 */
39
40struct named_arg
41{
42  char *name;
43};
44
45struct spec
46{
47  unsigned int directives;
48  unsigned int named_arg_count;
49  unsigned int allocated;
50  struct named_arg *named;
51};
52
53
54static int
55named_arg_compare (const void *p1, const void *p2)
56{
57  return strcmp (((const struct named_arg *) p1)->name,
58		 ((const struct named_arg *) p2)->name);
59}
60
61static void *
62format_parse (const char *format, bool translated, char *fdi,
63	      char **invalid_reason)
64{
65  const char *const format_start = format;
66  struct spec spec;
67  struct spec *result;
68
69  spec.directives = 0;
70  spec.named_arg_count = 0;
71  spec.allocated = 0;
72  spec.named = NULL;
73
74  for (; *format != '\0';)
75    if (*format++ == '{')
76      {
77	const char *f = format;
78	char c;
79
80	c = *f;
81	if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
82	  {
83	    do
84	      c = *++f;
85	    while ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'
86		   || (c >= '0' && c <= '9'));
87	    if (c == '}')
88	      {
89		/* A directive.  */
90		char *name;
91		const char *name_start = format;
92		const char *name_end = f;
93		size_t n = name_end - name_start;
94
95		FDI_SET (format - 1, FMTDIR_START);
96
97		name = XNMALLOC (n + 1, char);
98		memcpy (name, name_start, n);
99		name[n] = '\0';
100
101		spec.directives++;
102
103		if (spec.allocated == spec.named_arg_count)
104		  {
105		    spec.allocated = 2 * spec.allocated + 1;
106		    spec.named = (struct named_arg *) xrealloc (spec.named, spec.allocated * sizeof (struct named_arg));
107		  }
108		spec.named[spec.named_arg_count].name = name;
109		spec.named_arg_count++;
110
111		FDI_SET (f, FMTDIR_END);
112
113		format = ++f;
114	      }
115	  }
116      }
117
118  /* Sort the named argument array, and eliminate duplicates.  */
119  if (spec.named_arg_count > 1)
120    {
121      unsigned int i, j;
122
123      qsort (spec.named, spec.named_arg_count, sizeof (struct named_arg),
124	     named_arg_compare);
125
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);
130	else
131	  {
132	    if (j < i)
133	      spec.named[j].name = spec.named[i].name;
134	    j++;
135	  }
136      spec.named_arg_count = j;
137    }
138
139  result = XMALLOC (struct spec);
140  *result = spec;
141  return result;
142}
143
144static void
145format_free (void *descr)
146{
147  struct spec *spec = (struct spec *) descr;
148
149  if (spec->named != NULL)
150    {
151      unsigned int i;
152      for (i = 0; i < spec->named_arg_count; i++)
153	free (spec->named[i].name);
154      free (spec->named);
155    }
156  free (spec);
157}
158
159static int
160format_get_number_of_directives (void *descr)
161{
162  struct spec *spec = (struct spec *) descr;
163
164  return spec->directives;
165}
166
167static bool
168format_check (void *msgid_descr, void *msgstr_descr, bool equality,
169	      formatstring_error_logger_t error_logger,
170	      const char *pretty_msgstr)
171{
172  struct spec *spec1 = (struct spec *) msgid_descr;
173  struct spec *spec2 = (struct spec *) msgstr_descr;
174  bool err = false;
175
176  if (spec1->named_arg_count + spec2->named_arg_count > 0)
177    {
178      unsigned int i, j;
179      unsigned int n1 = spec1->named_arg_count;
180      unsigned int n2 = spec2->named_arg_count;
181
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; )
187	{
188	  int cmp = (i >= n1 ? 1 :
189		     j >= n2 ? -1 :
190		     strcmp (spec1->named[i].name, spec2->named[j].name));
191
192	  if (cmp > 0)
193	    j++;
194	  else if (cmp < 0)
195	    {
196	      if (equality)
197		{
198		  if (error_logger)
199		    error_logger (_("a format specification for argument '%s' doesn't exist in '%s'"),
200				  spec1->named[i].name, pretty_msgstr);
201		  err = true;
202		  break;
203		}
204	      else
205		i++;
206	    }
207	  else
208	    j++, i++;
209	}
210    }
211
212  return err;
213}
214
215
216struct formatstring_parser formatstring_perl_brace =
217{
218  format_parse,
219  format_free,
220  format_get_number_of_directives,
221  NULL,
222  format_check
223};
224
225
226#ifdef TEST
227
228/* Test program: Print the argument list specification returned by
229   format_parse for strings read from standard input.  */
230
231#include <stdio.h>
232
233static void
234format_print (void *descr)
235{
236  struct spec *spec = (struct spec *) descr;
237  unsigned int i;
238
239  if (spec == NULL)
240    {
241      printf ("INVALID");
242      return;
243    }
244
245  printf ("{");
246  for (i = 0; i < spec->named_arg_count; i++)
247    {
248      if (i > 0)
249	printf (", ");
250      printf ("'%s'", spec->named[i].name);
251    }
252  printf ("}");
253}
254
255int
256main ()
257{
258  for (;;)
259    {
260      char *line = NULL;
261      size_t line_size = 0;
262      int line_len;
263      char *invalid_reason;
264      void *descr;
265
266      line_len = getline (&line, &line_size, stdin);
267      if (line_len < 0)
268	break;
269      if (line_len > 0 && line[line_len - 1] == '\n')
270	line[--line_len] = '\0';
271
272      invalid_reason = NULL;
273      descr = format_parse (line, false, NULL, &invalid_reason);
274
275      format_print (descr);
276      printf ("\n");
277      if (descr == NULL)
278	printf ("%s\n", invalid_reason);
279
280      free (invalid_reason);
281      free (line);
282    }
283
284  return 0;
285}
286
287/*
288 * For Emacs M-x compile
289 * Local Variables:
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"
291 * End:
292 */
293
294#endif /* TEST */
295