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