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