xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/src/format-pascal.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
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 
66 enum 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 
75 struct numbered_arg
76 {
77   unsigned int number;
78   enum format_arg_type type;
79 };
80 
81 struct 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 
96 static int
numbered_arg_compare(const void * p1,const void * p2)97 numbered_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 
105 static void *
format_parse(const char * format,bool translated,char ** invalid_reason)106 format_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 
344 static void
format_free(void * descr)345 format_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 
354 static int
format_get_number_of_directives(void * descr)355 format_get_number_of_directives (void *descr)
356 {
357   struct spec *spec = (struct spec *) descr;
358 
359   return spec->directives;
360 }
361 
362 static bool
format_check(void * msgid_descr,void * msgstr_descr,bool equality,formatstring_error_logger_t error_logger,const char * pretty_msgstr)363 format_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 
436 struct 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 
454 static void
format_print(void * descr)455 format_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 
504 int
main()505 main ()
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