• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Ruby format strings.
2    Copyright (C) 2001-2004, 2006-2009, 2019-2020 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2020.
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 <https://www.gnu.org/licenses/>.  */
17 
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #include <limits.h>
23 #include <stdbool.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "format.h"
28 #include "c-ctype.h"
29 #include "xalloc.h"
30 #include "xvasprintf.h"
31 #include "format-invalid.h"
32 #include "gettext.h"
33 
34 #define _(str) gettext (str)
35 
36 /* Ruby format strings are described in
37    https://ruby-doc.org/core-2.7.1/Kernel.html#method-i-sprintf
38    and are implemented in ruby-2.7.1/sprintf.c .
39    A format string consists of literal text and directives.
40    A directive
41    - starts with '%',
42    - is optionally followed by a sequence of the following:
43      - any of the characters ' ', '#', '+', '-', '0', each of which acts as a
44        flag,
45      - a digit sequence starting with a non-zero digit, followed by '$', at
46        most once per directive, indicating a positional argument to consume,
47      - '<' KEY '>', at most once per directive, indicating a hash table element
48        to consume,
49      - a digit sequence starting with a non-zero digit, specifying a width,
50      - '*', indicating a width, taken from the argument list,
51      - '*' and a digit sequence, followed by '$', indicating a width, taken
52        from a positional argument,
53      - '.' and a digit sequence, indicating a precision,
54      - '.' '*', indicating a precision, taken from the argument list,
55      - '.' '*' and a digit sequence, followed by '$', indicating a precision,
56        taken from a positional argument.
57      This sequence is in any order, except that
58        - flags must occur before width and precision,
59        - width must occur before precision.
60    - is finished by a specifier
61      - 's', that takes an object to print without double-quote delimiters,
62      - 'p', that takes an object to print with double-quote delimiters (in case
63        of a string),
64      - '{' KEY '}', indicating a hash table element to consume and to print
65        like with 's',
66      - 'c', that takes a character,
67      - 'd', 'i', 'u', 'o', 'x', 'X', 'b', 'B', that take an integer,
68      - 'f', 'g', 'G', 'e', 'E', 'a', 'A', that take a floating-point number.
69    Additionally there are the directives '%%' '%<newline>', which take no
70    argument.
71    Numbered, unnumbered, and named argument specifications cannot be used in
72    the same string; either all arguments are numbered, or all arguments are
73    unnumbered, or all arguments are named.
74  */
75 
76 enum format_arg_type
77 {
78   FAT_NONE,
79   FAT_ANY,
80   FAT_ESCAPED_ANY,
81   FAT_CHARACTER,
82   FAT_INTEGER,
83   FAT_FLOAT
84 };
85 
86 struct named_arg
87 {
88   char *name;
89   enum format_arg_type type;
90 };
91 
92 struct numbered_arg
93 {
94   unsigned int number;
95   enum format_arg_type type;
96 };
97 
98 struct spec
99 {
100   unsigned int directives;
101   unsigned int named_arg_count;
102   unsigned int numbered_arg_count;
103   struct named_arg *named;
104   struct numbered_arg *numbered;
105 };
106 
107 /* Locale independent test for a decimal digit.
108    Argument can be  'char' or 'unsigned char'.  (Whereas the argument of
109    <ctype.h> isdigit must be an 'unsigned char'.)  */
110 #undef isdigit
111 #define isdigit(c) ((unsigned int) ((c) - '0') < 10)
112 
113 
114 static int
named_arg_compare(const void * p1,const void * p2)115 named_arg_compare (const void *p1, const void *p2)
116 {
117   return strcmp (((const struct named_arg *) p1)->name,
118                  ((const struct named_arg *) p2)->name);
119 }
120 
121 static int
numbered_arg_compare(const void * p1,const void * p2)122 numbered_arg_compare (const void *p1, const void *p2)
123 {
124   unsigned int n1 = ((const struct numbered_arg *) p1)->number;
125   unsigned int n2 = ((const struct numbered_arg *) p2)->number;
126 
127   return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
128 }
129 
130 #define INVALID_MIXES_NAMED_UNNAMED() \
131   xstrdup (_("The string refers to arguments both through argument names and through unnamed argument specifications."))
132 
133 #define INVALID_TWO_ARG_NAMES(directive_number) \
134   xasprintf (_("In the directive number %u, two names are given for the same argument."), directive_number)
135 
136 #define INVALID_TWO_ARG_NUMBERS(directive_number) \
137   xasprintf (_("In the directive number %u, two numbers are given for the same argument."), directive_number)
138 
139 #define INVALID_FLAG_AFTER_WIDTH(directive_number) \
140   xasprintf (_("In the directive number %u, a flag is given after the width."), directive_number)
141 
142 #define INVALID_FLAG_AFTER_PRECISION(directive_number) \
143   xasprintf (_("In the directive number %u, a flag is given after the precision."), directive_number)
144 
145 #define INVALID_WIDTH_AFTER_PRECISION(directive_number) \
146   xasprintf (_("In the directive number %u, the width is given after the precision."), directive_number)
147 
148 #define INVALID_WIDTH_TWICE(directive_number) \
149   xasprintf (_("In the directive number %u, a width is given twice."), directive_number)
150 
151 #define INVALID_PRECISION_TWICE(directive_number) \
152   xasprintf (_("In the directive number %u, a precision is given twice."), directive_number)
153 
154 static void *
format_parse(const char * format,bool translated,char * fdi,char ** invalid_reason)155 format_parse (const char *format, bool translated, char *fdi,
156               char **invalid_reason)
157 {
158   const char *const format_start = format;
159   struct spec spec;
160   unsigned int unnumbered_arg_count;
161   unsigned int allocated;
162   struct spec *result;
163 
164   spec.directives = 0;
165   spec.named_arg_count = 0;
166   spec.numbered_arg_count = 0;
167   spec.named = NULL;
168   spec.numbered = NULL;
169   unnumbered_arg_count = 0;
170   allocated = 0;
171 
172   for (; *format != '\0';)
173     if (*format++ == '%')
174       {
175         /* A directive.  */
176         char *name = NULL;
177         unsigned int number = 0;
178 
179         bool seen_width = false;
180         unsigned int width_number = 0;
181         bool width_takenext = false;
182 
183         bool seen_precision = false;
184         unsigned int precision_number = 0;
185         bool precision_takenext = false;
186 
187         enum format_arg_type type;
188 
189         FDI_SET (format - 1, FMTDIR_START);
190         spec.directives++;
191 
192         for (;;)
193           {
194             if (*format == ' '
195                 || *format == '#'
196                 || *format == '+'
197                 || *format == '-'
198                 || *format == '0')
199               {
200                 /* A flag.  */
201                 if (seen_width)
202                   {
203                     *invalid_reason = INVALID_FLAG_AFTER_WIDTH (spec.directives);
204                     FDI_SET (format, FMTDIR_ERROR);
205                     goto bad_format;
206                   }
207                 if (seen_precision)
208                   {
209                     *invalid_reason = INVALID_FLAG_AFTER_PRECISION (spec.directives);
210                     FDI_SET (format, FMTDIR_ERROR);
211                     goto bad_format;
212                   }
213                 format++;
214                 continue;
215               }
216 
217             if (*format == '<')
218               {
219                 const char *name_start;
220                 const char *name_end;
221                 size_t n;
222 
223                 if ((spec.numbered_arg_count > 0
224                      || number > 0 || width_number > 0 || precision_number > 0)
225                     || (unnumbered_arg_count > 0
226                         || width_takenext || precision_takenext))
227                   {
228                     *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
229                     FDI_SET (format, FMTDIR_ERROR);
230                     goto bad_format;
231                   }
232                 if (name != NULL)
233                   {
234                     *invalid_reason = INVALID_TWO_ARG_NAMES (spec.directives);
235                     FDI_SET (format, FMTDIR_ERROR);
236                     goto bad_format;
237                   }
238 
239                 name_start = ++format;
240                 for (; *format != '\0'; format++)
241                   if (*format == '>')
242                     break;
243                 if (*format == '\0')
244                   {
245                     *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
246                     FDI_SET (format - 1, FMTDIR_ERROR);
247                     goto bad_format;
248                   }
249                 name_end = format++;
250 
251                 n = name_end - name_start;
252                 name = XNMALLOC (n + 1, char);
253                 memcpy (name, name_start, n);
254                 name[n] = '\0';
255 
256                 continue;
257               }
258 
259             if (isdigit (*format))
260               {
261                 unsigned int m = 0;
262 
263                 do
264                   {
265                     if (m < UINT_MAX / 10)
266                       m = 10 * m + (*format - '0');
267                     else
268                       m = UINT_MAX - 1;
269                     format++;
270                   }
271                 while (isdigit (*format));
272 
273                 if (*format == '$')
274                   {
275                     if (spec.named_arg_count > 0 || name != NULL)
276                       {
277                         *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
278                         FDI_SET (format, FMTDIR_ERROR);
279                         goto bad_format;
280                       }
281                     if (unnumbered_arg_count > 0
282                         || width_takenext || precision_takenext)
283                       {
284                         *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
285                         FDI_SET (format, FMTDIR_ERROR);
286                         goto bad_format;
287                       }
288                     if (number > 0)
289                       {
290                         *invalid_reason = INVALID_TWO_ARG_NUMBERS (spec.directives);
291                         FDI_SET (format, FMTDIR_ERROR);
292                         goto bad_format;
293                       }
294                     number = m;
295                     format++;
296                   }
297                 else
298                   {
299                     /* Seen a constant width.  */
300                     if (seen_precision)
301                       {
302                         *invalid_reason = INVALID_WIDTH_AFTER_PRECISION (spec.directives);
303                         FDI_SET (format, FMTDIR_ERROR);
304                         goto bad_format;
305                       }
306                     if (seen_width)
307                       {
308                         *invalid_reason = INVALID_WIDTH_TWICE (spec.directives);
309                         FDI_SET (format, FMTDIR_ERROR);
310                         goto bad_format;
311                       }
312                     seen_width = true;
313                   }
314                 continue;
315               }
316 
317             if (*format == '*')
318               {
319                 /* Parse width.  */
320                 format++;
321 
322                 if (isdigit (*format))
323                   {
324                     const char *f = format;
325                     unsigned int m = 0;
326 
327                     do
328                       {
329                         if (m < UINT_MAX / 10)
330                           m = 10 * m + (*f - '0');
331                         else
332                           m = UINT_MAX - 1;
333                         f++;
334                       }
335                     while (isdigit (*f));
336 
337                     if (*f == '$')
338                       {
339                         format = f;
340                         if (spec.named_arg_count > 0 || name != NULL)
341                           {
342                             *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
343                             FDI_SET (format, FMTDIR_ERROR);
344                             goto bad_format;
345                           }
346                         if (unnumbered_arg_count > 0
347                             || width_takenext || precision_takenext)
348                           {
349                             *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
350                             FDI_SET (format, FMTDIR_ERROR);
351                             goto bad_format;
352                           }
353                         if (seen_precision)
354                           {
355                             *invalid_reason = INVALID_WIDTH_AFTER_PRECISION (spec.directives);
356                             FDI_SET (format, FMTDIR_ERROR);
357                             goto bad_format;
358                           }
359                         if (seen_width)
360                           {
361                             *invalid_reason = INVALID_WIDTH_TWICE (spec.directives);
362                             FDI_SET (format, FMTDIR_ERROR);
363                             goto bad_format;
364                           }
365                         if (m == 0)
366                           {
367                             *invalid_reason = INVALID_ARGNO_0 (spec.directives);
368                             FDI_SET (format, FMTDIR_ERROR);
369                             goto bad_format;
370                           }
371                         seen_width = true;
372                         width_number = m;
373                         format++;
374                         continue;
375                       }
376                   }
377 
378                 if (spec.named_arg_count > 0 || name != NULL)
379                   {
380                     *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
381                     FDI_SET (format - 1, FMTDIR_ERROR);
382                     goto bad_format;
383                   }
384                 if (spec.numbered_arg_count > 0
385                     || number > 0 || width_number > 0 || precision_number > 0)
386                   {
387                     *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
388                     FDI_SET (format - 1, FMTDIR_ERROR);
389                     goto bad_format;
390                   }
391                 if (seen_precision)
392                   {
393                     *invalid_reason = INVALID_WIDTH_AFTER_PRECISION (spec.directives);
394                     FDI_SET (format - 1, FMTDIR_ERROR);
395                     goto bad_format;
396                   }
397                 if (seen_width)
398                   {
399                     *invalid_reason = INVALID_WIDTH_TWICE (spec.directives);
400                     FDI_SET (format - 1, FMTDIR_ERROR);
401                     goto bad_format;
402                   }
403                 seen_width = true;
404                 width_takenext = true;
405                 continue;
406               }
407 
408             if (*format == '.')
409               {
410                 /* Parse precision.  */
411                 format++;
412 
413                 if (*format == '*')
414                   {
415                     format++;
416 
417                     if (isdigit (*format))
418                       {
419                         const char *f = format;
420                         unsigned int m = 0;
421 
422                         do
423                           {
424                             if (m < UINT_MAX / 10)
425                               m = 10 * m + (*f - '0');
426                             else
427                               m = UINT_MAX - 1;
428                             f++;
429                           }
430                         while (isdigit (*f));
431 
432                         if (*f == '$')
433                           {
434                             format = f;
435                             if (spec.named_arg_count > 0 || name != NULL)
436                               {
437                                 *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
438                                 FDI_SET (format, FMTDIR_ERROR);
439                                 goto bad_format;
440                               }
441                             if (unnumbered_arg_count > 0
442                                 || width_takenext || precision_takenext)
443                               {
444                                 *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
445                                 FDI_SET (format, FMTDIR_ERROR);
446                                 goto bad_format;
447                               }
448                             if (seen_precision)
449                               {
450                                 *invalid_reason = INVALID_PRECISION_TWICE (spec.directives);
451                                 FDI_SET (format, FMTDIR_ERROR);
452                                 goto bad_format;
453                               }
454                             if (m == 0)
455                               {
456                                 *invalid_reason = INVALID_ARGNO_0 (spec.directives);
457                                 FDI_SET (format, FMTDIR_ERROR);
458                                 goto bad_format;
459                               }
460                             seen_precision = true;
461                             precision_number = m;
462                             format++;
463                             continue;
464                           }
465                       }
466 
467                     if (spec.named_arg_count > 0 || name != NULL)
468                       {
469                         *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
470                         FDI_SET (format - 1, FMTDIR_ERROR);
471                         goto bad_format;
472                       }
473                     if (spec.numbered_arg_count > 0
474                         || number > 0 || width_number > 0 || precision_number > 0)
475                       {
476                         *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
477                         FDI_SET (format - 1, FMTDIR_ERROR);
478                         goto bad_format;
479                       }
480                     if (seen_precision)
481                       {
482                         *invalid_reason = INVALID_PRECISION_TWICE (spec.directives);
483                         FDI_SET (format - 1, FMTDIR_ERROR);
484                         goto bad_format;
485                       }
486                     seen_precision = true;
487                     precision_takenext = true;
488                     continue;
489                   }
490 
491                 while (isdigit (*format))
492                   format++;
493 
494                 /* Seen a constant precision.  */
495                 if (seen_precision)
496                   {
497                     *invalid_reason = INVALID_PRECISION_TWICE (spec.directives);
498                     FDI_SET (format, FMTDIR_ERROR);
499                     goto bad_format;
500                   }
501                 seen_precision = true;
502                 continue;
503               }
504 
505             break;
506           }
507 
508         switch (*format)
509           {
510           case '%':
511           case '\n':
512             type = FAT_NONE;
513             break;
514           case 's':
515             type = FAT_ANY;
516             break;
517           case 'p':
518             type = FAT_ESCAPED_ANY;
519             break;
520           case 'c':
521             type = FAT_CHARACTER;
522             break;
523           case 'd':
524           case 'i':
525           case 'u':
526           case 'o':
527           case 'x':
528           case 'X':
529           case 'b':
530           case 'B':
531             type = FAT_INTEGER;
532             break;
533           case 'f':
534           case 'g':
535           case 'G':
536           case 'e':
537           case 'E':
538           case 'a':
539           case 'A':
540             type = FAT_FLOAT;
541             break;
542           case '{':
543             {
544               const char *name_start;
545               const char *name_end;
546               size_t n;
547 
548               if ((spec.numbered_arg_count > 0
549                    || number > 0 || width_number > 0 || precision_number > 0)
550                   || (unnumbered_arg_count > 0
551                       || width_takenext || precision_takenext))
552                 {
553                   *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
554                   FDI_SET (format, FMTDIR_ERROR);
555                   goto bad_format;
556                 }
557               if (name != NULL)
558                 {
559                   *invalid_reason = INVALID_TWO_ARG_NAMES (spec.directives);
560                   FDI_SET (format, FMTDIR_ERROR);
561                   goto bad_format;
562                 }
563 
564               name_start = ++format;
565               for (; *format != '\0'; format++)
566                 if (*format == '}')
567                   break;
568               if (*format == '\0')
569                 {
570                   *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
571                   FDI_SET (format - 1, FMTDIR_ERROR);
572                   goto bad_format;
573                 }
574               name_end = format;
575 
576               n = name_end - name_start;
577               name = XNMALLOC (n + 1, char);
578               memcpy (name, name_start, n);
579               name[n] = '\0';
580             }
581             type = FAT_ANY;
582             break;
583 
584           default:
585             if (*format == '\0')
586               {
587                 *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
588                 FDI_SET (format - 1, FMTDIR_ERROR);
589               }
590             else
591               {
592                 *invalid_reason =
593                   INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
594                 FDI_SET (format, FMTDIR_ERROR);
595               }
596             goto bad_format;
597           }
598 
599         if (seen_width)
600           {
601             /* Register the argument specification for the width.  */
602             if (width_number > 0)
603               {
604                 if (allocated == spec.numbered_arg_count)
605                   {
606                     allocated = 2 * allocated + 1;
607                     spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, allocated * sizeof (struct numbered_arg));
608                   }
609                 spec.numbered[spec.numbered_arg_count].number = width_number;
610                 spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
611                 spec.numbered_arg_count++;
612               }
613             else if (width_takenext)
614               {
615                 if (allocated == unnumbered_arg_count)
616                   {
617                     allocated = 2 * allocated + 1;
618                     spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, allocated * sizeof (struct numbered_arg));
619                   }
620                 spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
621                 spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
622                 unnumbered_arg_count++;
623               }
624           }
625 
626         if (seen_precision)
627           {
628             /* Register the argument specification for the precision.  */
629             if (precision_number > 0)
630               {
631                 if (allocated == spec.numbered_arg_count)
632                   {
633                     allocated = 2 * allocated + 1;
634                     spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, allocated * sizeof (struct numbered_arg));
635                   }
636                 spec.numbered[spec.numbered_arg_count].number = precision_number;
637                 spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
638                 spec.numbered_arg_count++;
639               }
640             else if (precision_takenext)
641               {
642                 if (allocated == unnumbered_arg_count)
643                   {
644                     allocated = 2 * allocated + 1;
645                     spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, allocated * sizeof (struct numbered_arg));
646                   }
647                 spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
648                 spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
649                 unnumbered_arg_count++;
650               }
651           }
652 
653         if (type != FAT_NONE)
654           {
655             /* Register the argument specification for the value.  */
656             if (name != NULL)
657               {
658                 if (allocated == spec.named_arg_count)
659                   {
660                     allocated = 2 * allocated + 1;
661                     spec.named = (struct named_arg *) xrealloc (spec.named, allocated * sizeof (struct named_arg));
662                   }
663                 spec.named[spec.named_arg_count].name = name;
664                 spec.named[spec.named_arg_count].type = type;
665                 spec.named_arg_count++;
666               }
667             else if (number > 0)
668               {
669                 if (allocated == spec.numbered_arg_count)
670                   {
671                     allocated = 2 * allocated + 1;
672                     spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, allocated * sizeof (struct numbered_arg));
673                   }
674                 spec.numbered[spec.numbered_arg_count].number = number;
675                 spec.numbered[spec.numbered_arg_count].type = type;
676                 spec.numbered_arg_count++;
677               }
678             else
679               {
680                 if (spec.named_arg_count > 0)
681                   {
682                     *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
683                     FDI_SET (format, FMTDIR_ERROR);
684                     goto bad_format;
685                   }
686                 if (spec.numbered_arg_count > 0)
687                   {
688                     *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
689                     FDI_SET (format, FMTDIR_ERROR);
690                     goto bad_format;
691                   }
692                 if (allocated == unnumbered_arg_count)
693                   {
694                     allocated = 2 * allocated + 1;
695                     spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, allocated * sizeof (struct numbered_arg));
696                   }
697                 spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
698                 spec.numbered[unnumbered_arg_count].type = type;
699                 unnumbered_arg_count++;
700               }
701           }
702 
703         FDI_SET (format, FMTDIR_END);
704 
705         format++;
706       }
707 
708   /* Verify that either all arguments are numbered, or all arguments are
709      unnumbered, or all arguments are named.  */
710   if ((spec.numbered_arg_count > 0)
711       + (unnumbered_arg_count > 0)
712       + (spec.named_arg_count > 0)
713       > 1)
714     abort ();
715 
716   /* Convert the unnumbered argument array to numbered arguments.  */
717   if (unnumbered_arg_count > 0)
718     spec.numbered_arg_count = unnumbered_arg_count;
719   /* Sort the numbered argument array, and eliminate duplicates.  */
720   else if (spec.numbered_arg_count > 1)
721     {
722       unsigned int i, j;
723       bool err;
724 
725       qsort (spec.numbered, spec.numbered_arg_count,
726              sizeof (struct numbered_arg), numbered_arg_compare);
727 
728       /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
729       err = false;
730       for (i = j = 0; i < spec.numbered_arg_count; i++)
731         if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
732           {
733             enum format_arg_type type1 = spec.numbered[i].type;
734             enum format_arg_type type2 = spec.numbered[j-1].type;
735             enum format_arg_type type_both;
736 
737             if (type1 == type2)
738               type_both = type1;
739             else
740               {
741                 /* Incompatible types.  */
742                 type_both = FAT_NONE;
743                 if (!err)
744                   *invalid_reason =
745                     INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
746                 err = true;
747               }
748 
749             spec.numbered[j-1].type = type_both;
750           }
751         else
752           {
753             if (j < i)
754               {
755                 spec.numbered[j].number = spec.numbered[i].number;
756                 spec.numbered[j].type = spec.numbered[i].type;
757               }
758             j++;
759           }
760       spec.numbered_arg_count = j;
761       if (err)
762         /* *invalid_reason has already been set above.  */
763         goto bad_format;
764     }
765 
766   /* Sort the named argument array, and eliminate duplicates.  */
767   if (spec.named_arg_count > 1)
768     {
769       unsigned int i, j;
770       bool err;
771 
772       qsort (spec.named, spec.named_arg_count, sizeof (struct named_arg),
773              named_arg_compare);
774 
775       /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
776       err = false;
777       for (i = j = 0; i < spec.named_arg_count; i++)
778         if (j > 0 && strcmp (spec.named[i].name, spec.named[j-1].name) == 0)
779           {
780             enum format_arg_type type1 = spec.named[i].type;
781             enum format_arg_type type2 = spec.named[j-1].type;
782             enum format_arg_type type_both;
783 
784             if (type1 == type2)
785               type_both = type1;
786             else
787               {
788                 /* Incompatible types.  */
789                 type_both = FAT_NONE;
790                 if (!err)
791                   *invalid_reason =
792                     xasprintf (_("The string refers to the argument named '%s' in incompatible ways."), spec.named[i].name);
793                 err = true;
794               }
795 
796             spec.named[j-1].type = type_both;
797             free (spec.named[i].name);
798           }
799         else
800           {
801             if (j < i)
802               {
803                 spec.named[j].name = spec.named[i].name;
804                 spec.named[j].type = spec.named[i].type;
805               }
806             j++;
807           }
808       spec.named_arg_count = j;
809       if (err)
810         /* *invalid_reason has already been set above.  */
811         goto bad_format;
812     }
813 
814   result = XMALLOC (struct spec);
815   *result = spec;
816   return result;
817 
818  bad_format:
819   if (spec.named != NULL)
820     {
821       unsigned int i;
822       for (i = 0; i < spec.named_arg_count; i++)
823         free (spec.named[i].name);
824       free (spec.named);
825     }
826   if (spec.numbered != NULL)
827     free (spec.numbered);
828   return NULL;
829 }
830 
831 static void
format_free(void * descr)832 format_free (void *descr)
833 {
834   struct spec *spec = (struct spec *) descr;
835 
836   if (spec->named != NULL)
837     {
838       unsigned int i;
839       for (i = 0; i < spec->named_arg_count; i++)
840         free (spec->named[i].name);
841       free (spec->named);
842     }
843   if (spec->numbered != NULL)
844     free (spec->numbered);
845   free (spec);
846 }
847 
848 static int
format_get_number_of_directives(void * descr)849 format_get_number_of_directives (void *descr)
850 {
851   struct spec *spec = (struct spec *) descr;
852 
853   return spec->directives;
854 }
855 
856 static bool
format_check(void * msgid_descr,void * msgstr_descr,bool equality,formatstring_error_logger_t error_logger,const char * pretty_msgid,const char * pretty_msgstr)857 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
858               formatstring_error_logger_t error_logger,
859               const char *pretty_msgid, const char *pretty_msgstr)
860 {
861   struct spec *spec1 = (struct spec *) msgid_descr;
862   struct spec *spec2 = (struct spec *) msgstr_descr;
863   bool err = false;
864 
865   if (spec1->named_arg_count > 0 && spec2->numbered_arg_count > 0)
866     {
867       if (error_logger)
868         error_logger (_("format specifications in '%s' expect a hash table, those in '%s' expect individual arguments"),
869                       pretty_msgid, pretty_msgstr);
870       err = true;
871     }
872   else if (spec1->numbered_arg_count > 0 && spec2->named_arg_count > 0)
873     {
874       if (error_logger)
875         error_logger (_("format specifications in '%s' expect individual arguments, those in '%s' expect a hash table"),
876                       pretty_msgid, pretty_msgstr);
877       err = true;
878     }
879   else
880     {
881       if (spec1->named_arg_count + spec2->named_arg_count > 0)
882         {
883           unsigned int i, j;
884           unsigned int n1 = spec1->named_arg_count;
885           unsigned int n2 = spec2->named_arg_count;
886 
887           /* Check the argument names are the same.
888              Both arrays are sorted.  We search for the first difference.  */
889           for (i = 0, j = 0; i < n1 || j < n2; )
890             {
891               int cmp = (i >= n1 ? 1 :
892                          j >= n2 ? -1 :
893                          strcmp (spec1->named[i].name, spec2->named[j].name));
894 
895               if (cmp > 0)
896                 {
897                   if (error_logger)
898                     error_logger (_("a format specification for argument '%s', as in '%s', doesn't exist in '%s'"),
899                                   spec2->named[j].name, pretty_msgstr,
900                                   pretty_msgid);
901                   err = true;
902                   break;
903                 }
904               else if (cmp < 0)
905                 {
906                   if (equality)
907                     {
908                       if (error_logger)
909                         error_logger (_("a format specification for argument '%s' doesn't exist in '%s'"),
910                                       spec1->named[i].name, pretty_msgstr);
911                       err = true;
912                       break;
913                     }
914                   else
915                     i++;
916                 }
917               else
918                 j++, i++;
919             }
920           /* Check the argument types are the same.  */
921           if (!err)
922             for (i = 0, j = 0; j < n2; )
923               {
924                 if (strcmp (spec1->named[i].name, spec2->named[j].name) == 0)
925                   {
926                     if (!(spec1->named[i].type == spec2->named[j].type))
927                       {
928                         if (error_logger)
929                           error_logger (_("format specifications in '%s' and '%s' for argument '%s' are not the same"),
930                                         pretty_msgid, pretty_msgstr,
931                                         spec2->named[j].name);
932                         err = true;
933                         break;
934                       }
935                     j++, i++;
936                   }
937                 else
938                   i++;
939               }
940         }
941 
942       if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
943         {
944           unsigned int i;
945 
946           /* Check the argument types are the same.  */
947           if (spec1->numbered_arg_count != spec2->numbered_arg_count)
948             {
949               if (error_logger)
950                 error_logger (_("number of format specifications in '%s' and '%s' does not match"),
951                               pretty_msgid, pretty_msgstr);
952               err = true;
953             }
954           else
955             for (i = 0; i < spec2->numbered_arg_count; i++)
956               if (!(spec1->numbered[i].type == spec2->numbered[i].type))
957                 {
958                   if (error_logger)
959                     error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
960                                   pretty_msgid, pretty_msgstr, i + 1);
961                   err = true;
962                 }
963         }
964     }
965 
966   return err;
967 }
968 
969 
970 struct formatstring_parser formatstring_ruby =
971 {
972   format_parse,
973   format_free,
974   format_get_number_of_directives,
975   NULL,
976   format_check
977 };
978 
979 
980 #ifdef TEST
981 
982 /* Test program: Print the argument list specification returned by
983    format_parse for strings read from standard input.  */
984 
985 #include <stdio.h>
986 
987 static void
format_print(void * descr)988 format_print (void *descr)
989 {
990   struct spec *spec = (struct spec *) descr;
991   unsigned int i;
992 
993   if (spec == NULL)
994     {
995       printf ("INVALID");
996       return;
997     }
998 
999   if (spec->named_arg_count > 0)
1000     {
1001       if (spec->numbered_arg_count > 0)
1002         abort ();
1003 
1004       printf ("({");
1005       for (i = 0; i < spec->named_arg_count; i++)
1006         {
1007           if (i > 0)
1008             printf (", ");
1009           printf (":%s => ", spec->named[i].name);
1010           switch (spec->named[i].type)
1011             {
1012             case FAT_ANY:
1013               printf ("s");
1014               break;
1015             case FAT_ESCAPED_ANY:
1016               printf ("p");
1017               break;
1018             case FAT_CHARACTER:
1019               printf ("c");
1020               break;
1021             case FAT_INTEGER:
1022               printf ("i");
1023               break;
1024             case FAT_FLOAT:
1025               printf ("f");
1026               break;
1027             default:
1028               abort ();
1029             }
1030         }
1031       printf ("})");
1032     }
1033   else
1034     {
1035       unsigned int last;
1036 
1037       printf ("(");
1038       last = 1;
1039       for (i = 0; i < spec->numbered_arg_count; i++)
1040         {
1041           unsigned int number = spec->numbered[i].number;
1042 
1043           if (i > 0)
1044             printf (" ");
1045           if (number < last)
1046             abort ();
1047           for (; last < number; last++)
1048             printf ("_ ");
1049           switch (spec->numbered[i].type)
1050             {
1051             case FAT_ANY:
1052               printf ("s");
1053               break;
1054             case FAT_ESCAPED_ANY:
1055               printf ("p");
1056               break;
1057             case FAT_CHARACTER:
1058               printf ("c");
1059               break;
1060             case FAT_INTEGER:
1061               printf ("i");
1062               break;
1063             case FAT_FLOAT:
1064               printf ("f");
1065               break;
1066             default:
1067               abort ();
1068             }
1069           last = number + 1;
1070         }
1071       printf (")");
1072     }
1073 }
1074 
1075 int
main()1076 main ()
1077 {
1078   for (;;)
1079     {
1080       char *line = NULL;
1081       size_t line_size = 0;
1082       int line_len;
1083       char *invalid_reason;
1084       void *descr;
1085 
1086       line_len = getline (&line, &line_size, stdin);
1087       if (line_len < 0)
1088         break;
1089       if (line_len > 0 && line[line_len - 1] == '\n')
1090         line[--line_len] = '\0';
1091 
1092       invalid_reason = NULL;
1093       descr = format_parse (line, false, NULL, &invalid_reason);
1094 
1095       format_print (descr);
1096       printf ("\n");
1097       if (descr == NULL)
1098         printf ("%s\n", invalid_reason);
1099 
1100       free (invalid_reason);
1101       free (line);
1102     }
1103 
1104   return 0;
1105 }
1106 
1107 /*
1108  * For Emacs M-x compile
1109  * Local Variables:
1110  * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../../gettext-runtime/intl -DHAVE_CONFIG_H -DTEST format-ruby.c ../gnulib-lib/libgettextlib.la"
1111  * End:
1112  */
1113 
1114 #endif /* TEST */
1115