• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/gettext-0.17/gettext-tools/src/
1/* librep format strings.
2   Copyright (C) 2001-2004, 2006-2007 Free Software Foundation, Inc.
3   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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
25#include "format.h"
26#include "c-ctype.h"
27#include "xalloc.h"
28#include "xvasprintf.h"
29#include "format-invalid.h"
30#include "gettext.h"
31
32#define _(str) gettext (str)
33
34/* librep format strings are implemented in librep-0.14/src/streams.c.
35   A directive
36   - starts with '%' or '%m$' where m is a positive integer,
37   - is optionally followed by any of the characters '-', '^', '0', '+', ' ',
38     each of which acts as a flag,
39   - is optionally followed by a width specification: a nonempty digit
40     sequence,
41   - is optionally followed by '.' and a precision specification: a nonempty
42     digit sequence,
43   - is finished by a specifier
44       - '%', that needs no argument,
45       - 'c', that need a character argument,
46       - 'd', 'x', 'X', 'o', that need an integer argument,
47       - 's', that need an argument and prints it using princ,
48       - 'S', that need an argument and prints it using prin1.
49   Numbered ('%m$') and unnumbered argument specifications can be used in the
50   same string. The effect of '%m$' is to set the current argument number to
51   m. The current argument number is incremented after processing a directive.
52 */
53
54enum format_arg_type
55{
56  FAT_NONE,
57  FAT_CHARACTER,
58  FAT_INTEGER,
59  FAT_OBJECT_PRETTY,
60  FAT_OBJECT
61};
62
63struct numbered_arg
64{
65  unsigned int number;
66  enum format_arg_type type;
67};
68
69struct spec
70{
71  unsigned int directives;
72  unsigned int numbered_arg_count;
73  unsigned int allocated;
74  struct numbered_arg *numbered;
75};
76
77/* Locale independent test for a decimal digit.
78   Argument can be  'char' or 'unsigned char'.  (Whereas the argument of
79   <ctype.h> isdigit must be an 'unsigned char'.)  */
80#undef isdigit
81#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
82
83
84static int
85numbered_arg_compare (const void *p1, const void *p2)
86{
87  unsigned int n1 = ((const struct numbered_arg *) p1)->number;
88  unsigned int n2 = ((const struct numbered_arg *) p2)->number;
89
90  return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
91}
92
93static void *
94format_parse (const char *format, bool translated, char *fdi,
95	      char **invalid_reason)
96{
97  const char *const format_start = format;
98  struct spec spec;
99  struct spec *result;
100  unsigned int number;
101
102  spec.directives = 0;
103  spec.numbered_arg_count = 0;
104  spec.allocated = 0;
105  spec.numbered = NULL;
106  number = 1;
107
108  for (; *format != '\0';)
109    if (*format++ == '%')
110      {
111	/* A directive.  */
112	enum format_arg_type type;
113
114	FDI_SET (format - 1, FMTDIR_START);
115	spec.directives++;
116
117	if (isdigit (*format))
118	  {
119	    const char *f = format;
120	    unsigned int m = 0;
121
122	    do
123	      {
124		m = 10 * m + (*f - '0');
125		f++;
126	      }
127	    while (isdigit (*f));
128
129	    if (*f == '$' && m > 0)
130	      {
131		number = m;
132		format = ++f;
133	      }
134	  }
135
136	/* Parse flags.  */
137	while (*format == '-' || *format == '^' || *format == '0'
138	       || *format == '+' || *format == ' ')
139	  format++;
140
141	/* Parse width.  */
142	if (isdigit (*format))
143	  {
144	    do format++; while (isdigit (*format));
145	  }
146
147	/* Parse precision.  */
148	if (*format == '.')
149	  {
150	    format++;
151
152	    if (isdigit (*format))
153	      {
154		do format++; while (isdigit (*format));
155	      }
156	  }
157
158	switch (*format)
159	  {
160	  case '%':
161	    type = FAT_NONE;
162	    break;
163	  case 'c':
164	    type = FAT_CHARACTER;
165	    break;
166	  case 'd': case 'x': case 'X': case 'o':
167	    type = FAT_INTEGER;
168	    break;
169	  case 's':
170	    type = FAT_OBJECT_PRETTY;
171	    break;
172	  case 'S':
173	    type = FAT_OBJECT;
174	    break;
175	  default:
176	    if (*format == '\0')
177	      {
178		*invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
179		FDI_SET (format - 1, FMTDIR_ERROR);
180	      }
181	    else
182	      {
183		*invalid_reason =
184		  INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
185		FDI_SET (format, FMTDIR_ERROR);
186	      }
187	    goto bad_format;
188	  }
189
190	if (type != FAT_NONE)
191	  {
192	    if (spec.allocated == spec.numbered_arg_count)
193	      {
194		spec.allocated = 2 * spec.allocated + 1;
195		spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
196	      }
197	    spec.numbered[spec.numbered_arg_count].number = number;
198	    spec.numbered[spec.numbered_arg_count].type = type;
199	    spec.numbered_arg_count++;
200
201	    number++;
202	  }
203
204	FDI_SET (format, FMTDIR_END);
205
206	format++;
207      }
208
209  /* Sort the numbered argument array, and eliminate duplicates.  */
210  if (spec.numbered_arg_count > 1)
211    {
212      unsigned int i, j;
213      bool err;
214
215      qsort (spec.numbered, spec.numbered_arg_count,
216	     sizeof (struct numbered_arg), numbered_arg_compare);
217
218      /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
219      err = false;
220      for (i = j = 0; i < spec.numbered_arg_count; i++)
221	if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
222	  {
223	    enum format_arg_type type1 = spec.numbered[i].type;
224	    enum format_arg_type type2 = spec.numbered[j-1].type;
225	    enum format_arg_type type_both;
226
227	    if (type1 == type2)
228	      type_both = type1;
229	    else
230	      {
231		/* Incompatible types.  */
232		type_both = FAT_NONE;
233		if (!err)
234		  *invalid_reason =
235		    INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
236		err = true;
237	      }
238
239	    spec.numbered[j-1].type = type_both;
240	  }
241	else
242	  {
243	    if (j < i)
244	      {
245		spec.numbered[j].number = spec.numbered[i].number;
246		spec.numbered[j].type = spec.numbered[i].type;
247	      }
248	    j++;
249	  }
250      spec.numbered_arg_count = j;
251      if (err)
252	/* *invalid_reason has already been set above.  */
253	goto bad_format;
254    }
255
256  result = XMALLOC (struct spec);
257  *result = spec;
258  return result;
259
260 bad_format:
261  if (spec.numbered != NULL)
262    free (spec.numbered);
263  return NULL;
264}
265
266static void
267format_free (void *descr)
268{
269  struct spec *spec = (struct spec *) descr;
270
271  if (spec->numbered != NULL)
272    free (spec->numbered);
273  free (spec);
274}
275
276static int
277format_get_number_of_directives (void *descr)
278{
279  struct spec *spec = (struct spec *) descr;
280
281  return spec->directives;
282}
283
284static bool
285format_check (void *msgid_descr, void *msgstr_descr, bool equality,
286	      formatstring_error_logger_t error_logger,
287	      const char *pretty_msgstr)
288{
289  struct spec *spec1 = (struct spec *) msgid_descr;
290  struct spec *spec2 = (struct spec *) msgstr_descr;
291  bool err = false;
292
293  if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
294    {
295      unsigned int i, j;
296      unsigned int n1 = spec1->numbered_arg_count;
297      unsigned int n2 = spec2->numbered_arg_count;
298
299      /* Check the argument names are the same.
300	 Both arrays are sorted.  We search for the first difference.  */
301      for (i = 0, j = 0; i < n1 || j < n2; )
302	{
303	  int cmp = (i >= n1 ? 1 :
304		     j >= n2 ? -1 :
305		     spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
306		     spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
307		     0);
308
309	  if (cmp > 0)
310	    {
311	      if (error_logger)
312		error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"),
313			      spec2->numbered[j].number, pretty_msgstr);
314	      err = true;
315	      break;
316	    }
317	  else if (cmp < 0)
318	    {
319	      if (equality)
320		{
321		  if (error_logger)
322		    error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
323				  spec1->numbered[i].number, pretty_msgstr);
324		  err = true;
325		  break;
326		}
327	      else
328		i++;
329	    }
330	  else
331	    j++, i++;
332	}
333      /* Check the argument types are the same.  */
334      if (!err)
335	for (i = 0, j = 0; j < n2; )
336	  {
337	    if (spec1->numbered[i].number == spec2->numbered[j].number)
338	      {
339		if (spec1->numbered[i].type != spec2->numbered[j].type)
340		  {
341		    if (error_logger)
342		      error_logger (_("format specifications in 'msgid' and '%s' for argument %u are not the same"),
343				    pretty_msgstr, spec2->numbered[j].number);
344		    err = true;
345		    break;
346		  }
347		j++, i++;
348	      }
349	    else
350	      i++;
351	  }
352    }
353
354  return err;
355}
356
357
358struct formatstring_parser formatstring_librep =
359{
360  format_parse,
361  format_free,
362  format_get_number_of_directives,
363  NULL,
364  format_check
365};
366
367
368#ifdef TEST
369
370/* Test program: Print the argument list specification returned by
371   format_parse for strings read from standard input.  */
372
373#include <stdio.h>
374
375static void
376format_print (void *descr)
377{
378  struct spec *spec = (struct spec *) descr;
379  unsigned int last;
380  unsigned int i;
381
382  if (spec == NULL)
383    {
384      printf ("INVALID");
385      return;
386    }
387
388  printf ("(");
389  last = 1;
390  for (i = 0; i < spec->numbered_arg_count; i++)
391    {
392      unsigned int number = spec->numbered[i].number;
393
394      if (i > 0)
395	printf (" ");
396      if (number < last)
397	abort ();
398      for (; last < number; last++)
399	printf ("_ ");
400      switch (spec->numbered[i].type)
401	{
402	case FAT_CHARACTER:
403	  printf ("c");
404	  break;
405	case FAT_INTEGER:
406	  printf ("i");
407	  break;
408	case FAT_OBJECT_PRETTY:
409	  printf ("s");
410	  break;
411	case FAT_OBJECT:
412	  printf ("*");
413	  break;
414	default:
415	  abort ();
416	}
417      last = number + 1;
418    }
419  printf (")");
420}
421
422int
423main ()
424{
425  for (;;)
426    {
427      char *line = NULL;
428      size_t line_size = 0;
429      int line_len;
430      char *invalid_reason;
431      void *descr;
432
433      line_len = getline (&line, &line_size, stdin);
434      if (line_len < 0)
435	break;
436      if (line_len > 0 && line[line_len - 1] == '\n')
437	line[--line_len] = '\0';
438
439      invalid_reason = NULL;
440      descr = format_parse (line, false, NULL, &invalid_reason);
441
442      format_print (descr);
443      printf ("\n");
444      if (descr == NULL)
445	printf ("%s\n", invalid_reason);
446
447      free (invalid_reason);
448      free (line);
449    }
450
451  return 0;
452}
453
454/*
455 * For Emacs M-x compile
456 * Local Variables:
457 * 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-librep.c ../gnulib-lib/libgettextlib.la"
458 * End:
459 */
460
461#endif /* TEST */
462