Coverage Report

Created: 2026-08-17 07:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gettext/gettext-tools/src/format-ocaml.c
Line
Count
Source
1
/* OCaml format strings.
2
   Copyright (C) 2001-2026 Free Software Foundation, Inc.
3
4
   This program is free software: you can redistribute it and/or modify
5
   it under the terms of the GNU General Public License as published by
6
   the Free Software Foundation; either version 3 of the License, or
7
   (at your option) any later version.
8
9
   This program is distributed in the hope that it will be useful,
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
11
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
   GNU General Public License for more details.
13
14
   You should have received a copy of the GNU General Public License
15
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16
17
/* Written by Bruno Haible.  */
18
19
#include <config.h>
20
21
#include <stdbool.h>
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include "format.h"
26
#include "attribute.h"
27
#include "gettext.h"
28
#include "xalloc.h"
29
#include "format-invalid.h"
30
#include "c-ctype.h"
31
#include "xvasprintf.h"
32
33
0
#define _(str) gettext (str)
34
35
/* The OCaml format strings are described in the OCaml reference manual,
36
   at https://ocaml.org/manual/5.3/api/Printf.html#VALfprintf .
37
   They are implemented in ocaml-5.3.0/stdlib/scanf.ml.
38
39
   A directive
40
   - starts with '%',
41
   - [in msgstr only] is optionally followed by
42
       a positive integer m, then '$'
43
   - is optionally followed by a sequence of flags, each being one of
44
       '+', '-', ' ', '0', '#',
45
   - is optionally followed by a width specification:
46
       a positive integer, or
47
       '*', or
48
       [in msgstr only] '*', then a positive integer, then '$',
49
   - is optionally followed by a precision specification:
50
       '.' then optionally:
51
         a positive integer, or
52
         '*', or
53
         [in msgstr only] '*', then a positive integer, then '$',
54
   - is finished by a specifier
55
       - 'd', 'i', 'u', 'x', 'X', 'o', that need an integer argument,
56
       - 'l' then 'd', 'i', 'u', 'x', 'X', 'o', that need an int32 argument,
57
       - 'n' then 'd', 'i', 'u', 'x', 'X', 'o', that need an nativeint argument,
58
       - 'L' then 'd', 'i', 'u', 'x', 'X', 'o', that need an int64 argument,
59
       - 's', that needs a string argument,
60
       - 'S', that needs a string argument and outputs it in OCaml syntax,
61
       - 'c', that needs a character argument,
62
       - 'C', that needs a character argument and outputs it in OCaml syntax,
63
       - 'f', 'e', 'E', 'g', 'G', 'h', 'H', that need a floating-point argument,
64
       - 'F', that needs a floating-point argument and outputs it in OCaml syntax,
65
       - 'B', that needs a boolean argument,
66
       - 'a', that takes a function (of type : out_channel -> unit) argument,
67
       - 't', that takes two arguments: a function (of type : out_channel -> <T> -> unit)
68
              and a <T>,
69
       - '{' FMT '%}', that takes a format string argument without msgstr
70
         extensions, expected to have the same signature as FMT, effectively
71
         ignores it, and instead outputs the minimal format string with the
72
         same signature as FMT: a concatenation of
73
           - "%i" for an integer argument,
74
           - "%li" for an int32 argument,
75
           - "%ni" for a nativeint argument,
76
           - "%Li" for an int64 argument,
77
           - "%s" for a string argument,
78
           - "%c" for a character argument,
79
           - "%f" for a floating-point argument,
80
           - "%B" for a boolean argument,
81
           - "%a" for a function argument,
82
           - "%t" for two arguments, as described above,
83
       - '(' FMT '%)', that takes a format string argument without msgstr
84
         extensions, expected to have the same signature as FMT, and a set
85
         of arguments suitable for FMT,
86
       - '!', '%', '@', ',', that take no argument.
87
   Numbered ('%m$' or '*m$') and unnumbered argument specifications cannot
88
   be used in the same string.
89
 */
90
91
enum format_arg_type
92
{
93
  FAT_NONE              = 0,
94
  /* Basic types */
95
  FAT_INTEGER           = 1,
96
  FAT_INT32             = 2,
97
  FAT_NATIVEINT         = 3,
98
  FAT_INT64             = 4,
99
  FAT_STRING            = 5,
100
  FAT_CHARACTER         = 6,
101
  FAT_FLOATINGPOINT     = 7,
102
  FAT_BOOLEAN           = 8,
103
  FAT_FUNCTION_A        = 9,
104
  FAT_FUNCTION_T        = 10, /* first argument for %t */
105
  FAT_FUNCTION_T2       = 11, /* second argument for %t */
106
  FAT_FORMAT_STRING     = 12,
107
  /* Flags */
108
  FAT_OCAML_SYNTAX          = 1 << 4,
109
  FAT_OPTIONAL_OCAML_SYNTAX = 1 << 5,
110
  /* Bitmasks */
111
  FAT_BASIC_MASK        = (FAT_INTEGER | FAT_INT32 | FAT_NATIVEINT | FAT_INT64
112
                           | FAT_STRING | FAT_CHARACTER | FAT_FLOATINGPOINT
113
                           | FAT_BOOLEAN | FAT_FUNCTION_A | FAT_FUNCTION_T
114
                           | FAT_FUNCTION_T2 | FAT_FORMAT_STRING)
115
};
116
#ifdef __cplusplus
117
typedef int format_arg_type_t;
118
#else
119
typedef enum format_arg_type format_arg_type_t;
120
#endif
121
122
struct numbered_arg
123
{
124
  size_t number;
125
  format_arg_type_t type;
126
  char *signature;        /* for type == FAT_FORMAT_STRING */
127
};
128
129
struct spec
130
{
131
  size_t directives;
132
  size_t numbered_arg_count;
133
  struct numbered_arg *numbered
134
    COUNTED_BY (numbered_arg_count);
135
};
136
137
138
static int
139
numbered_arg_compare (const void *p1, const void *p2)
140
0
{
141
0
  size_t n1 = ((const struct numbered_arg *) p1)->number;
142
0
  size_t n2 = ((const struct numbered_arg *) p2)->number;
143
144
0
  return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
145
0
}
146
147
/* Frees the memory held by *spec.  */
148
static void
149
destroy_spec (struct spec *spec)
150
0
{
151
0
  if (spec->numbered != NULL)
152
0
    {
153
0
      for (size_t i = spec->numbered_arg_count; i > 0; )
154
0
        {
155
0
          --i;
156
0
          if (spec->numbered[i].type == FAT_FORMAT_STRING)
157
0
            free (spec->numbered[i].signature);
158
0
        }
159
0
      free (spec->numbered);
160
0
    }
161
0
}
162
163
/* Returns the signature of a format string
164
   as a freshly allocated string.  */
165
static char *
166
format_string_signature (const struct spec *spec)
167
0
{
168
0
  size_t len;
169
0
  {
170
0
    size_t i;
171
0
    const struct numbered_arg *p;
172
0
    len = spec->numbered_arg_count;
173
0
    for (i = 0, p = spec->numbered; i < spec->numbered_arg_count; i++, p++)
174
0
      if ((p->type & FAT_BASIC_MASK) == FAT_FORMAT_STRING)
175
0
        len += strlen (p->signature) + 1;
176
0
  }
177
0
  char *signature = (char *) xmalloc (len + 1);
178
0
  {
179
0
    size_t i;
180
0
    const struct numbered_arg *p;
181
0
    char *s;
182
0
    for (i = 0, p = spec->numbered, s = signature;
183
0
         i < spec->numbered_arg_count;
184
0
         i++, p++)
185
0
      switch (p->type & FAT_BASIC_MASK)
186
0
        {
187
0
        case FAT_INTEGER:
188
0
          *s++ = 'i';
189
0
          break;
190
0
        case FAT_INT32:
191
0
          *s++ = 'l';
192
0
          break;
193
0
        case FAT_NATIVEINT:
194
0
          *s++ = 'n';
195
0
          break;
196
0
        case FAT_INT64:
197
0
          *s++ = 'L';
198
0
          break;
199
0
        case FAT_STRING:
200
0
          *s++ = 's';
201
0
          break;
202
0
        case FAT_CHARACTER:
203
0
          *s++ = 'c';
204
0
          break;
205
0
        case FAT_FLOATINGPOINT:
206
0
          *s++ = 'f';
207
0
          break;
208
0
        case FAT_BOOLEAN:
209
0
          *s++ = 'B';
210
0
          break;
211
0
        case FAT_FUNCTION_A:
212
0
          *s++ = 'a';
213
0
          break;
214
0
        case FAT_FUNCTION_T:
215
0
          *s++ = 't';
216
0
          break;
217
0
        case FAT_FUNCTION_T2:
218
0
          break;
219
0
        case FAT_FORMAT_STRING:
220
0
          *s++ = '(';
221
0
          memcpy (s, p->signature, strlen (p->signature));
222
0
          s += strlen (p->signature);
223
0
          *s++ = ')';
224
0
          break;
225
0
        default:
226
0
          abort ();
227
0
        }
228
0
    *s = '\0';
229
0
  }
230
0
  return signature;
231
0
}
232
233
/* When a type is specified via format string substitution, e.g. "%(%s%)", both
234
   the variant without OCaml syntax "%s" and the variant with OCaml syntax "%S"
235
   are allowed.  */
236
static format_arg_type_t
237
type_without_translator_constraint (format_arg_type_t type)
238
0
{
239
0
  switch (type & FAT_BASIC_MASK)
240
0
    {
241
0
    case FAT_STRING:
242
0
    case FAT_CHARACTER:
243
0
    case FAT_FLOATINGPOINT:
244
0
      return (type & FAT_BASIC_MASK) | FAT_OPTIONAL_OCAML_SYNTAX;
245
0
    default:
246
0
      return type;
247
0
    }
248
0
}
249
250
/* Parse a piece of format string, until the matching terminating format
251
   directive is encountered.
252
   spec is the global struct spec.
253
   format is the remainder of the format string.
254
   It is updated upon valid return.
255
   terminator is '\0' at the top-level, otherwise '}' or ')'.
256
   translated is true when msgstr extensions should be accepted.
257
   fdi is an array to be filled with format directive indicators, or NULL.
258
   If the format string is invalid, false is returned and *invalid_reason is
259
   set to an error message explaining why.  */
260
static bool
261
parse_upto (struct spec *spec,
262
            const char **formatp,
263
            char terminator, bool translated,
264
            char *fdi, char **invalid_reason)
265
0
{
266
0
  const char *format = *formatp;
267
0
  const char *const format_start = format;
268
269
0
  spec->directives = 0;
270
0
  spec->numbered_arg_count = 0;
271
0
  spec->numbered = NULL;
272
0
  size_t numbered_allocated = 0;
273
0
  size_t unnumbered_arg_count = 0;
274
0
  struct numbered_arg *unnumbered = NULL;
275
276
0
  for (; *format != '\0';)
277
    /* Invariant: spec->numbered_arg_count == 0 || unnumbered_arg_count == 0.  */
278
0
    if (*format++ == '%')
279
0
      {
280
        /* A directive.  */
281
0
        FDI_SET (format - 1, FMTDIR_START);
282
0
        spec->directives++;
283
284
0
        size_t number = 0;
285
0
        if (translated && c_isdigit (*format))
286
0
          {
287
0
            const char *f = format;
288
0
            size_t m = 0;
289
290
0
            do
291
0
              {
292
0
                m = 10 * m + (*f - '0');
293
0
                f++;
294
0
              }
295
0
            while (c_isdigit (*f));
296
297
0
            if (*f == '$')
298
0
              {
299
0
                if (m == 0)
300
0
                  {
301
0
                    *invalid_reason = INVALID_ARGNO_0 (spec->directives);
302
0
                    FDI_SET (f, FMTDIR_ERROR);
303
0
                    goto bad_format;
304
0
                  }
305
0
                number = m;
306
0
                format = ++f;
307
0
              }
308
0
          }
309
310
        /* Parse flags.  */
311
0
        while (*format == ' ' || *format == '+' || *format == '-'
312
0
               || *format == '#' || *format == '0')
313
0
          format++;
314
315
        /* Parse width.  */
316
0
        if (*format == '*')
317
0
          {
318
0
            format++;
319
320
0
            size_t width_number = 0;
321
0
            if (translated && c_isdigit (*format))
322
0
              {
323
0
                const char *f = format;
324
0
                size_t m = 0;
325
326
0
                do
327
0
                  {
328
0
                    m = 10 * m + (*f - '0');
329
0
                    f++;
330
0
                  }
331
0
                while (c_isdigit (*f));
332
333
0
                if (*f == '$')
334
0
                  {
335
0
                    if (m == 0)
336
0
                      {
337
0
                        *invalid_reason =
338
0
                          INVALID_WIDTH_ARGNO_0 (spec->directives);
339
0
                        FDI_SET (f, FMTDIR_ERROR);
340
0
                        goto bad_format;
341
0
                      }
342
0
                    width_number = m;
343
0
                    format = ++f;
344
0
                  }
345
0
              }
346
347
0
            if (width_number)
348
0
              {
349
                /* Numbered argument.  */
350
351
                /* Numbered and unnumbered specifications are exclusive.  */
352
0
                if (unnumbered_arg_count > 0)
353
0
                  {
354
0
                    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
355
0
                    FDI_SET (format - 1, FMTDIR_ERROR);
356
0
                    goto bad_format;
357
0
                  }
358
359
0
                if (numbered_allocated == spec->numbered_arg_count)
360
0
                  {
361
0
                    numbered_allocated = 2 * numbered_allocated + 1;
362
0
                    spec->numbered = (struct numbered_arg *) xrealloc (spec->numbered, numbered_allocated * sizeof (struct numbered_arg));
363
0
                  }
364
0
                size_t numbered_index = spec->numbered_arg_count++;
365
0
                spec->numbered[numbered_index].number = width_number;
366
0
                spec->numbered[numbered_index].type = FAT_INTEGER;
367
0
              }
368
0
            else
369
0
              {
370
                /* Unnumbered argument.  */
371
372
                /* Numbered and unnumbered specifications are exclusive.  */
373
0
                if (spec->numbered_arg_count > 0)
374
0
                  {
375
0
                    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
376
0
                    FDI_SET (format - 1, FMTDIR_ERROR);
377
0
                    goto bad_format;
378
0
                  }
379
380
0
                if (numbered_allocated == unnumbered_arg_count)
381
0
                  {
382
0
                    numbered_allocated = 2 * numbered_allocated + 1;
383
0
                    unnumbered = (struct numbered_arg *) xrealloc (unnumbered, numbered_allocated * sizeof (struct numbered_arg));
384
0
                  }
385
0
                size_t unnumbered_index = unnumbered_arg_count++;
386
0
                unnumbered[unnumbered_index].number = unnumbered_index + 1;
387
0
                unnumbered[unnumbered_index].type = FAT_INTEGER;
388
0
              }
389
0
          }
390
0
        else if (c_isdigit (*format))
391
0
          {
392
0
            do format++; while (c_isdigit (*format));
393
0
          }
394
395
        /* Parse precision.  */
396
0
        if (*format == '.')
397
0
          {
398
0
            format++;
399
400
0
            if (*format == '*')
401
0
              {
402
0
                format++;
403
404
0
                size_t precision_number = 0;
405
0
                if (translated && c_isdigit (*format))
406
0
                  {
407
0
                    const char *f = format;
408
0
                    size_t m = 0;
409
410
0
                    do
411
0
                      {
412
0
                        m = 10 * m + (*f - '0');
413
0
                        f++;
414
0
                      }
415
0
                    while (c_isdigit (*f));
416
417
0
                    if (*f == '$')
418
0
                      {
419
0
                        if (m == 0)
420
0
                          {
421
0
                            *invalid_reason =
422
0
                              INVALID_PRECISION_ARGNO_0 (spec->directives);
423
0
                            FDI_SET (f, FMTDIR_ERROR);
424
0
                            goto bad_format;
425
0
                          }
426
0
                        precision_number = m;
427
0
                        format = ++f;
428
0
                      }
429
0
                  }
430
431
0
                if (precision_number)
432
0
                  {
433
                    /* Numbered argument.  */
434
435
                    /* Numbered and unnumbered specifications are exclusive.  */
436
0
                    if (unnumbered_arg_count > 0)
437
0
                      {
438
0
                        *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
439
0
                        FDI_SET (format - 1, FMTDIR_ERROR);
440
0
                        goto bad_format;
441
0
                      }
442
443
0
                    if (numbered_allocated == spec->numbered_arg_count)
444
0
                      {
445
0
                        numbered_allocated = 2 * numbered_allocated + 1;
446
0
                        spec->numbered = (struct numbered_arg *) xrealloc (spec->numbered, numbered_allocated * sizeof (struct numbered_arg));
447
0
                      }
448
0
                    size_t numbered_index = spec->numbered_arg_count++;
449
0
                    spec->numbered[numbered_index].number = precision_number;
450
0
                    spec->numbered[numbered_index].type = FAT_INTEGER;
451
0
                  }
452
0
                else
453
0
                  {
454
                    /* Unnumbered argument.  */
455
456
                    /* Numbered and unnumbered specifications are exclusive.  */
457
0
                    if (spec->numbered_arg_count > 0)
458
0
                      {
459
0
                        *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
460
0
                        FDI_SET (format - 1, FMTDIR_ERROR);
461
0
                        goto bad_format;
462
0
                      }
463
464
0
                    if (numbered_allocated == unnumbered_arg_count)
465
0
                      {
466
0
                        numbered_allocated = 2 * numbered_allocated + 1;
467
0
                        unnumbered = (struct numbered_arg *) xrealloc (unnumbered, numbered_allocated * sizeof (struct numbered_arg));
468
0
                      }
469
0
                    size_t unnumbered_index = unnumbered_arg_count++;
470
0
                    unnumbered[unnumbered_index].number = unnumbered_index + 1;
471
0
                    unnumbered[unnumbered_index].type = FAT_INTEGER;
472
0
                  }
473
0
              }
474
0
            else if (c_isdigit (*format))
475
0
              {
476
0
                do format++; while (c_isdigit (*format));
477
0
              }
478
0
          }
479
480
        /* Parse the specifier.  */
481
0
        enum format_arg_type integer_type = FAT_INTEGER;
482
0
        if (*format == 'l')
483
0
          {
484
0
            integer_type = FAT_INT32;
485
0
            format++;
486
0
          }
487
0
        else if (*format == 'n')
488
0
          {
489
0
            integer_type = FAT_NATIVEINT;
490
0
            format++;
491
0
          }
492
0
        else if (*format == 'L')
493
0
          {
494
0
            integer_type = FAT_INT64;
495
0
            format++;
496
0
          }
497
498
0
        format_arg_type_t type;
499
0
        char *signature = NULL;
500
0
        switch (*format)
501
0
          {
502
0
          case 'd':
503
0
          case 'i':
504
0
          case 'u':
505
0
          case 'x': case 'X':
506
0
          case 'o':
507
0
            type = integer_type;
508
0
            break;
509
0
          default:
510
0
            if (integer_type != FAT_INTEGER)
511
0
              --format;
512
0
            switch (*format)
513
0
              {
514
0
              case 's':
515
0
                type = FAT_STRING;
516
0
                break;
517
0
              case 'S':
518
0
                type = FAT_STRING | FAT_OCAML_SYNTAX;
519
0
                break;
520
0
              case 'c':
521
0
                type = FAT_CHARACTER;
522
0
                break;
523
0
              case 'C':
524
0
                type = FAT_CHARACTER | FAT_OCAML_SYNTAX;
525
0
                break;
526
0
              case 'f':
527
0
              case 'e': case 'E':
528
0
              case 'g': case 'G':
529
0
              case 'h': case 'H':
530
0
                type = FAT_FLOATINGPOINT;
531
0
                break;
532
0
              case 'F':
533
0
                type = FAT_FLOATINGPOINT | FAT_OCAML_SYNTAX;
534
0
                break;
535
0
              case 'B':
536
0
                type = FAT_BOOLEAN;
537
0
                break;
538
0
              case 'a':
539
0
                type = FAT_FUNCTION_A;
540
0
                break;
541
0
              case 't':
542
0
                type = FAT_FUNCTION_T;
543
0
                break;
544
0
              case '{':
545
0
                {
546
0
                  struct spec sub_spec;
547
0
                  *formatp = format;
548
0
                  if (!parse_upto (&sub_spec, formatp, '}', false,
549
0
                                   fdi, invalid_reason))
550
0
                    {
551
0
                      FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
552
0
                               FMTDIR_ERROR);
553
0
                      goto bad_format;
554
0
                    }
555
0
                  format = *formatp;
556
0
                  type = FAT_FORMAT_STRING;
557
0
                  signature = format_string_signature (&sub_spec);
558
0
                  destroy_spec (&sub_spec);
559
0
                }
560
0
                break;
561
0
              case '}':
562
0
                if (terminator != '}')
563
0
                  {
564
0
                    *invalid_reason =
565
0
                      xasprintf (_("Found '%s' without matching '%s'."), "%}", "%{");
566
0
                    FDI_SET (format - 1, FMTDIR_ERROR);
567
0
                    goto bad_format;
568
0
                  }
569
0
                spec->directives--;
570
0
                goto done;
571
0
              case '(':
572
0
                {
573
0
                  struct spec sub_spec;
574
0
                  *formatp = format;
575
0
                  if (!parse_upto (&sub_spec, formatp, ')', false,
576
0
                                   fdi, invalid_reason))
577
0
                    {
578
0
                      FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
579
0
                               FMTDIR_ERROR);
580
0
                      goto bad_format;
581
0
                    }
582
0
                  format = *formatp;
583
0
                  type = FAT_FORMAT_STRING;
584
0
                  signature = format_string_signature (&sub_spec);
585
586
0
                  if (number)
587
0
                    {
588
                      /* Numbered argument.  */
589
590
                      /* Numbered and unnumbered specifications are exclusive.  */
591
0
                      if (unnumbered_arg_count > 0)
592
0
                        {
593
0
                          *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
594
0
                          FDI_SET (format, FMTDIR_ERROR);
595
0
                          goto bad_format;
596
0
                        }
597
598
0
                      size_t new_numbered_arg_count =
599
0
                        spec->numbered_arg_count + 1 + sub_spec.numbered_arg_count;
600
0
                      if (numbered_allocated < new_numbered_arg_count)
601
0
                        {
602
0
                          numbered_allocated = 2 * numbered_allocated + 1;
603
0
                          if (numbered_allocated < new_numbered_arg_count)
604
0
                            numbered_allocated = new_numbered_arg_count;
605
0
                          spec->numbered = (struct numbered_arg *) xrealloc (spec->numbered, numbered_allocated * sizeof (struct numbered_arg));
606
0
                        }
607
0
                      {
608
0
                        size_t numbered_index = spec->numbered_arg_count++;
609
0
                        spec->numbered[numbered_index].number = number;
610
0
                        spec->numbered[numbered_index].type = type;
611
0
                        spec->numbered[numbered_index].signature = signature;
612
0
                      }
613
0
                      for (size_t i = 0; i < sub_spec.numbered_arg_count; i++)
614
0
                        {
615
0
                          size_t numbered_index = spec->numbered_arg_count++;
616
0
                          spec->numbered[numbered_index].number = number + sub_spec.numbered[i].number;
617
0
                          spec->numbered[numbered_index].type =
618
0
                            type_without_translator_constraint (sub_spec.numbered[i].type);
619
0
                          if (sub_spec.numbered[i].type == FAT_FORMAT_STRING)
620
0
                            spec->numbered[numbered_index].signature = sub_spec.numbered[i].signature;
621
0
                        }
622
0
                    }
623
0
                  else
624
0
                    {
625
                      /* Unnumbered argument.  */
626
627
                      /* Numbered and unnumbered specifications are exclusive.  */
628
0
                      if (spec->numbered_arg_count > 0)
629
0
                        {
630
0
                          *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
631
0
                          FDI_SET (format, FMTDIR_ERROR);
632
0
                          goto bad_format;
633
0
                        }
634
635
0
                      size_t new_unnumbered_arg_count =
636
0
                        unnumbered_arg_count + 1 + sub_spec.numbered_arg_count;
637
0
                      if (numbered_allocated < new_unnumbered_arg_count)
638
0
                        {
639
0
                          numbered_allocated = 2 * numbered_allocated + 1;
640
0
                          if (numbered_allocated < new_unnumbered_arg_count)
641
0
                            numbered_allocated = new_unnumbered_arg_count;
642
0
                          unnumbered = (struct numbered_arg *) xrealloc (unnumbered, numbered_allocated * sizeof (struct numbered_arg));
643
0
                        }
644
0
                      {
645
0
                        size_t unnumbered_index = unnumbered_arg_count++;
646
0
                        unnumbered[unnumbered_index].number = unnumbered_index + 1;
647
0
                        unnumbered[unnumbered_index].type = type;
648
0
                        unnumbered[unnumbered_index].signature = signature;
649
0
                      }
650
0
                      for (size_t i = 0; i < sub_spec.numbered_arg_count; i++)
651
0
                        {
652
0
                          size_t unnumbered_index = unnumbered_arg_count++;
653
0
                          unnumbered[unnumbered_index].number = unnumbered_index + 1;
654
0
                          unnumbered[unnumbered_index].type =
655
0
                            type_without_translator_constraint (sub_spec.numbered[i].type);
656
0
                          if (sub_spec.numbered[i].type == FAT_FORMAT_STRING)
657
0
                            unnumbered[unnumbered_index].signature = sub_spec.numbered[i].signature;
658
0
                        }
659
0
                    }
660
661
0
                  free (sub_spec.numbered);
662
0
                }
663
0
                goto done_specifier;
664
0
              case ')':
665
0
                if (terminator != ')')
666
0
                  {
667
0
                    *invalid_reason =
668
0
                      xasprintf (_("Found '%s' without matching '%s'."), "%)", "%(");
669
0
                    FDI_SET (format - 1, FMTDIR_ERROR);
670
0
                    goto bad_format;
671
0
                  }
672
0
                spec->directives--;
673
0
                goto done;
674
0
              case '!':
675
0
              case '%':
676
0
              case '@':
677
0
              case ',':
678
0
                type = FAT_NONE;
679
0
                break;
680
0
              default:
681
0
                if (*format == '\0')
682
0
                  {
683
0
                    *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
684
0
                    FDI_SET (format - 1, FMTDIR_ERROR);
685
0
                  }
686
0
                else
687
0
                  {
688
0
                    *invalid_reason =
689
0
                      INVALID_CONVERSION_SPECIFIER (spec->directives, *format);
690
0
                    FDI_SET (format, FMTDIR_ERROR);
691
0
                  }
692
0
                goto bad_format;
693
0
              }
694
0
            break;
695
0
          }
696
697
0
        if (type != FAT_NONE)
698
0
          {
699
0
            if (number)
700
0
              {
701
                /* Numbered argument.  */
702
703
                /* Numbered and unnumbered specifications are exclusive.  */
704
0
                if (unnumbered_arg_count > 0)
705
0
                  {
706
0
                    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
707
0
                    FDI_SET (format, FMTDIR_ERROR);
708
0
                    goto bad_format;
709
0
                  }
710
711
0
                size_t new_numbered_arg_count =
712
0
                  spec->numbered_arg_count + 1 + (type == FAT_FUNCTION_T);
713
0
                if (numbered_allocated < new_numbered_arg_count)
714
0
                  {
715
0
                    numbered_allocated = 2 * numbered_allocated + 1;
716
0
                    if (numbered_allocated < new_numbered_arg_count)
717
0
                      numbered_allocated = new_numbered_arg_count;
718
0
                    spec->numbered = (struct numbered_arg *) xrealloc (spec->numbered, numbered_allocated * sizeof (struct numbered_arg));
719
0
                  }
720
0
                {
721
0
                  size_t numbered_index = spec->numbered_arg_count++;
722
0
                  spec->numbered[numbered_index].number = number;
723
0
                  spec->numbered[numbered_index].type = type;
724
0
                  if (type == FAT_FORMAT_STRING)
725
0
                    spec->numbered[numbered_index].signature = signature;
726
0
                }
727
0
                if (type == FAT_FUNCTION_T)
728
0
                  {
729
0
                    size_t numbered_index = spec->numbered_arg_count++;
730
0
                    spec->numbered[numbered_index].number = number + 1;
731
0
                    spec->numbered[numbered_index].type = FAT_FUNCTION_T2;
732
0
                  }
733
0
              }
734
0
            else
735
0
              {
736
                /* Unnumbered argument.  */
737
738
                /* Numbered and unnumbered specifications are exclusive.  */
739
0
                if (spec->numbered_arg_count > 0)
740
0
                  {
741
0
                    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
742
0
                    FDI_SET (format, FMTDIR_ERROR);
743
0
                    goto bad_format;
744
0
                  }
745
746
0
                size_t new_unnumbered_arg_count =
747
0
                  unnumbered_arg_count + 1 + (type == FAT_FUNCTION_T);
748
0
                if (numbered_allocated < new_unnumbered_arg_count)
749
0
                  {
750
0
                    numbered_allocated = 2 * numbered_allocated + 1;
751
0
                    if (numbered_allocated < new_unnumbered_arg_count)
752
0
                      numbered_allocated = new_unnumbered_arg_count;
753
0
                    unnumbered = (struct numbered_arg *) xrealloc (unnumbered, numbered_allocated * sizeof (struct numbered_arg));
754
0
                  }
755
0
                {
756
0
                  size_t unnumbered_index = unnumbered_arg_count++;
757
0
                  unnumbered[unnumbered_index].number = unnumbered_index + 1;
758
0
                  unnumbered[unnumbered_index].type = type;
759
0
                  if (type == FAT_FORMAT_STRING)
760
0
                    unnumbered[unnumbered_index].signature = signature;
761
0
                }
762
0
                if (type == FAT_FUNCTION_T)
763
0
                  {
764
0
                    size_t unnumbered_index = unnumbered_arg_count++;
765
0
                    unnumbered[unnumbered_index].number = unnumbered_index + 1;
766
0
                    unnumbered[unnumbered_index].type = FAT_FUNCTION_T2;
767
0
                  }
768
0
              }
769
0
          }
770
771
0
       done_specifier:
772
0
        FDI_SET (format, FMTDIR_END);
773
774
0
        format++;
775
0
      }
776
777
0
  if (terminator != '\0')
778
0
    {
779
0
      *invalid_reason = xasprintf (_("Found '%%%c' without matching '%%%c'."),
780
0
                                   terminator == '}' ? '{' : '(', terminator);
781
0
      goto bad_format;
782
0
    }
783
784
0
 done:
785
  /* Convert the unnumbered argument array to numbered arguments.  */
786
0
  if (unnumbered_arg_count > 0)
787
0
    {
788
0
      spec->numbered = unnumbered;
789
0
      spec->numbered_arg_count = unnumbered_arg_count;
790
0
    }
791
  /* Sort the numbered argument array, and eliminate duplicates.  */
792
0
  else if (spec->numbered_arg_count > 1)
793
0
    {
794
0
      qsort (spec->numbered, spec->numbered_arg_count,
795
0
             sizeof (struct numbered_arg), numbered_arg_compare);
796
797
      /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
798
0
      bool err = false;
799
0
      size_t i, j;
800
0
      for (i = j = 0; i < spec->numbered_arg_count; i++)
801
0
        if (j > 0 && spec->numbered[i].number == spec->numbered[j-1].number)
802
0
          {
803
0
            format_arg_type_t type1 = spec->numbered[i].type;
804
0
            format_arg_type_t type2 = spec->numbered[j-1].type;
805
806
0
            format_arg_type_t type_both;
807
0
            if (((type1 == type2)
808
0
                 && (type1 != FAT_FORMAT_STRING
809
0
                     || streq (spec->numbered[i].signature,
810
0
                               spec->numbered[j-1].signature)))
811
0
                || (((type1 | type2) & FAT_OPTIONAL_OCAML_SYNTAX) != 0
812
0
                    && (((type1 & ~FAT_OPTIONAL_OCAML_SYNTAX) | FAT_OCAML_SYNTAX)
813
0
                        == ((type2 & ~FAT_OPTIONAL_OCAML_SYNTAX) | FAT_OCAML_SYNTAX))))
814
0
              type_both = (type1 | type2) & ~FAT_OPTIONAL_OCAML_SYNTAX;
815
0
            else
816
0
              {
817
                /* Incompatible types.  */
818
0
                type_both = FAT_NONE;
819
0
                if (!err)
820
0
                  *invalid_reason =
821
0
                    INVALID_INCOMPATIBLE_ARG_TYPES (spec->numbered[i].number);
822
0
                err = true;
823
0
              }
824
825
0
            spec->numbered[j-1].type = type_both;
826
0
            if (type_both == FAT_FORMAT_STRING)
827
0
              free (spec->numbered[i].signature);
828
0
          }
829
0
        else
830
0
          {
831
0
            if (j < i)
832
0
              {
833
0
                spec->numbered[j].number = spec->numbered[i].number;
834
0
                spec->numbered[j].type = spec->numbered[i].type;
835
0
                if (spec->numbered[j].type == FAT_FORMAT_STRING)
836
0
                  spec->numbered[j].signature = spec->numbered[i].signature;
837
0
              }
838
0
            j++;
839
0
          }
840
0
      spec->numbered_arg_count = j;
841
0
      if (err)
842
        /* *invalid_reason has already been set above.  */
843
0
        goto bad_format;
844
0
    }
845
846
0
  *formatp = format;
847
0
  return true;
848
849
0
 bad_format:
850
0
  if (unnumbered != NULL)
851
0
    free (unnumbered);
852
0
  destroy_spec (spec);
853
0
  return false;
854
0
}
855
856
static void *
857
format_parse (const char *format, bool translated, char *fdi,
858
              char **invalid_reason)
859
0
{
860
0
  struct spec spec;
861
862
0
  if (!parse_upto (&spec, &format, '\0', translated, fdi, invalid_reason))
863
0
    return NULL;
864
865
0
  struct spec *result = XMALLOC (struct spec);
866
0
  *result = spec;
867
0
  return result;
868
0
}
869
870
static void
871
format_free (void *descr)
872
0
{
873
0
  struct spec *spec = (struct spec *) descr;
874
875
0
  destroy_spec (spec);
876
0
  free (spec);
877
0
}
878
879
static int
880
format_get_number_of_directives (void *descr)
881
0
{
882
0
  struct spec *spec = (struct spec *) descr;
883
884
0
  return spec->directives;
885
0
}
886
887
static bool
888
format_check (void *msgid_descr, void *msgstr_descr, bool equality,
889
              formatstring_error_logger_t error_logger, void *error_logger_data,
890
              const char *pretty_msgid, const char *pretty_msgstr)
891
0
{
892
0
  struct spec *spec1 = (struct spec *) msgid_descr;
893
0
  struct spec *spec2 = (struct spec *) msgstr_descr;
894
0
  bool err = false;
895
896
0
  if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
897
0
    {
898
0
      size_t n1 = spec1->numbered_arg_count;
899
0
      size_t n2 = spec2->numbered_arg_count;
900
901
      /* Check that the argument numbers are the same.
902
         Both arrays are sorted.  We search for the first difference.  */
903
0
      {
904
0
        size_t i, j;
905
0
        for (i = 0, j = 0; i < n1 || j < n2; )
906
0
          {
907
0
            int cmp = (i >= n1 ? 1 :
908
0
                       j >= n2 ? -1 :
909
0
                       spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
910
0
                       spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
911
0
                       0);
912
913
0
            if (cmp > 0)
914
0
              {
915
0
                if (error_logger)
916
0
                  error_logger (error_logger_data,
917
0
                                _("a format specification for argument %zu, as in '%s', doesn't exist in '%s'"),
918
0
                                spec2->numbered[j].number, pretty_msgstr,
919
0
                                pretty_msgid);
920
0
                err = true;
921
0
                break;
922
0
              }
923
0
            else if (cmp < 0)
924
0
              {
925
0
                if (equality)
926
0
                  {
927
0
                    if (error_logger)
928
0
                      error_logger (error_logger_data,
929
0
                                    _("a format specification for argument %zu doesn't exist in '%s'"),
930
0
                                    spec1->numbered[i].number, pretty_msgstr);
931
0
                    err = true;
932
0
                    break;
933
0
                  }
934
0
                else
935
0
                  i++;
936
0
              }
937
0
            else
938
0
              j++, i++;
939
0
          }
940
0
      }
941
      /* Check that the argument types are essentially the same.  */
942
0
      if (!err)
943
0
        {
944
0
          size_t i, j;
945
0
          for (i = 0, j = 0; j < n2; )
946
0
            {
947
0
              if (spec1->numbered[i].number == spec2->numbered[j].number)
948
0
                {
949
0
                  format_arg_type_t type1 = spec1->numbered[i].type;
950
0
                  format_arg_type_t type2 = spec2->numbered[j].type;
951
952
0
                  if (!(((type1 == type2)
953
0
                         && (type1 != FAT_FORMAT_STRING
954
0
                             || streq (spec1->numbered[i].signature,
955
0
                                       spec2->numbered[j].signature)))
956
0
                        || ((type2 & FAT_OPTIONAL_OCAML_SYNTAX) != 0
957
0
                            && (type2 & ~FAT_OPTIONAL_OCAML_SYNTAX)
958
0
                               == (type1 & ~FAT_OCAML_SYNTAX))))
959
0
                    {
960
0
                      if (error_logger)
961
0
                        error_logger (error_logger_data,
962
0
                                      _("format specifications in '%s' and '%s' for argument %zu are not the same"),
963
0
                                      pretty_msgid, pretty_msgstr,
964
0
                                      spec2->numbered[j].number);
965
0
                      err = true;
966
0
                      break;
967
0
                    }
968
0
                  j++, i++;
969
0
                }
970
0
              else
971
0
                i++;
972
0
            }
973
0
        }
974
0
    }
975
976
0
  return err;
977
0
}
978
979
980
struct formatstring_parser formatstring_ocaml =
981
{
982
  format_parse,
983
  format_free,
984
  format_get_number_of_directives,
985
  NULL,
986
  format_check
987
};
988
989
990
#ifdef TEST
991
992
/* Test program: Print the argument list specification returned by
993
   format_parse for strings read from standard input.  */
994
995
#include <stdio.h>
996
997
static void
998
format_print (void *descr)
999
{
1000
  struct spec *spec = (struct spec *) descr;
1001
1002
  if (spec == NULL)
1003
    {
1004
      printf ("INVALID");
1005
      return;
1006
    }
1007
1008
  printf ("(");
1009
  size_t last = 1;
1010
  for (size_t i = 0; i < spec->numbered_arg_count; i++)
1011
    {
1012
      size_t number = spec->numbered[i].number;
1013
1014
      if (i > 0)
1015
        printf (" ");
1016
      if (number < last)
1017
        abort ();
1018
      for (; last < number; last++)
1019
        printf ("_ ");
1020
      switch (spec->numbered[i].type & FAT_BASIC_MASK)
1021
        {
1022
        case FAT_INTEGER:
1023
          printf ("i");
1024
          break;
1025
        case FAT_INT32:
1026
          printf ("l");
1027
          break;
1028
        case FAT_NATIVEINT:
1029
          printf ("n");
1030
          break;
1031
        case FAT_INT64:
1032
          printf ("L");
1033
          break;
1034
        case FAT_STRING:
1035
          printf ("s");
1036
          break;
1037
        case FAT_CHARACTER:
1038
          printf ("c");
1039
          break;
1040
        case FAT_FLOATINGPOINT:
1041
          printf ("f");
1042
          break;
1043
        case FAT_BOOLEAN:
1044
          printf ("B");
1045
          break;
1046
        case FAT_FUNCTION_A:
1047
          printf ("a");
1048
          break;
1049
        case FAT_FUNCTION_T:
1050
          printf ("t1");
1051
          break;
1052
        case FAT_FUNCTION_T2:
1053
          printf ("t2");
1054
          break;
1055
        case FAT_FORMAT_STRING:
1056
          printf ("\"%s\"", spec->numbered[i].signature);
1057
          break;
1058
        default:
1059
          abort ();
1060
        }
1061
      if (spec->numbered[i].type & FAT_OCAML_SYNTAX)
1062
        printf ("!");
1063
      if (spec->numbered[i].type & FAT_OPTIONAL_OCAML_SYNTAX)
1064
        printf ("?");
1065
      last = number + 1;
1066
    }
1067
  printf (")");
1068
}
1069
1070
int
1071
main ()
1072
{
1073
  for (;;)
1074
    {
1075
      char *line = NULL;
1076
      size_t line_size = 0;
1077
      int line_len = getline (&line, &line_size, stdin);
1078
      if (line_len < 0)
1079
        break;
1080
      if (line_len > 0 && line[line_len - 1] == '\n')
1081
        line[--line_len] = '\0';
1082
1083
      char *invalid_reason = NULL;
1084
      void *descr = format_parse (line, true, NULL, &invalid_reason);
1085
1086
      format_print (descr);
1087
      printf ("\n");
1088
      if (descr == NULL)
1089
        printf ("%s\n", invalid_reason);
1090
1091
      free (invalid_reason);
1092
      free (line);
1093
    }
1094
1095
  return 0;
1096
}
1097
1098
/*
1099
 * For Emacs M-x compile
1100
 * Local Variables:
1101
 * 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 -DTEST format-ocaml.c ../gnulib-lib/libgettextlib.la"
1102
 * End:
1103
 */
1104
1105
#endif /* TEST */