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