Coverage Report

Created: 2026-03-12 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gettext/gettext-tools/src/format-boost.c
Line
Count
Source
1
/* Boost 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
24
#include "attribute.h"
25
#include "format.h"
26
#include "c-ctype.h"
27
#include "xalloc.h"
28
#include "xvasprintf.h"
29
#include "format-invalid.h"
30
#include "gettext.h"
31
32
0
#define _(str) gettext (str)
33
34
/* Boost format strings are described in
35
     boost_1_33_1/libs/format/doc/format.html
36
   and implemented in
37
     boost_1_33_1/boost/format/parsing.hpp.
38
   A directive (other than '%%')
39
   - starts with '%' or '%|'; in the latter case it must end in '|',
40
   - is continued either by
41
       - 'm%' where m is a positive integer, starting with a nonzero digit;
42
         in this case the directive must not have started with '%|'; or
43
       - the following:
44
           - optional: 'm$' where m is a positive integer, starting with a
45
             nonzero digit,
46
           - optional: any of the characters '#', '0', '-', ' ', '+', "'",
47
             '_', '=', 'h', 'l',
48
           - optional: a width specification: '*' (reads an argument) or '*m$'
49
             or a nonempty digit sequence,
50
           - optional: a '.' and a precision specification: '*' (reads an
51
             argument) or '*m$' or an optional nonempty digit sequence,
52
           - optional: any of the characters 'h', 'l', 'L',
53
           - if the directive started with '%|':
54
               an optional specifier and a final '|',
55
             otherwise
56
               a mandatory specifier.
57
             If no specifier is given, it needs an argument of any type.
58
             The possible specifiers are:
59
               - 'c', 'C', that need a character argument,
60
               - 's', 'S', that need an argument of any type,
61
               - 'i', 'd', 'o', 'u', 'x', 'X', that need an integer argument,
62
               - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument,
63
               - 'p', that needs a 'void *' argument,
64
               - 't', that doesn't need an argument,
65
               - 'TX', where X is any character, that doesn't need an argument,
66
               - 'n', that needs a pointer to integer.
67
             The Boost format string interpreter doesn't actually care about
68
             the argument types, but we do, because it increases the likelihood
69
             of detecting translator mistakes.
70
   Numbered ('%m%' or '%m$' or '*m$') and unnumbered argument specifications
71
   cannot be used in the same string.
72
 */
73
74
enum format_arg_type
75
{
76
  FAT_NONE              = 0,
77
  /* Basic types */
78
  FAT_INTEGER           = 1,
79
  FAT_DOUBLE            = 2,
80
  FAT_CHAR              = 3,
81
  FAT_POINTER           = 4,
82
  FAT_ANY               = 5
83
};
84
85
struct numbered_arg
86
{
87
  size_t number;
88
  enum format_arg_type type;
89
};
90
91
struct spec
92
{
93
  size_t directives;
94
  /* We consider a directive as "likely intentional" if it does not contain a
95
     space.  This prevents xgettext from flagging strings like "100% complete"
96
     as 'boost-format' if they don't occur in a context that requires a format
97
     string.  */
98
  size_t likely_intentional_directives;
99
  size_t numbered_arg_count;
100
  struct numbered_arg *numbered;
101
};
102
103
104
static int
105
numbered_arg_compare (const void *p1, const void *p2)
106
0
{
107
0
  size_t n1 = ((const struct numbered_arg *) p1)->number;
108
0
  size_t n2 = ((const struct numbered_arg *) p2)->number;
109
110
0
  return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
111
0
}
112
113
static void *
114
format_parse (const char *format, bool translated, char *fdi,
115
              char **invalid_reason)
116
0
{
117
0
  const char *const format_start = format;
118
119
0
  struct spec spec;
120
0
  spec.directives = 0;
121
0
  spec.likely_intentional_directives = 0;
122
0
  spec.numbered_arg_count = 0;
123
0
  spec.numbered = NULL;
124
0
  size_t numbered_allocated = 0;
125
0
  size_t unnumbered_arg_count = 0;
126
127
0
  for (; *format != '\0';)
128
    /* Invariant: spec.numbered_arg_count == 0 || unnumbered_arg_count == 0.  */
129
0
    if (*format++ == '%')
130
0
      {
131
        /* A directive.  */
132
0
        FDI_SET (format - 1, FMTDIR_START);
133
0
        spec.directives++;
134
0
        bool likely_intentional = true;
135
136
0
        if (*format == '%')
137
0
          format++;
138
0
        else
139
0
          {
140
0
            bool done = false;
141
0
            enum format_arg_type type = FAT_NONE;
142
143
0
            bool brackets = false;
144
0
            if (*format == '|')
145
0
              {
146
0
                format++;
147
0
                brackets = true;
148
0
              }
149
150
0
            size_t number = 0;
151
0
            if (c_isdigit (*format) && *format != '0')
152
0
              {
153
0
                const char *f = format;
154
0
                size_t m = 0;
155
156
0
                do
157
0
                  {
158
0
                    m = 10 * m + (*f - '0');
159
0
                    f++;
160
0
                  }
161
0
                while (c_isdigit (*f));
162
163
0
                if ((!brackets && *f == '%') || *f == '$')
164
0
                  {
165
0
                    if (m == 0) /* can happen if m overflows */
166
0
                      {
167
0
                        *invalid_reason = INVALID_ARGNO_0 (spec.directives);
168
0
                        FDI_SET (f, FMTDIR_ERROR);
169
0
                        goto bad_format;
170
0
                      }
171
0
                    number = m;
172
0
                    if (*f == '%')
173
0
                      {
174
0
                        type = FAT_ANY;
175
0
                        done = true;
176
0
                      }
177
0
                    format = ++f;
178
0
                  }
179
0
              }
180
181
0
            if (!done)
182
0
              {
183
                /* Parse flags.  */
184
0
                for (;;)
185
0
                  {
186
0
                    if (*format == ' ' || *format == '+' || *format == '-'
187
0
                        || *format == '#' || *format == '0' || *format == '\''
188
0
                        || *format == '_' || *format == '=' || *format == 'h'
189
0
                        || *format == 'l')
190
0
                      {
191
0
                        if (*format == ' ')
192
0
                          likely_intentional = false;
193
0
                        format++;
194
0
                      }
195
0
                    else
196
0
                      break;
197
0
                  }
198
199
                /* Parse width.  */
200
0
                if (*format == '*')
201
0
                  {
202
0
                    size_t width_number = 0;
203
204
0
                    format++;
205
206
0
                    if (c_isdigit (*format))
207
0
                      {
208
0
                        const char *f = format;
209
0
                        size_t m = 0;
210
211
0
                        do
212
0
                          {
213
0
                            m = 10 * m + (*f - '0');
214
0
                            f++;
215
0
                          }
216
0
                        while (c_isdigit (*f));
217
218
0
                        if (*f == '$')
219
0
                          {
220
0
                            if (m == 0)
221
0
                              {
222
0
                                *invalid_reason =
223
0
                                  INVALID_WIDTH_ARGNO_0 (spec.directives);
224
0
                                FDI_SET (f, FMTDIR_ERROR);
225
0
                                goto bad_format;
226
0
                              }
227
0
                            width_number = m;
228
0
                            format = ++f;
229
0
                          }
230
0
                      }
231
232
0
                    if (width_number)
233
0
                      {
234
                        /* Numbered argument.  */
235
236
                        /* Numbered and unnumbered specifications are
237
                           exclusive.  */
238
0
                        if (unnumbered_arg_count > 0)
239
0
                          {
240
0
                            *invalid_reason =
241
0
                              INVALID_MIXES_NUMBERED_UNNUMBERED ();
242
0
                            FDI_SET (format - 1, FMTDIR_ERROR);
243
0
                            goto bad_format;
244
0
                          }
245
246
0
                        if (numbered_allocated == spec.numbered_arg_count)
247
0
                          {
248
0
                            numbered_allocated = 2 * numbered_allocated + 1;
249
0
                            spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, numbered_allocated * sizeof (struct numbered_arg));
250
0
                          }
251
0
                        spec.numbered[spec.numbered_arg_count].number = width_number;
252
0
                        spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
253
0
                        spec.numbered_arg_count++;
254
0
                      }
255
0
                    else
256
0
                      {
257
                        /* Unnumbered argument.  */
258
259
                        /* Numbered and unnumbered specifications are
260
                           exclusive.  */
261
0
                        if (spec.numbered_arg_count > 0)
262
0
                          {
263
0
                            *invalid_reason =
264
0
                              INVALID_MIXES_NUMBERED_UNNUMBERED ();
265
0
                            FDI_SET (format - 1, FMTDIR_ERROR);
266
0
                            goto bad_format;
267
0
                          }
268
269
0
                        if (numbered_allocated == unnumbered_arg_count)
270
0
                          {
271
0
                            numbered_allocated = 2 * numbered_allocated + 1;
272
0
                            spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, numbered_allocated * sizeof (struct numbered_arg));
273
0
                          }
274
0
                        spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
275
0
                        spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
276
0
                        unnumbered_arg_count++;
277
0
                      }
278
0
                  }
279
0
                else if (c_isdigit (*format))
280
0
                  {
281
0
                    do format++; while (c_isdigit (*format));
282
0
                  }
283
284
                /* Parse precision.  */
285
0
                if (*format == '.')
286
0
                  {
287
0
                    format++;
288
289
0
                    if (*format == '*')
290
0
                      {
291
0
                        size_t precision_number = 0;
292
293
0
                        format++;
294
295
0
                        if (c_isdigit (*format))
296
0
                          {
297
0
                            const char *f = format;
298
0
                            size_t m = 0;
299
300
0
                            do
301
0
                              {
302
0
                                m = 10 * m + (*f - '0');
303
0
                                f++;
304
0
                              }
305
0
                            while (c_isdigit (*f));
306
307
0
                            if (*f == '$')
308
0
                              {
309
0
                                if (m == 0)
310
0
                                  {
311
0
                                    *invalid_reason =
312
0
                                      INVALID_PRECISION_ARGNO_0 (spec.directives);
313
0
                                    FDI_SET (f, FMTDIR_ERROR);
314
0
                                    goto bad_format;
315
0
                                  }
316
0
                                precision_number = m;
317
0
                                format = ++f;
318
0
                              }
319
0
                          }
320
321
0
                        if (precision_number)
322
0
                          {
323
                            /* Numbered argument.  */
324
325
                            /* Numbered and unnumbered specifications are
326
                               exclusive.  */
327
0
                            if (unnumbered_arg_count > 0)
328
0
                              {
329
0
                                *invalid_reason =
330
0
                                  INVALID_MIXES_NUMBERED_UNNUMBERED ();
331
0
                                FDI_SET (format - 1, FMTDIR_ERROR);
332
0
                                goto bad_format;
333
0
                              }
334
335
0
                            if (numbered_allocated == spec.numbered_arg_count)
336
0
                              {
337
0
                                numbered_allocated = 2 * numbered_allocated + 1;
338
0
                                spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, numbered_allocated * sizeof (struct numbered_arg));
339
0
                              }
340
0
                            spec.numbered[spec.numbered_arg_count].number = precision_number;
341
0
                            spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
342
0
                            spec.numbered_arg_count++;
343
0
                          }
344
0
                        else
345
0
                          {
346
                            /* Unnumbered argument.  */
347
348
                            /* Numbered and unnumbered specifications are
349
                               exclusive.  */
350
0
                            if (spec.numbered_arg_count > 0)
351
0
                              {
352
0
                                *invalid_reason =
353
0
                                  INVALID_MIXES_NUMBERED_UNNUMBERED ();
354
0
                                FDI_SET (format - 1, FMTDIR_ERROR);
355
0
                                goto bad_format;
356
0
                              }
357
358
0
                            if (numbered_allocated == unnumbered_arg_count)
359
0
                              {
360
0
                                numbered_allocated = 2 * numbered_allocated + 1;
361
0
                                spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, numbered_allocated  * sizeof (struct numbered_arg));
362
0
                              }
363
0
                            spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
364
0
                            spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
365
0
                            unnumbered_arg_count++;
366
0
                          }
367
0
                      }
368
0
                    else if (c_isdigit (*format))
369
0
                      {
370
0
                        do format++; while (c_isdigit (*format));
371
0
                      }
372
0
                  }
373
374
                /* Parse size.  */
375
0
                for (;;)
376
0
                  {
377
0
                    if (*format == 'h' || *format == 'l' || *format == 'L')
378
0
                      format++;
379
0
                    else
380
0
                      break;
381
0
                  }
382
383
0
                switch (*format++)
384
0
                  {
385
0
                  case 'c': case 'C':
386
0
                    type = FAT_CHAR;
387
0
                    break;
388
0
                  case 's': case 'S':
389
0
                    type = FAT_ANY;
390
0
                    break;
391
0
                  case 'i': case 'd': case 'o': case 'u': case 'x': case 'X':
392
0
                    type = FAT_INTEGER;
393
0
                    break;
394
0
                  case 'e': case 'E': case 'f': case 'g': case 'G':
395
0
                    type = FAT_DOUBLE;
396
0
                    break;
397
0
                  case 'p':
398
0
                    type = FAT_POINTER;
399
0
                    break;
400
0
                  case 't':
401
0
                    type = FAT_NONE;
402
0
                    break;
403
0
                  case 'T':
404
0
                    if (*format == '\0')
405
0
                      {
406
0
                        *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
407
0
                        FDI_SET (format - 1, FMTDIR_ERROR);
408
0
                        goto bad_format;
409
0
                      }
410
0
                    format++;
411
0
                    type = FAT_NONE;
412
0
                    break;
413
0
                  case 'n':
414
0
                    type = FAT_NONE;
415
0
                    break;
416
0
                  case '|':
417
0
                    if (brackets)
418
0
                      {
419
0
                        --format;
420
0
                        type = FAT_ANY;
421
0
                        break;
422
0
                      }
423
0
                    FALLTHROUGH;
424
0
                  default:
425
0
                    --format;
426
0
                    if (*format == '\0')
427
0
                      {
428
0
                        *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
429
0
                        FDI_SET (format - 1, FMTDIR_ERROR);
430
0
                      }
431
0
                    else
432
0
                      {
433
0
                        *invalid_reason =
434
0
                          INVALID_CONVERSION_SPECIFIER (spec.directives,
435
0
                                                        *format);
436
0
                        FDI_SET (format, FMTDIR_ERROR);
437
0
                      }
438
0
                    goto bad_format;
439
0
                  }
440
0
                if (brackets)
441
0
                  {
442
0
                    if (*format != '|')
443
0
                      {
444
0
                        if (*format == '\0')
445
0
                          {
446
0
                            *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
447
0
                            FDI_SET (format - 1, FMTDIR_ERROR);
448
0
                          }
449
0
                        else
450
0
                          {
451
0
                            *invalid_reason =
452
0
                              xasprintf (_("The directive number %zu starts with | but does not end with |."),
453
0
                                         spec.directives);
454
0
                            FDI_SET (format, FMTDIR_ERROR);
455
0
                          }
456
0
                        goto bad_format;
457
0
                      }
458
0
                    format++;
459
0
                  }
460
0
              }
461
462
0
            if (type != FAT_NONE)
463
0
              {
464
0
                if (number)
465
0
                  {
466
                    /* Numbered argument.  */
467
468
                    /* Numbered and unnumbered specifications are exclusive.  */
469
0
                    if (unnumbered_arg_count > 0)
470
0
                      {
471
0
                        *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
472
0
                        FDI_SET (format - 1, FMTDIR_ERROR);
473
0
                        goto bad_format;
474
0
                      }
475
476
0
                    if (numbered_allocated == spec.numbered_arg_count)
477
0
                      {
478
0
                        numbered_allocated = 2 * numbered_allocated + 1;
479
0
                        spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, numbered_allocated * sizeof (struct numbered_arg));
480
0
                      }
481
0
                    spec.numbered[spec.numbered_arg_count].number = number;
482
0
                    spec.numbered[spec.numbered_arg_count].type = type;
483
0
                    spec.numbered_arg_count++;
484
0
                  }
485
0
                else
486
0
                  {
487
                    /* Unnumbered argument.  */
488
489
                    /* Numbered and unnumbered specifications are exclusive.  */
490
0
                    if (spec.numbered_arg_count > 0)
491
0
                      {
492
0
                        *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
493
0
                        FDI_SET (format - 1, FMTDIR_ERROR);
494
0
                        goto bad_format;
495
0
                      }
496
497
0
                    if (numbered_allocated == unnumbered_arg_count)
498
0
                      {
499
0
                        numbered_allocated = 2 * numbered_allocated + 1;
500
0
                        spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, numbered_allocated * sizeof (struct numbered_arg));
501
0
                      }
502
0
                    spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
503
0
                    spec.numbered[unnumbered_arg_count].type = type;
504
0
                    unnumbered_arg_count++;
505
0
                  }
506
0
              }
507
0
          }
508
509
0
        if (likely_intentional)
510
0
          spec.likely_intentional_directives++;
511
0
        FDI_SET (format - 1, FMTDIR_END);
512
0
      }
513
514
  /* Convert the unnumbered argument array to numbered arguments.  */
515
0
  if (unnumbered_arg_count > 0)
516
0
    spec.numbered_arg_count = unnumbered_arg_count;
517
  /* Sort the numbered argument array, and eliminate duplicates.  */
518
0
  else if (spec.numbered_arg_count > 1)
519
0
    {
520
0
      qsort (spec.numbered, spec.numbered_arg_count,
521
0
             sizeof (struct numbered_arg), numbered_arg_compare);
522
523
      /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
524
0
      bool err = false;
525
0
      size_t i, j;
526
0
      for (i = j = 0; i < spec.numbered_arg_count; i++)
527
0
        if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
528
0
          {
529
0
            enum format_arg_type type1 = spec.numbered[i].type;
530
0
            enum format_arg_type type2 = spec.numbered[j-1].type;
531
532
0
            enum format_arg_type type_both;
533
0
            if (type1 == type2 || type2 == FAT_ANY)
534
0
              type_both = type1;
535
0
            else if (type1 == FAT_ANY)
536
0
              type_both = type2;
537
0
            else
538
0
              {
539
                /* Incompatible types.  */
540
0
                type_both = FAT_NONE;
541
0
                if (!err)
542
0
                  *invalid_reason =
543
0
                    INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
544
0
                err = true;
545
0
              }
546
547
0
            spec.numbered[j-1].type = type_both;
548
0
          }
549
0
        else
550
0
          {
551
0
            if (j < i)
552
0
              {
553
0
                spec.numbered[j].number = spec.numbered[i].number;
554
0
                spec.numbered[j].type = spec.numbered[i].type;
555
0
              }
556
0
            j++;
557
0
          }
558
0
      spec.numbered_arg_count = j;
559
0
      if (err)
560
        /* *invalid_reason has already been set above.  */
561
0
        goto bad_format;
562
0
    }
563
564
0
  struct spec *result = XMALLOC (struct spec);
565
0
  *result = spec;
566
0
  return result;
567
568
0
 bad_format:
569
0
  if (spec.numbered != NULL)
570
0
    free (spec.numbered);
571
0
  return NULL;
572
0
}
573
574
static void
575
format_free (void *descr)
576
0
{
577
0
  struct spec *spec = (struct spec *) descr;
578
579
0
  if (spec->numbered != NULL)
580
0
    free (spec->numbered);
581
0
  free (spec);
582
0
}
583
584
static int
585
format_get_number_of_directives (void *descr)
586
0
{
587
0
  struct spec *spec = (struct spec *) descr;
588
589
0
  return spec->directives;
590
0
}
591
592
static bool
593
format_is_unlikely_intentional (void *descr)
594
0
{
595
0
  struct spec *spec = (struct spec *) descr;
596
597
0
  return spec->likely_intentional_directives == 0;
598
0
}
599
600
static bool
601
format_check (void *msgid_descr, void *msgstr_descr, bool equality,
602
              formatstring_error_logger_t error_logger, void *error_logger_data,
603
              const char *pretty_msgid, const char *pretty_msgstr)
604
0
{
605
0
  struct spec *spec1 = (struct spec *) msgid_descr;
606
0
  struct spec *spec2 = (struct spec *) msgstr_descr;
607
0
  bool err = false;
608
609
0
  if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
610
0
    {
611
0
      size_t n1 = spec1->numbered_arg_count;
612
0
      size_t n2 = spec2->numbered_arg_count;
613
614
      /* Check that the argument numbers are the same.
615
         Both arrays are sorted.  We search for the first difference.  */
616
0
      {
617
0
        size_t i, j;
618
0
        for (i = 0, j = 0; i < n1 || j < n2; )
619
0
          {
620
0
            int cmp = (i >= n1 ? 1 :
621
0
                       j >= n2 ? -1 :
622
0
                       spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
623
0
                       spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
624
0
                       0);
625
626
0
            if (cmp > 0)
627
0
              {
628
0
                if (error_logger)
629
0
                  error_logger (error_logger_data,
630
0
                                _("a format specification for argument %zu, as in '%s', doesn't exist in '%s'"),
631
0
                                spec2->numbered[j].number, pretty_msgstr,
632
0
                                pretty_msgid);
633
0
                err = true;
634
0
                break;
635
0
              }
636
0
            else if (cmp < 0)
637
0
              {
638
0
                if (equality)
639
0
                  {
640
0
                    if (error_logger)
641
0
                      error_logger (error_logger_data,
642
0
                                    _("a format specification for argument %zu doesn't exist in '%s'"),
643
0
                                    spec1->numbered[i].number, pretty_msgstr);
644
0
                    err = true;
645
0
                    break;
646
0
                  }
647
0
                else
648
0
                  i++;
649
0
              }
650
0
            else
651
0
              j++, i++;
652
0
          }
653
0
      }
654
      /* Check the argument types are the same.  */
655
0
      if (!err)
656
0
        {
657
0
          size_t i, j;
658
0
          for (i = 0, j = 0; j < n2; )
659
0
            {
660
0
              if (spec1->numbered[i].number == spec2->numbered[j].number)
661
0
                {
662
0
                  if (spec1->numbered[i].type != spec2->numbered[j].type)
663
0
                    {
664
0
                      if (error_logger)
665
0
                        error_logger (error_logger_data,
666
0
                                      _("format specifications in '%s' and '%s' for argument %zu are not the same"),
667
0
                                      pretty_msgid, pretty_msgstr,
668
0
                                      spec2->numbered[j].number);
669
0
                      err = true;
670
0
                      break;
671
0
                    }
672
0
                  j++, i++;
673
0
                }
674
0
              else
675
0
                i++;
676
0
            }
677
0
        }
678
0
    }
679
680
0
  return err;
681
0
}
682
683
684
struct formatstring_parser formatstring_boost =
685
{
686
  format_parse,
687
  format_free,
688
  format_get_number_of_directives,
689
  format_is_unlikely_intentional,
690
  format_check
691
};
692
693
694
#ifdef TEST
695
696
/* Test program: Print the argument list specification returned by
697
   format_parse for strings read from standard input.  */
698
699
#include <stdio.h>
700
701
static void
702
format_print (void *descr)
703
{
704
  struct spec *spec = (struct spec *) descr;
705
706
  if (spec == NULL)
707
    {
708
      printf ("INVALID");
709
      return;
710
    }
711
712
  printf ("(");
713
  size_t last = 1;
714
  for (size_t i = 0; i < spec->numbered_arg_count; i++)
715
    {
716
      size_t number = spec->numbered[i].number;
717
718
      if (i > 0)
719
        printf (" ");
720
      if (number < last)
721
        abort ();
722
      for (; last < number; last++)
723
        printf ("_ ");
724
      switch (spec->numbered[i].type)
725
        {
726
        case FAT_INTEGER:
727
          printf ("i");
728
          break;
729
        case FAT_DOUBLE:
730
          printf ("f");
731
          break;
732
        case FAT_CHAR:
733
          printf ("c");
734
          break;
735
        case FAT_POINTER:
736
          printf ("p");
737
          break;
738
        case FAT_ANY:
739
          printf ("*");
740
          break;
741
        default:
742
          abort ();
743
        }
744
      last = number + 1;
745
    }
746
  printf (")");
747
}
748
749
int
750
main ()
751
{
752
  for (;;)
753
    {
754
      char *line = NULL;
755
      size_t line_size = 0;
756
      int line_len = getline (&line, &line_size, stdin);
757
      if (line_len < 0)
758
        break;
759
      if (line_len > 0 && line[line_len - 1] == '\n')
760
        line[--line_len] = '\0';
761
762
      char *invalid_reason = NULL;
763
      void *descr = format_parse (line, false, NULL, &invalid_reason);
764
765
      format_print (descr);
766
      printf ("\n");
767
      if (descr == NULL)
768
        printf ("%s\n", invalid_reason);
769
770
      free (invalid_reason);
771
      free (line);
772
    }
773
774
  return 0;
775
}
776
777
/*
778
 * For Emacs M-x compile
779
 * Local Variables:
780
 * 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-boost.c ../gnulib-lib/libgettextlib.la"
781
 * End:
782
 */
783
784
#endif /* TEST */
785