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-d.c
Line
Count
Source
1
/* D 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 <stdint.h>
23
#include <stdlib.h>
24
25
#include "format.h"
26
#include "attribute.h"
27
#include "c-ctype.h"
28
#include "gcd.h"
29
#include "xalloc.h"
30
#include "xvasprintf.h"
31
#include "format-invalid.h"
32
#include "minmax.h"
33
#include "gettext.h"
34
35
0
#define _(str) gettext (str)
36
37
38
/* Assertion macro.  Could be defined to empty for speed.  */
39
0
#define ASSERT(expr) if (!(expr)) abort ();
40
41
42
/* D format strings are described in the description of the std.format module
43
   <https://dlang.org/library/std/format.html> and implemented in
44
   gcc-14.2.0/libphobos/src/std/format/spec.d
45
   gcc-14.2.0/libphobos/src/std/format/write.d
46
   gcc-14.2.0/libphobos/src/std/format/internal/write.d .
47
48
   A format string consists of literal text (that is output verbatim), doubled
49
   percent-signs ('%%', that lead to a single percent-sign when output), and
50
   directives.
51
   A directive
52
   - starts with '%',
53
   - is optionally followed by
54
       a positive integer m, then '$', or
55
       a positive integer m, then ':', then a positive integer m₂ ≥ m, then '$', or
56
       a positive integer m, then ':', then '$',
57
   - is optionally followed by a sequence of flags, each being one of
58
       '+', '-', ' ', '0', '#', '=',
59
   - is optionally followed by a width specification:
60
       a positive integer, or
61
       '*', or
62
       '*', then a positive integer, then '$',
63
   - is optionally followed by a precision specification:
64
       '.' then optionally:
65
         a positive integer, or
66
         '*', or
67
         '*', then a positive integer, then '$',
68
   - is optionally followed by a separator specification:
69
       ',' then optionally:
70
         a positive integer, or
71
         '*',
72
       then optionally a '?',
73
   - is followed by
74
       either a format specifier
75
       or a compound specifier:
76
         - a '(',
77
         - a format string that eats 1 or 2 arguments,
78
         - optionally '%|' then literal text, possibly with doubled
79
           percent-signs,
80
         - '%)'.
81
 */
82
83
/* Data structure describing format string derived constraints for an
84
   argument list.  It is a recursive list structure.  Structure sharing
85
   is not allowed.  */
86
87
enum format_cdr_type
88
{
89
  FCT_REQUIRED, /* The format argument list cannot end before this argument.  */
90
  FCT_OPTIONAL  /* The format argument list may end before this argument.  */
91
};
92
93
enum format_arg_type
94
{
95
  FAT_NONE           = 0,
96
  FAT_BOOL           = 1 << 0,
97
  FAT_INTEGER        = 1 << 1,
98
  FAT_FLOATINGPOINT  = 1 << 2,
99
  FAT_CHAR           = 1 << 3,
100
  FAT_ARRAY          = 1 << 4, /* string or array */
101
  FAT_ASSOCIATIVE    = 1 << 5,
102
  FAT_IRANGE         = 1 << 6, /* irange or simd */
103
  FAT_STRUCT         = 1 << 7, /* struct or class or union */
104
  FAT_POINTER        = 1 << 8, /* pointer or null */
105
  /* Note: enum are not listed here, since enum values can be formatted with
106
     any specifier available for their base type.  */
107
  FAT_ANY_TYPE       = (FAT_BOOL | FAT_INTEGER | FAT_FLOATINGPOINT | FAT_CHAR
108
                        | FAT_ARRAY | FAT_ASSOCIATIVE | FAT_IRANGE | FAT_STRUCT
109
                        | FAT_POINTER),
110
  /* A flag: */
111
  FAT_ELEMENTWISE    = 1 << 10,
112
  /* Combination of allowed types and flag: */
113
  FAT_ELEMENTWISE_1  = FAT_ELEMENTWISE | FAT_ARRAY | FAT_IRANGE,
114
  FAT_ELEMENTWISE_2  = FAT_ELEMENTWISE | FAT_ASSOCIATIVE
115
};
116
117
struct format_arg
118
{
119
  size_t repcount;       /* Number of consecutive arguments this constraint
120
                            applies to.  Normally 1, but unconstrained
121
                            arguments are often repeated.  */
122
  enum format_cdr_type presence; /* Can the argument list end right before
123
                                    this argument?  */
124
  enum format_arg_type type;    /* Possible values for this argument.  */
125
  struct format_arg_list *list; /* For FAT_ELEMENTWISE.  */
126
};
127
128
struct segment
129
{
130
  size_t count;          /* Number of format_arg records used.  */
131
  size_t allocated;
132
  struct format_arg *element;   /* Argument constraints.  */
133
  size_t length;         /* Number of arguments represented by this segment.
134
                            This is the sum of all repcounts in the segment.  */
135
};
136
137
struct format_arg_list
138
{
139
  /* The constraints for the potentially infinite argument list are assumed
140
     to become ultimately periodic.  Such a periodic sequence can be split into
141
     an initial segment and an endlessly repeated loop segment.
142
     A finite sequence is represented entirely in the initial segment; the
143
     loop segment is empty.
144
     In this file, the loop segment is always either empty or has length 1.
145
     But it is not worth exploiting this property: The code is more future-proof
146
     in the general form, shared with format-lisp.c and format-scheme.c.  */
147
148
  struct segment initial;       /* Initial arguments segment.  */
149
  struct segment repeated;      /* Endlessly repeated segment.  */
150
};
151
152
struct spec
153
{
154
  size_t directives;
155
  /* We consider a directive as "likely intentional" if it does not contain a
156
     space.  This prevents xgettext from flagging strings like "100% complete"
157
     as 'd-format' if they don't occur in a context that requires a format
158
     string.  */
159
  size_t likely_intentional_directives;
160
  struct format_arg_list *list;
161
};
162
163
164
/* Forward declaration of local functions.  */
165
static void verify_list (const struct format_arg_list *list);
166
static void free_list (struct format_arg_list *list);
167
static struct format_arg_list * copy_list (const struct format_arg_list *list);
168
static bool equal_list (const struct format_arg_list *list1,
169
                        const struct format_arg_list *list2);
170
static struct format_arg_list * make_intersected_list
171
                                               (struct format_arg_list *list1,
172
                                                struct format_arg_list *list2);
173
174
175
/* ======================= Verify a format_arg_list ======================= */
176
177
/* Verify some invariants.  */
178
static void
179
verify_element (const struct format_arg * e)
180
0
{
181
0
  ASSERT (e->repcount > 0);
182
0
  if (e->type & FAT_ELEMENTWISE)
183
0
    verify_list (e->list);
184
0
}
185
186
/* Verify some invariants.  */
187
/* Memory effects: none.  */
188
static void
189
verify_list (const struct format_arg_list *list)
190
0
{
191
0
  ASSERT (list->initial.count <= list->initial.allocated);
192
0
  {
193
0
    size_t total_repcount;
194
195
0
    total_repcount = 0;
196
0
    for (size_t i = 0; i < list->initial.count; i++)
197
0
      {
198
0
        verify_element (&list->initial.element[i]);
199
0
        total_repcount += list->initial.element[i].repcount;
200
0
      }
201
202
0
    ASSERT (total_repcount == list->initial.length);
203
0
  }
204
205
0
  ASSERT (list->repeated.count <= list->repeated.allocated);
206
0
  {
207
0
    size_t total_repcount;
208
209
0
    total_repcount = 0;
210
0
    for (size_t i = 0; i < list->repeated.count; i++)
211
0
      {
212
0
        verify_element (&list->repeated.element[i]);
213
0
        total_repcount += list->repeated.element[i].repcount;
214
0
      }
215
216
0
    ASSERT (total_repcount == list->repeated.length);
217
0
  }
218
0
}
219
220
/* Assertion macro.  Could be defined to empty for speed.  */
221
0
#define VERIFY_LIST(list) verify_list (list)
222
223
224
/* ======================== Free a format_arg_list ======================== */
225
226
/* Free the data belonging to an argument list element.  */
227
static inline void
228
free_element (struct format_arg *element)
229
0
{
230
0
  if (element->type & FAT_ELEMENTWISE)
231
0
    free_list (element->list);
232
0
}
233
234
/* Free an argument list.  */
235
/* Memory effects: Frees list.  */
236
static void
237
free_list (struct format_arg_list *list)
238
0
{
239
0
  for (size_t i = 0; i < list->initial.count; i++)
240
0
    free_element (&list->initial.element[i]);
241
0
  if (list->initial.element != NULL)
242
0
    free (list->initial.element);
243
244
0
  for (size_t i = 0; i < list->repeated.count; i++)
245
0
    free_element (&list->repeated.element[i]);
246
0
  if (list->repeated.element != NULL)
247
0
    free (list->repeated.element);
248
0
}
249
250
251
/* ======================== Copy a format_arg_list ======================== */
252
253
/* Copy the data belonging to an argument list element.  */
254
static inline void
255
copy_element (struct format_arg *newelement,
256
              const struct format_arg *oldelement)
257
0
{
258
0
  newelement->repcount = oldelement->repcount;
259
0
  newelement->presence = oldelement->presence;
260
0
  newelement->type = oldelement->type;
261
0
  if (oldelement->type & FAT_ELEMENTWISE)
262
0
    newelement->list = copy_list (oldelement->list);
263
0
}
264
265
/* Copy an argument list.  */
266
/* Memory effects: Freshly allocated result.  */
267
static struct format_arg_list *
268
copy_list (const struct format_arg_list *list)
269
0
{
270
0
  VERIFY_LIST (list);
271
272
0
  struct format_arg_list *newlist = XMALLOC (struct format_arg_list);
273
274
0
  newlist->initial.count = newlist->initial.allocated = list->initial.count;
275
0
  {
276
0
    size_t length = 0;
277
0
    if (list->initial.count == 0)
278
0
      newlist->initial.element = NULL;
279
0
    else
280
0
      {
281
0
        newlist->initial.element =
282
0
          XNMALLOC (newlist->initial.allocated, struct format_arg);
283
0
        for (size_t i = 0; i < list->initial.count; i++)
284
0
          {
285
0
            copy_element (&newlist->initial.element[i],
286
0
                          &list->initial.element[i]);
287
0
            length += list->initial.element[i].repcount;
288
0
          }
289
0
      }
290
0
    ASSERT (length == list->initial.length);
291
0
    newlist->initial.length = length;
292
0
  }
293
294
0
  newlist->repeated.count = newlist->repeated.allocated = list->repeated.count;
295
0
  {
296
0
    size_t length = 0;
297
0
    if (list->repeated.count == 0)
298
0
      newlist->repeated.element = NULL;
299
0
    else
300
0
      {
301
0
        newlist->repeated.element =
302
0
          XNMALLOC (newlist->repeated.allocated, struct format_arg);
303
0
        for (size_t i = 0; i < list->repeated.count; i++)
304
0
          {
305
0
            copy_element (&newlist->repeated.element[i],
306
0
                          &list->repeated.element[i]);
307
0
            length += list->repeated.element[i].repcount;
308
0
          }
309
0
      }
310
0
    ASSERT (length == list->repeated.length);
311
0
    newlist->repeated.length = length;
312
0
  }
313
314
0
  VERIFY_LIST (newlist);
315
316
0
  return newlist;
317
0
}
318
319
320
/* ===================== Compare two format_arg_lists ===================== */
321
322
/* Tests whether two normalized argument constraints are equivalent,
323
   ignoring the repcount.  */
324
static bool
325
equal_element (const struct format_arg * e1, const struct format_arg * e2)
326
0
{
327
0
  return (e1->presence == e2->presence
328
0
          && e1->type == e2->type
329
0
          && (e1->type & FAT_ELEMENTWISE ? equal_list (e1->list, e2->list) :
330
0
              true));
331
0
}
332
333
/* Tests whether two normalized argument list constraints are equivalent.  */
334
/* Memory effects: none.  */
335
static bool
336
equal_list (const struct format_arg_list *list1,
337
            const struct format_arg_list *list2)
338
0
{
339
0
  VERIFY_LIST (list1);
340
0
  VERIFY_LIST (list2);
341
342
0
  {
343
0
    size_t n = list1->initial.count;
344
0
    if (n != list2->initial.count)
345
0
      return false;
346
0
    for (size_t i = 0; i < n; i++)
347
0
      {
348
0
        const struct format_arg * e1 = &list1->initial.element[i];
349
0
        const struct format_arg * e2 = &list2->initial.element[i];
350
351
0
        if (!(e1->repcount == e2->repcount && equal_element (e1, e2)))
352
0
          return false;
353
0
      }
354
0
  }
355
0
  {
356
0
    size_t n = list1->repeated.count;
357
0
    if (n != list2->repeated.count)
358
0
      return false;
359
0
    for (size_t i = 0; i < n; i++)
360
0
      {
361
0
        const struct format_arg * e1 = &list1->repeated.element[i];
362
0
        const struct format_arg * e2 = &list2->repeated.element[i];
363
364
0
        if (!(e1->repcount == e2->repcount && equal_element (e1, e2)))
365
0
          return false;
366
0
      }
367
0
  }
368
369
0
  return true;
370
0
}
371
372
373
/* ===================== Incremental memory allocation ===================== */
374
375
/* Ensure list->initial.allocated >= newcount.  */
376
static inline void
377
ensure_initial_alloc (struct format_arg_list *list, size_t newcount)
378
0
{
379
0
  if (newcount > list->initial.allocated)
380
0
    {
381
0
      list->initial.allocated =
382
0
        MAX (2 * list->initial.allocated + 1, newcount);
383
0
      list->initial.element =
384
0
        (struct format_arg *)
385
0
        xrealloc (list->initial.element,
386
0
                  list->initial.allocated * sizeof (struct format_arg));
387
0
    }
388
0
}
389
390
/* Ensure list->initial.allocated > list->initial.count.  */
391
static inline void
392
grow_initial_alloc (struct format_arg_list *list)
393
0
{
394
0
  if (list->initial.count >= list->initial.allocated)
395
0
    {
396
0
      list->initial.allocated =
397
0
        MAX (2 * list->initial.allocated + 1, list->initial.count + 1);
398
0
      list->initial.element =
399
0
        (struct format_arg *)
400
0
        xrealloc (list->initial.element,
401
0
                  list->initial.allocated * sizeof (struct format_arg));
402
0
    }
403
0
}
404
405
/* Ensure list->repeated.allocated >= newcount.  */
406
static inline void
407
ensure_repeated_alloc (struct format_arg_list *list, size_t newcount)
408
0
{
409
0
  if (newcount > list->repeated.allocated)
410
0
    {
411
0
      list->repeated.allocated =
412
0
        MAX (2 * list->repeated.allocated + 1, newcount);
413
0
      list->repeated.element =
414
0
        (struct format_arg *)
415
0
        xrealloc (list->repeated.element,
416
0
                  list->repeated.allocated * sizeof (struct format_arg));
417
0
    }
418
0
}
419
420
/* Ensure list->repeated.allocated > list->repeated.count.  */
421
static inline void
422
grow_repeated_alloc (struct format_arg_list *list)
423
0
{
424
0
  if (list->repeated.count >= list->repeated.allocated)
425
0
    {
426
0
      list->repeated.allocated =
427
0
        MAX (2 * list->repeated.allocated + 1, list->repeated.count + 1);
428
0
      list->repeated.element =
429
0
        (struct format_arg *)
430
0
        xrealloc (list->repeated.element,
431
0
                  list->repeated.allocated * sizeof (struct format_arg));
432
0
    }
433
0
}
434
435
436
/* ====================== Normalize a format_arg_list ====================== */
437
438
/* Normalize an argument list constraint, assuming all sublists are already
439
   normalized.  */
440
/* Memory effects: Destructively modifies list.  */
441
static void
442
normalize_outermost_list (struct format_arg_list *list)
443
0
{
444
  /* Step 1: Combine adjacent elements.
445
     Copy from i to j, keeping 0 <= j <= i.  */
446
0
  {
447
0
    size_t n = list->initial.count;
448
0
    size_t i, j;
449
0
    for (i = j = 0; i < n; i++)
450
0
      if (j > 0
451
0
          && equal_element (&list->initial.element[i],
452
0
                            &list->initial.element[j-1]))
453
0
        {
454
0
          list->initial.element[j-1].repcount +=
455
0
            list->initial.element[i].repcount;
456
0
          free_element (&list->initial.element[i]);
457
0
        }
458
0
      else
459
0
        {
460
0
          if (j < i)
461
0
            list->initial.element[j] = list->initial.element[i];
462
0
          j++;
463
0
        }
464
0
    list->initial.count = j;
465
0
  }
466
0
  {
467
0
    size_t n = list->repeated.count;
468
0
    size_t i, j;
469
0
    for (i = j = 0; i < n; i++)
470
0
      if (j > 0
471
0
          && equal_element (&list->repeated.element[i],
472
0
                            &list->repeated.element[j-1]))
473
0
        {
474
0
          list->repeated.element[j-1].repcount +=
475
0
            list->repeated.element[i].repcount;
476
0
          free_element (&list->repeated.element[i]);
477
0
        }
478
0
      else
479
0
        {
480
0
          if (j < i)
481
0
            list->repeated.element[j] = list->repeated.element[i];
482
0
          j++;
483
0
        }
484
0
    list->repeated.count = j;
485
0
  }
486
487
  /* Nothing more to be done if the loop segment is empty.  */
488
0
  if (list->repeated.count > 0)
489
0
    {
490
0
      size_t repcount0_extra;
491
492
      /* Step 2: Reduce the loop period.  */
493
0
      size_t n = list->repeated.count;
494
0
      repcount0_extra = 0;
495
0
      if (n > 1
496
0
          && equal_element (&list->repeated.element[0],
497
0
                            &list->repeated.element[n-1]))
498
0
        {
499
0
          repcount0_extra = list->repeated.element[n-1].repcount;
500
0
          n--;
501
0
        }
502
      /* Proceed as if the loop period were n, with
503
         list->repeated.element[0].repcount incremented by repcount0_extra.  */
504
0
      for (size_t m = 2; m <= n / 2; m++)
505
0
        if ((n % m) == 0)
506
0
          {
507
            /* m is a divisor of n.  Try to reduce the loop period to n.  */
508
0
            bool ok = true;
509
510
0
            for (size_t i = 0; i < n - m; i++)
511
0
              if (!((list->repeated.element[i].repcount
512
0
                     + (i == 0 ? repcount0_extra : 0)
513
0
                     == list->repeated.element[i+m].repcount)
514
0
                    && equal_element (&list->repeated.element[i],
515
0
                                      &list->repeated.element[i+m])))
516
0
                {
517
0
                  ok = false;
518
0
                  break;
519
0
                }
520
0
            if (ok)
521
0
              {
522
0
                for (size_t i = m; i < n; i++)
523
0
                  free_element (&list->repeated.element[i]);
524
0
                if (n < list->repeated.count)
525
0
                  list->repeated.element[m] = list->repeated.element[n];
526
0
                list->repeated.count = list->repeated.count - n + m;
527
0
                list->repeated.length /= n / m;
528
0
                break;
529
0
              }
530
0
          }
531
0
      if (list->repeated.count == 1)
532
0
        {
533
          /* The loop has period 1.  Normalize the repcount.  */
534
0
          list->repeated.element[0].repcount = 1;
535
0
          list->repeated.length = 1;
536
0
        }
537
538
      /* Step 3: Roll as much as possible of the initial segment's tail
539
         into the loop.  */
540
0
      if (list->repeated.count == 1)
541
0
        {
542
0
          if (list->initial.count > 0
543
0
              && equal_element (&list->initial.element[list->initial.count-1],
544
0
                                &list->repeated.element[0]))
545
0
            {
546
              /* Roll the last element of the initial segment into the loop.
547
                 Its repcount is irrelevant.  The second-to-last element is
548
                 certainly different and doesn't need to be considered.  */
549
0
              list->initial.length -=
550
0
                list->initial.element[list->initial.count-1].repcount;
551
0
              free_element (&list->initial.element[list->initial.count-1]);
552
0
              list->initial.count--;
553
0
            }
554
0
        }
555
0
      else
556
0
        {
557
0
          while (list->initial.count > 0
558
0
                 && equal_element (&list->initial.element[list->initial.count-1],
559
0
                                   &list->repeated.element[list->repeated.count-1]))
560
0
            {
561
0
              size_t moved_repcount =
562
0
                MIN (list->initial.element[list->initial.count-1].repcount,
563
0
                     list->repeated.element[list->repeated.count-1].repcount);
564
565
              /* Add the element at the start of list->repeated.  */
566
0
              if (equal_element (&list->repeated.element[0],
567
0
                                 &list->repeated.element[list->repeated.count-1]))
568
0
                list->repeated.element[0].repcount += moved_repcount;
569
0
              else
570
0
                {
571
0
                  size_t newcount = list->repeated.count + 1;
572
0
                  ensure_repeated_alloc (list, newcount);
573
0
                  for (size_t i = newcount - 1; i > 0; i--)
574
0
                    list->repeated.element[i] = list->repeated.element[i-1];
575
0
                  list->repeated.count = newcount;
576
0
                  copy_element (&list->repeated.element[0],
577
0
                                &list->repeated.element[list->repeated.count-1]);
578
0
                  list->repeated.element[0].repcount = moved_repcount;
579
0
                }
580
581
              /* Remove the element from the end of list->repeated.  */
582
0
              list->repeated.element[list->repeated.count-1].repcount -=
583
0
                moved_repcount;
584
0
              if (list->repeated.element[list->repeated.count-1].repcount == 0)
585
0
                {
586
0
                  free_element (&list->repeated.element[list->repeated.count-1]);
587
0
                  list->repeated.count--;
588
0
                }
589
590
              /* Remove the element from the end of list->initial.  */
591
0
              list->initial.element[list->initial.count-1].repcount -=
592
0
                moved_repcount;
593
0
              if (list->initial.element[list->initial.count-1].repcount == 0)
594
0
                {
595
0
                  free_element (&list->initial.element[list->initial.count-1]);
596
0
                  list->initial.count--;
597
0
                }
598
0
              list->initial.length -= moved_repcount;
599
0
            }
600
0
        }
601
0
    }
602
0
}
603
604
/* Normalize an argument list constraint.  */
605
/* Memory effects: Destructively modifies list.  */
606
static void
607
normalize_list (struct format_arg_list *list)
608
0
{
609
0
  VERIFY_LIST (list);
610
611
  /* First normalize all elements, recursively.  */
612
0
  {
613
0
    size_t n = list->initial.count;
614
0
    for (size_t i = 0; i < n; i++)
615
0
      if (list->initial.element[i].type & FAT_ELEMENTWISE)
616
0
        normalize_list (list->initial.element[i].list);
617
0
  }
618
0
  {
619
0
    size_t n = list->repeated.count;
620
0
    for (size_t i = 0; i < n; i++)
621
0
      if (list->repeated.element[i].type & FAT_ELEMENTWISE)
622
0
        normalize_list (list->repeated.element[i].list);
623
0
  }
624
625
  /* Then normalize the top level list.  */
626
0
  normalize_outermost_list (list);
627
628
0
  VERIFY_LIST (list);
629
0
}
630
631
632
/* ===================== Unconstrained and empty lists ===================== */
633
634
/* It's easier to allocate these on demand, than to be careful not to
635
   accidentally modify statically allocated lists.  */
636
637
638
/* Create an unconstrained argument list.  */
639
/* Memory effects: Freshly allocated result.  */
640
static struct format_arg_list *
641
make_unconstrained_list ()
642
0
{
643
0
  struct format_arg_list *list = XMALLOC (struct format_arg_list);
644
0
  list->initial.count = 0;
645
0
  list->initial.allocated = 0;
646
0
  list->initial.element = NULL;
647
0
  list->initial.length = 0;
648
0
  list->repeated.count = 1;
649
0
  list->repeated.allocated = 1;
650
0
  list->repeated.element = XNMALLOC (1, struct format_arg);
651
0
  list->repeated.element[0].repcount = 1;
652
0
  list->repeated.element[0].presence = FCT_OPTIONAL;
653
0
  list->repeated.element[0].type = FAT_ANY_TYPE;
654
0
  list->repeated.length = 1;
655
656
0
  VERIFY_LIST (list);
657
658
0
  return list;
659
0
}
660
661
662
/* Create an empty argument list.  */
663
/* Memory effects: Freshly allocated result.  */
664
static struct format_arg_list *
665
make_empty_list ()
666
0
{
667
0
  struct format_arg_list *list = XMALLOC (struct format_arg_list);
668
0
  list->initial.count = 0;
669
0
  list->initial.allocated = 0;
670
0
  list->initial.element = NULL;
671
0
  list->initial.length = 0;
672
0
  list->repeated.count = 0;
673
0
  list->repeated.allocated = 0;
674
0
  list->repeated.element = NULL;
675
0
  list->repeated.length = 0;
676
0
677
0
  VERIFY_LIST (list);
678
0
679
0
  return list;
680
0
}
681
682
683
/* Test for an empty list.  */
684
/* Memory effects: none.  */
685
MAYBE_UNUSED static bool
686
is_empty_list (const struct format_arg_list *list)
687
0
{
688
0
  return (list->initial.count == 0 && list->repeated.count == 0);
689
0
}
690
691
692
/* ======================== format_arg_list surgery ======================== */
693
694
/* Unfold list->repeated m times, where m >= 1.
695
   Assumes list->repeated.count > 0.  */
696
/* Memory effects: list is destructively modified.  */
697
static void
698
unfold_loop (struct format_arg_list *list, size_t m)
699
0
{
700
0
  if (m > 1)
701
0
    {
702
0
      size_t newcount = list->repeated.count * m;
703
0
      ensure_repeated_alloc (list, newcount);
704
0
      size_t i = list->repeated.count;
705
0
      for (size_t k = 1; k < m; k++)
706
0
        for (size_t j = 0; j < list->repeated.count; j++)
707
0
          {
708
0
            copy_element (&list->repeated.element[i], &list->repeated.element[j]);
709
0
            i++;
710
0
          }
711
0
      list->repeated.count = newcount;
712
0
      list->repeated.length = list->repeated.length * m;
713
0
    }
714
0
}
715
716
/* Ensure list->initial.length := m, where m >= list->initial.length.
717
   Assumes list->repeated.count > 0.  */
718
/* Memory effects: list is destructively modified.  */
719
static void
720
rotate_loop (struct format_arg_list *list, size_t m)
721
0
{
722
0
  if (m == list->initial.length)
723
0
    return;
724
725
0
  if (list->repeated.count == 1)
726
0
    {
727
      /* Instead of multiple copies of list->repeated.element[0], a single
728
         copy with higher repcount is appended to list->initial.  */
729
0
      size_t newcount = list->initial.count + 1;
730
0
      ensure_initial_alloc (list, newcount);
731
0
      size_t i = list->initial.count;
732
0
      copy_element (&list->initial.element[i], &list->repeated.element[0]);
733
0
      list->initial.element[i].repcount = m - list->initial.length;
734
0
      list->initial.count = newcount;
735
0
      list->initial.length = m;
736
0
    }
737
0
  else
738
0
    {
739
0
      size_t n = list->repeated.length;
740
741
      /* Write m = list->initial.length + q * n + r with 0 <= r < n.  */
742
0
      size_t q = (m - list->initial.length) / n;
743
0
      size_t r = (m - list->initial.length) % n;
744
745
      /* Determine how many entries of list->repeated are needed for
746
         length r.  */
747
0
      size_t s;
748
0
      size_t t;
749
750
0
      for (t = r, s = 0;
751
0
           s < list->repeated.count && t >= list->repeated.element[s].repcount;
752
0
           t -= list->repeated.element[s].repcount, s++)
753
0
        ;
754
755
      /* s must be < list->repeated.count, otherwise r would have been >= n.  */
756
0
      ASSERT (s < list->repeated.count);
757
758
      /* So we need to add to list->initial:
759
         q full copies of list->repeated,
760
         plus the s first elements of list->repeated,
761
         plus, if t > 0, a splitoff of list->repeated.element[s].  */
762
0
      {
763
0
        size_t i = list->initial.count;
764
0
        size_t newcount = i + q * list->repeated.count + s + (t > 0 ? 1 : 0);
765
0
        ensure_initial_alloc (list, newcount);
766
0
        for (size_t k = 0; k < q; k++)
767
0
          for (size_t j = 0; j < list->repeated.count; j++)
768
0
            {
769
0
              copy_element (&list->initial.element[i], &list->repeated.element[j]);
770
0
              i++;
771
0
            }
772
0
        for (size_t j = 0; j < s; j++)
773
0
          {
774
0
            copy_element (&list->initial.element[i], &list->repeated.element[j]);
775
0
            i++;
776
0
          }
777
0
        if (t > 0)
778
0
          {
779
0
            copy_element (&list->initial.element[i], &list->repeated.element[s]);
780
0
            list->initial.element[i].repcount = t;
781
0
            i++;
782
0
          }
783
0
        ASSERT (i == newcount);
784
0
        list->initial.count = newcount;
785
        /* The new length of the initial segment is
786
           = list->initial.length
787
             + q * list->repeated.length
788
             + list->repeated[0..s-1].repcount + t
789
           = list->initial.length + q * n + r
790
           = m.
791
         */
792
0
        list->initial.length = m;
793
0
      }
794
795
      /* And rotate list->repeated.  */
796
0
      if (r > 0)
797
0
        {
798
0
          size_t oldcount = list->repeated.count;
799
0
          size_t newcount = list->repeated.count + (t > 0 ? 1 : 0);
800
0
          struct format_arg *newelement = XNMALLOC (newcount, struct format_arg);
801
0
          size_t i = 0;
802
0
          for (size_t j = s; j < oldcount; j++)
803
0
            {
804
0
              newelement[i] = list->repeated.element[j];
805
0
              i++;
806
0
            }
807
0
          for (size_t j = 0; j < s; j++)
808
0
            {
809
0
              newelement[i] = list->repeated.element[j];
810
0
              i++;
811
0
            }
812
0
          if (t > 0)
813
0
            {
814
0
              copy_element (&newelement[oldcount], &newelement[0]);
815
0
              newelement[0].repcount -= t;
816
0
              newelement[oldcount].repcount = t;
817
0
            }
818
0
          free (list->repeated.element);
819
0
          list->repeated.element = newelement;
820
0
          list->repeated.count = newcount;
821
0
        }
822
0
    }
823
0
}
824
825
826
/* Ensure index n in the initial segment falls on a split between elements,
827
   i.e. if 0 < n < list->initial.length, then n-1 and n are covered by two
828
   different adjacent elements.  */
829
/* Memory effects: list is destructively modified.  */
830
static size_t
831
initial_splitelement (struct format_arg_list *list, size_t n)
832
0
{
833
0
  VERIFY_LIST (list);
834
835
0
  if (n > list->initial.length)
836
0
    {
837
0
      ASSERT (list->repeated.count > 0);
838
0
      rotate_loop (list, n);
839
0
      ASSERT (n <= list->initial.length);
840
0
    }
841
842
  /* Determine how many entries of list->initial need to be skipped.  */
843
0
  size_t s;
844
0
  size_t t;
845
0
  for (t = n, s = 0;
846
0
       s < list->initial.count && t >= list->initial.element[s].repcount;
847
0
       t -= list->initial.element[s].repcount, s++)
848
0
    ;
849
850
0
  if (t == 0)
851
0
    return s;
852
853
0
  ASSERT (s < list->initial.count);
854
855
  /* Split the entry into two entries.  */
856
0
  size_t oldrepcount = list->initial.element[s].repcount;
857
0
  size_t newcount = list->initial.count + 1;
858
0
  ensure_initial_alloc (list, newcount);
859
0
  for (size_t i = list->initial.count - 1; i > s; i--)
860
0
    list->initial.element[i+1] = list->initial.element[i];
861
0
  copy_element (&list->initial.element[s+1], &list->initial.element[s]);
862
0
  list->initial.element[s].repcount = t;
863
0
  list->initial.element[s+1].repcount = oldrepcount - t;
864
0
  list->initial.count = newcount;
865
866
0
  VERIFY_LIST (list);
867
868
0
  return s+1;
869
0
}
870
871
872
/* Ensure index n in the initial segment is not shared.  Return its index.  */
873
/* Memory effects: list is destructively modified.  */
874
MAYBE_UNUSED static size_t
875
initial_unshare (struct format_arg_list *list, size_t n)
876
0
{
877
0
  /* This does the same side effects as
878
0
       initial_splitelement (list, n);
879
0
       initial_splitelement (list, n + 1);
880
0
   */
881
0
882
0
  VERIFY_LIST (list);
883
0
884
0
  if (n >= list->initial.length)
885
0
    {
886
0
      ASSERT (list->repeated.count > 0);
887
0
      rotate_loop (list, n + 1);
888
0
      ASSERT (n < list->initial.length);
889
0
    }
890
0
891
0
  /* Determine how many entries of list->initial need to be skipped.  */
892
0
  size_t s;
893
0
  size_t t;
894
0
  for (t = n, s = 0;
895
0
       s < list->initial.count && t >= list->initial.element[s].repcount;
896
0
       t -= list->initial.element[s].repcount, s++)
897
0
    ;
898
0
899
0
  /* s must be < list->initial.count.  */
900
0
  ASSERT (s < list->initial.count);
901
0
902
0
  if (list->initial.element[s].repcount > 1)
903
0
    {
904
0
      /* Split the entry into at most three entries: for indices < n,
905
0
         for index n, and for indices > n.  */
906
0
      size_t oldrepcount = list->initial.element[s].repcount;
907
0
      size_t newcount =
908
0
        list->initial.count + (t == 0 || t == oldrepcount - 1 ? 1 : 2);
909
0
      ensure_initial_alloc (list, newcount);
910
0
      if (t == 0 || t == oldrepcount - 1)
911
0
        {
912
0
          for (size_t i = list->initial.count - 1; i > s; i--)
913
0
            list->initial.element[i+1] = list->initial.element[i];
914
0
          copy_element (&list->initial.element[s+1], &list->initial.element[s]);
915
0
          if (t == 0)
916
0
            {
917
0
              list->initial.element[s].repcount = 1;
918
0
              list->initial.element[s+1].repcount = oldrepcount - 1;
919
0
            }
920
0
          else
921
0
            {
922
0
              list->initial.element[s].repcount = oldrepcount - 1;
923
0
              list->initial.element[s+1].repcount = 1;
924
0
            }
925
0
        }
926
0
      else
927
0
        {
928
0
          for (size_t i = list->initial.count - 1; i > s; i--)
929
0
            list->initial.element[i+2] = list->initial.element[i];
930
0
          copy_element (&list->initial.element[s+2], &list->initial.element[s]);
931
0
          copy_element (&list->initial.element[s+1], &list->initial.element[s]);
932
0
          list->initial.element[s].repcount = t;
933
0
          list->initial.element[s+1].repcount = 1;
934
0
          list->initial.element[s+2].repcount = oldrepcount - 1 - t;
935
0
        }
936
0
      list->initial.count = newcount;
937
0
      if (t > 0)
938
0
        s++;
939
0
    }
940
0
941
0
  /* Now the entry for index n has repcount 1.  */
942
0
  ASSERT (list->initial.element[s].repcount == 1);
943
0
944
0
  VERIFY_LIST (list);
945
0
946
0
  return s;
947
0
}
948
949
950
/* ================= Intersection of two format_arg_lists ================= */
951
952
/* Create the intersection (i.e. combined constraints) of two argument
953
   constraints.  Return false if the intersection is empty, i.e. if the
954
   two constraints give a contradiction.  */
955
/* Memory effects: Freshly allocated element's sublist.  */
956
static bool
957
make_intersected_element (struct format_arg *re,
958
                          const struct format_arg * e1,
959
                          const struct format_arg * e2)
960
0
{
961
  /* Intersect the cdr types.  */
962
0
  if (e1->presence == FCT_REQUIRED || e2->presence == FCT_REQUIRED)
963
0
    re->presence = FCT_REQUIRED;
964
0
  else
965
0
    re->presence = FCT_OPTIONAL;
966
967
  /* Intersect the arg types.  */
968
0
  if (e1->type == FAT_ANY_TYPE)
969
0
    {
970
0
      re->type = e2->type;
971
0
      if (e2->type & FAT_ELEMENTWISE)
972
0
        re->list = copy_list (e2->list);
973
0
    }
974
0
  else if (e2->type == FAT_ANY_TYPE)
975
0
    {
976
0
      re->type = e1->type;
977
0
      if (e1->type & FAT_ELEMENTWISE)
978
0
        re->list = copy_list (e1->list);
979
0
    }
980
0
  else if (e1->type & e2->type & FAT_ELEMENTWISE)
981
0
    {
982
0
      if ((e1->type == FAT_ELEMENTWISE_1 && e2->type == FAT_ELEMENTWISE_1)
983
0
          || (e1->type == FAT_ELEMENTWISE_2 && e2->type == FAT_ELEMENTWISE_2))
984
0
        {
985
0
          re->type = e1->type;
986
0
          re->list = make_intersected_list (copy_list (e1->list),
987
0
                                            copy_list (e2->list));
988
0
          if (re->list == NULL)
989
0
            return false;
990
0
        }
991
0
      else
992
0
        return false;
993
0
    }
994
0
  else
995
0
    {
996
0
      re->type = e1->type & e2->type;
997
0
      if (re->type == FAT_NONE)
998
0
        return false;
999
0
      if (e1->type & FAT_ELEMENTWISE)
1000
0
        {
1001
0
          re->type |= FAT_ELEMENTWISE;
1002
0
          re->list = copy_list (e1->list);
1003
0
        }
1004
0
      else if (e2->type & FAT_ELEMENTWISE)
1005
0
        {
1006
0
          re->type |= FAT_ELEMENTWISE;
1007
0
          re->list = copy_list (e2->list);
1008
0
        }
1009
0
    }
1010
1011
0
  return true;
1012
0
}
1013
1014
/* Append list->repeated to list->initial, and clear list->repeated.  */
1015
/* Memory effects: list is destructively modified.  */
1016
static void
1017
append_repeated_to_initial (struct format_arg_list *list)
1018
0
{
1019
0
  if (list->repeated.count > 0)
1020
0
    {
1021
      /* Move list->repeated over to list->initial.  */
1022
0
      size_t newcount = list->initial.count + list->repeated.count;
1023
0
      ensure_initial_alloc (list, newcount);
1024
0
      size_t i = list->initial.count;
1025
0
      for (size_t j = 0; j < list->repeated.count; j++)
1026
0
        {
1027
0
          list->initial.element[i] = list->repeated.element[j];
1028
0
          i++;
1029
0
        }
1030
0
      list->initial.count = newcount;
1031
0
      list->initial.length = list->initial.length + list->repeated.length;
1032
0
      free (list->repeated.element);
1033
0
      list->repeated.element = NULL;
1034
0
      list->repeated.allocated = 0;
1035
0
      list->repeated.count = 0;
1036
0
      list->repeated.length = 0;
1037
0
    }
1038
0
}
1039
1040
/* Handle a contradiction during building of a format_arg_list.
1041
   The list consists only of an initial segment.  The repeated segment is
1042
   empty.  This function searches the last FCT_OPTIONAL and cuts off the
1043
   list at this point, or - if none is found - returns NULL.  */
1044
/* Memory effects: list is destructively modified.  If NULL is returned,
1045
   list is freed.  */
1046
static struct format_arg_list *
1047
backtrack_in_initial (struct format_arg_list *list)
1048
0
{
1049
0
  ASSERT (list->repeated.count == 0);
1050
1051
0
  while (list->initial.count > 0)
1052
0
    {
1053
0
      size_t i = list->initial.count - 1;
1054
0
      if (list->initial.element[i].presence == FCT_REQUIRED)
1055
0
        {
1056
          /* Throw away this element.  */
1057
0
          list->initial.length -= list->initial.element[i].repcount;
1058
0
          free_element (&list->initial.element[i]);
1059
0
          list->initial.count = i;
1060
0
        }
1061
0
      else /* list->initial.element[i].presence == FCT_OPTIONAL */
1062
0
        {
1063
          /* The list must end here.  */
1064
0
          list->initial.length--;
1065
0
          if (list->initial.element[i].repcount > 1)
1066
0
            list->initial.element[i].repcount--;
1067
0
          else
1068
0
            {
1069
0
              free_element (&list->initial.element[i]);
1070
0
              list->initial.count = i;
1071
0
            }
1072
0
          VERIFY_LIST (list);
1073
0
          return list;
1074
0
        }
1075
0
    }
1076
1077
0
  free_list (list);
1078
0
  return NULL;
1079
0
}
1080
1081
/* Create the intersection (i.e. combined constraints) of two argument list
1082
   constraints.  Free both argument lists when done.  Return NULL if the
1083
   intersection is empty, i.e. if the two constraints give a contradiction.  */
1084
/* Memory effects: list1 and list2 are freed.  The result, if non-NULL, is
1085
   freshly allocated.  */
1086
static struct format_arg_list *
1087
make_intersected_list (struct format_arg_list *list1,
1088
                       struct format_arg_list *list2)
1089
0
{
1090
0
  struct format_arg_list *result;
1091
1092
0
  VERIFY_LIST (list1);
1093
0
  VERIFY_LIST (list2);
1094
1095
0
  if (list1->repeated.length > 0 && list2->repeated.length > 0)
1096
    /* Step 1: Ensure list1->repeated.length == list2->repeated.length.  */
1097
0
    {
1098
0
      size_t n1 = list1->repeated.length;
1099
0
      size_t n2 = list2->repeated.length;
1100
0
      size_t g = gcd (n1, n2);
1101
0
      size_t m1 = n2 / g; /* = lcm(n1,n2) / n1 */
1102
0
      size_t m2 = n1 / g; /* = lcm(n1,n2) / n2 */
1103
1104
0
      unfold_loop (list1, m1);
1105
0
      unfold_loop (list2, m2);
1106
      /* Now list1->repeated.length = list2->repeated.length = lcm(n1,n2).  */
1107
0
    }
1108
1109
0
  if (list1->repeated.length > 0 || list2->repeated.length > 0)
1110
    /* Step 2: Ensure the initial segment of the result can be computed
1111
       from the initial segments of list1 and list2.  If both have a
1112
       repeated segment, this means to ensure
1113
       list1->initial.length == list2->initial.length.  */
1114
0
    {
1115
0
      size_t m = MAX (list1->initial.length, list2->initial.length);
1116
1117
0
      if (list1->repeated.length > 0)
1118
0
        rotate_loop (list1, m);
1119
0
      if (list2->repeated.length > 0)
1120
0
        rotate_loop (list2, m);
1121
0
    }
1122
1123
0
  if (list1->repeated.length > 0 && list2->repeated.length > 0)
1124
0
    {
1125
0
      ASSERT (list1->initial.length == list2->initial.length);
1126
0
      ASSERT (list1->repeated.length == list2->repeated.length);
1127
0
    }
1128
1129
  /* Step 3: Allocate the result.  */
1130
0
  result = XMALLOC (struct format_arg_list);
1131
0
  result->initial.count = 0;
1132
0
  result->initial.allocated = 0;
1133
0
  result->initial.element = NULL;
1134
0
  result->initial.length = 0;
1135
0
  result->repeated.count = 0;
1136
0
  result->repeated.allocated = 0;
1137
0
  result->repeated.element = NULL;
1138
0
  result->repeated.length = 0;
1139
1140
  /* Step 4: Elementwise intersection of list1->initial, list2->initial.  */
1141
0
  {
1142
0
    struct format_arg *e1 = list1->initial.element;
1143
0
    size_t c1 = list1->initial.count;
1144
0
    struct format_arg *e2 = list2->initial.element;
1145
0
    size_t c2 = list2->initial.count;
1146
0
    while (c1 > 0 && c2 > 0)
1147
0
      {
1148
        /* Ensure room in result->initial.  */
1149
0
        grow_initial_alloc (result);
1150
0
        struct format_arg *re = &result->initial.element[result->initial.count];
1151
0
        re->repcount = MIN (e1->repcount, e2->repcount);
1152
1153
        /* Intersect the argument types.  */
1154
0
        if (!make_intersected_element (re, e1, e2))
1155
0
          {
1156
            /* If re->presence == FCT_OPTIONAL, the result list ends here.  */
1157
0
            if (re->presence == FCT_REQUIRED)
1158
              /* Contradiction.  Backtrack.  */
1159
0
              result = backtrack_in_initial (result);
1160
0
            goto done;
1161
0
          }
1162
1163
0
        result->initial.count++;
1164
0
        result->initial.length += re->repcount;
1165
1166
0
        e1->repcount -= re->repcount;
1167
0
        if (e1->repcount == 0)
1168
0
          {
1169
0
            e1++;
1170
0
            c1--;
1171
0
          }
1172
0
        e2->repcount -= re->repcount;
1173
0
        if (e2->repcount == 0)
1174
0
          {
1175
0
            e2++;
1176
0
            c2--;
1177
0
          }
1178
0
      }
1179
1180
0
    if (list1->repeated.count == 0 && list2->repeated.count == 0)
1181
0
      {
1182
        /* Intersecting two finite lists.  */
1183
0
        if (c1 > 0)
1184
0
          {
1185
            /* list1 longer than list2.  */
1186
0
            if (e1->presence == FCT_REQUIRED)
1187
              /* Contradiction.  Backtrack.  */
1188
0
              result = backtrack_in_initial (result);
1189
0
          }
1190
0
        else if (c2 > 0)
1191
0
          {
1192
            /* list2 longer than list1.  */
1193
0
            if (e2->presence == FCT_REQUIRED)
1194
              /* Contradiction.  Backtrack.  */
1195
0
              result = backtrack_in_initial (result);
1196
0
          }
1197
0
        goto done;
1198
0
      }
1199
0
    else if (list1->repeated.count == 0)
1200
0
      {
1201
        /* Intersecting a finite and an infinite list.  */
1202
0
        ASSERT (c1 == 0);
1203
0
        if ((c2 > 0 ? e2->presence : list2->repeated.element[0].presence)
1204
0
            == FCT_REQUIRED)
1205
          /* Contradiction.  Backtrack.  */
1206
0
          result = backtrack_in_initial (result);
1207
0
        goto done;
1208
0
      }
1209
0
    else if (list2->repeated.count == 0)
1210
0
      {
1211
        /* Intersecting an infinite and a finite list.  */
1212
0
        ASSERT (c2 == 0);
1213
0
        if ((c1 > 0 ? e1->presence : list1->repeated.element[0].presence)
1214
0
            == FCT_REQUIRED)
1215
          /* Contradiction.  Backtrack.  */
1216
0
          result = backtrack_in_initial (result);
1217
0
        goto done;
1218
0
      }
1219
    /* Intersecting two infinite lists.  */
1220
0
    ASSERT (c1 == 0 && c2 == 0);
1221
0
  }
1222
1223
  /* Step 5: Elementwise intersection of list1->repeated, list2->repeated.  */
1224
0
  {
1225
0
    struct format_arg *e1 = list1->repeated.element;
1226
0
    size_t c1 = list1->repeated.count;
1227
0
    struct format_arg *e2 = list2->repeated.element;
1228
0
    size_t c2 = list2->repeated.count;
1229
0
    while (c1 > 0 && c2 > 0)
1230
0
      {
1231
        /* Ensure room in result->repeated.  */
1232
0
        grow_repeated_alloc (result);
1233
0
        struct format_arg *re = &result->repeated.element[result->repeated.count];
1234
0
        re->repcount = MIN (e1->repcount, e2->repcount);
1235
1236
        /* Intersect the argument types.  */
1237
0
        if (!make_intersected_element (re, e1, e2))
1238
0
          {
1239
0
            bool re_is_required = re->presence == FCT_REQUIRED;
1240
1241
0
            append_repeated_to_initial (result);
1242
1243
            /* If re->presence == FCT_OPTIONAL, the result list ends here.  */
1244
0
            if (re_is_required)
1245
              /* Contradiction.  Backtrack.  */
1246
0
              result = backtrack_in_initial (result);
1247
1248
0
            goto done;
1249
0
          }
1250
1251
0
        result->repeated.count++;
1252
0
        result->repeated.length += re->repcount;
1253
1254
0
        e1->repcount -= re->repcount;
1255
0
        if (e1->repcount == 0)
1256
0
          {
1257
0
            e1++;
1258
0
            c1--;
1259
0
          }
1260
0
        e2->repcount -= re->repcount;
1261
0
        if (e2->repcount == 0)
1262
0
          {
1263
0
            e2++;
1264
0
            c2--;
1265
0
          }
1266
0
      }
1267
0
    ASSERT (c1 == 0 && c2 == 0);
1268
0
  }
1269
1270
0
 done:
1271
0
  free_list (list1);
1272
0
  free_list (list2);
1273
0
  if (result != NULL)
1274
0
    {
1275
      /* Undo the loop unfolding and unrolling done above.  */
1276
0
      normalize_outermost_list (result);
1277
0
      VERIFY_LIST (result);
1278
0
    }
1279
0
  return result;
1280
0
}
1281
1282
1283
/* Create the intersection of an argument list and the empty list.
1284
   Return NULL if the intersection is empty.  */
1285
/* Memory effects: The result, if non-NULL, is freshly allocated.  */
1286
MAYBE_UNUSED static struct format_arg_list *
1287
make_intersection_with_empty_list (struct format_arg_list *list)
1288
0
{
1289
0
#if 0 /* equivalent but slower */
1290
0
  return make_intersected_list (copy_list (list), make_empty_list ());
1291
0
#else
1292
0
  if (list->initial.count > 0
1293
0
      ? list->initial.element[0].presence == FCT_REQUIRED
1294
0
      : list->repeated.count > 0
1295
0
        && list->repeated.element[0].presence == FCT_REQUIRED)
1296
0
    return NULL;
1297
0
  else
1298
0
    return make_empty_list ();
1299
0
#endif
1300
0
}
1301
1302
1303
/* Create the intersection of two argument list constraints.  NULL stands
1304
   for an impossible situation, i.e. a contradiction.  */
1305
/* Memory effects: list1 and list2 are freed if non-NULL.  The result,
1306
   if non-NULL, is freshly allocated.  */
1307
MAYBE_UNUSED static struct format_arg_list *
1308
intersection (struct format_arg_list *list1, struct format_arg_list *list2)
1309
0
{
1310
0
  if (list1 != NULL)
1311
0
    {
1312
0
      if (list2 != NULL)
1313
0
        return make_intersected_list (list1, list2);
1314
0
      else
1315
0
        {
1316
0
          free_list (list1);
1317
0
          return NULL;
1318
0
        }
1319
0
    }
1320
0
  else
1321
0
    {
1322
0
      if (list2 != NULL)
1323
0
        {
1324
0
          free_list (list2);
1325
0
          return NULL;
1326
0
        }
1327
0
      else
1328
0
        return NULL;
1329
0
    }
1330
0
}
1331
1332
1333
/* ===================== Union of two format_arg_lists ===================== */
1334
1335
/* Create the union of an argument list and the empty list.  */
1336
/* Memory effects: list is freed.  The result is freshly allocated.  */
1337
MAYBE_UNUSED static struct format_arg_list *
1338
make_union_with_empty_list (struct format_arg_list *list)
1339
0
{
1340
0
  VERIFY_LIST (list);
1341
0
1342
0
  if (list->initial.count > 0
1343
0
      ? list->initial.element[0].presence == FCT_REQUIRED
1344
0
      : list->repeated.count > 0
1345
0
        && list->repeated.element[0].presence == FCT_REQUIRED)
1346
0
    {
1347
0
      initial_splitelement (list, 1);
1348
0
      ASSERT (list->initial.count > 0);
1349
0
      ASSERT (list->initial.element[0].repcount == 1);
1350
0
      ASSERT (list->initial.element[0].presence == FCT_REQUIRED);
1351
0
      list->initial.element[0].presence = FCT_OPTIONAL;
1352
0
1353
0
      /* We might need to merge list->initial.element[0] and
1354
0
         list->initial.element[1].  */
1355
0
      normalize_outermost_list (list);
1356
0
    }
1357
0
1358
0
  VERIFY_LIST (list);
1359
0
1360
0
  return list;
1361
0
}
1362
1363
1364
/* =========== Adding specific constraints to a format_arg_list =========== */
1365
1366
1367
/* Test whether arguments 0..n are required arguments in a list.  */
1368
MAYBE_UNUSED static bool
1369
is_required (const struct format_arg_list *list, size_t n)
1370
0
{
1371
0
  size_t t;
1372
0
1373
0
  /* We'll check whether the first n+1 presence flags are FCT_REQUIRED.  */
1374
0
  t = n + 1;
1375
0
1376
0
  /* Walk the list->initial segment.  */
1377
0
  {
1378
0
    size_t s;
1379
0
1380
0
    for (s = 0;
1381
0
         s < list->initial.count && t >= list->initial.element[s].repcount;
1382
0
         t -= list->initial.element[s].repcount, s++)
1383
0
      if (list->initial.element[s].presence != FCT_REQUIRED)
1384
0
        return false;
1385
0
1386
0
    if (t == 0)
1387
0
      return true;
1388
0
1389
0
    if (s < list->initial.count)
1390
0
      {
1391
0
        if (list->initial.element[s].presence != FCT_REQUIRED)
1392
0
          return false;
1393
0
        else
1394
0
          return true;
1395
0
      }
1396
0
  }
1397
0
1398
0
  /* Walk the list->repeated segment.  */
1399
0
  if (list->repeated.count == 0)
1400
0
    return false;
1401
0
1402
0
  {
1403
0
    size_t s;
1404
0
1405
0
    for (s = 0;
1406
0
         s < list->repeated.count && t >= list->repeated.element[s].repcount;
1407
0
         t -= list->repeated.element[s].repcount, s++)
1408
0
      if (list->repeated.element[s].presence != FCT_REQUIRED)
1409
0
        return false;
1410
0
1411
0
    if (t == 0)
1412
0
      return true;
1413
0
1414
0
    if (s < list->repeated.count)
1415
0
      {
1416
0
        if (list->repeated.element[s].presence != FCT_REQUIRED)
1417
0
          return false;
1418
0
        else
1419
0
          return true;
1420
0
      }
1421
0
  }
1422
0
1423
0
  /* The list->repeated segment consists only of FCT_REQUIRED.  So,
1424
0
     regardless how many more passes through list->repeated would be
1425
0
     needed until t becomes 0, the result is true.  */
1426
0
  return true;
1427
0
}
1428
1429
1430
/* Add a constraint to an argument list, namely that the arguments 0...n are
1431
   present.  NULL stands for an impossible situation, i.e. a contradiction.  */
1432
/* Memory effects: list is freed.  The result is freshly allocated.  */
1433
static struct format_arg_list *
1434
add_required_constraint (struct format_arg_list *list, size_t n)
1435
0
{
1436
0
  if (list == NULL)
1437
0
    return NULL;
1438
1439
0
  VERIFY_LIST (list);
1440
1441
0
  if (list->repeated.count == 0 && list->initial.length <= n)
1442
0
    {
1443
      /* list is already constrained to have at most length n.
1444
         Contradiction.  */
1445
0
      free_list (list);
1446
0
      return NULL;
1447
0
    }
1448
1449
0
  initial_splitelement (list, n + 1);
1450
1451
0
  {
1452
0
    size_t i = 0;
1453
0
    for (size_t rest = n + 1; rest > 0; )
1454
0
      {
1455
0
        list->initial.element[i].presence = FCT_REQUIRED;
1456
0
        rest -= list->initial.element[i].repcount;
1457
0
        i++;
1458
0
      }
1459
0
  }
1460
1461
0
  VERIFY_LIST (list);
1462
1463
0
  return list;
1464
0
}
1465
1466
1467
/* Add a constraint to an argument list, namely that the argument n is
1468
   never present.  NULL stands for an impossible situation, i.e. a
1469
   contradiction.  */
1470
/* Memory effects: list is freed.  The result is freshly allocated.  */
1471
static struct format_arg_list *
1472
add_end_constraint (struct format_arg_list *list, size_t n)
1473
0
{
1474
0
  if (list == NULL)
1475
0
    return NULL;
1476
1477
0
  VERIFY_LIST (list);
1478
1479
0
  if (list->repeated.count == 0 && list->initial.length <= n)
1480
    /* list is already constrained to have at most length n.  */
1481
0
    return list;
1482
1483
0
  size_t s = initial_splitelement (list, n);
1484
0
  enum format_cdr_type n_presence =
1485
0
    (s < list->initial.count
1486
0
     ? /* n < list->initial.length */ list->initial.element[s].presence
1487
0
     : /* n >= list->initial.length */ list->repeated.element[0].presence);
1488
1489
0
  for (size_t i = s; i < list->initial.count; i++)
1490
0
    {
1491
0
      list->initial.length -= list->initial.element[i].repcount;
1492
0
      free_element (&list->initial.element[i]);
1493
0
    }
1494
0
  list->initial.count = s;
1495
1496
0
  for (size_t i = 0; i < list->repeated.count; i++)
1497
0
    free_element (&list->repeated.element[i]);
1498
0
  if (list->repeated.element != NULL)
1499
0
    free (list->repeated.element);
1500
0
  list->repeated.element = NULL;
1501
0
  list->repeated.allocated = 0;
1502
0
  list->repeated.count = 0;
1503
0
  list->repeated.length = 0;
1504
1505
0
  if (n_presence == FCT_REQUIRED)
1506
0
    return backtrack_in_initial (list);
1507
0
  else
1508
0
    return list;
1509
0
}
1510
1511
1512
/* Add a constraint to an argument list, namely that the arguments n1..n2
1513
   (n1 <= n2) are of a given list type or (if sublist is NULL) of a given
1514
   non-list type.  NULL stands for an impossible situation, i.e. a
1515
   contradiction.  Assumes a preceding add_required_constraint (list, n2).  */
1516
/* Memory effects: list is freed.  The result is freshly allocated.  */
1517
static struct format_arg_list *
1518
add_type_constraint (struct format_arg_list *list,
1519
                     size_t n1, size_t n2,
1520
                     enum format_arg_type type,
1521
                     struct format_arg_list *sublist)
1522
0
{
1523
0
  if (list == NULL)
1524
0
    return NULL;
1525
1526
  /* Through the previous add_required_constraint, we can assume
1527
     list->initial.length >= n2+1.  */
1528
1529
0
  struct format_arg newconstraint;
1530
0
  newconstraint.presence = FCT_OPTIONAL;
1531
0
  newconstraint.type = type;
1532
0
  newconstraint.list = sublist;
1533
1534
0
  size_t s = initial_splitelement (list, n1);
1535
0
  initial_splitelement (list, n2 + 1);
1536
1537
  /* Modify the elements that represent the indices n1..n2.  */
1538
0
  size_t n = n1;
1539
0
  while (n <= n2)
1540
0
    {
1541
0
      struct format_arg tmpelement;
1542
0
      if (!make_intersected_element (&tmpelement,
1543
0
                                     &list->initial.element[s], &newconstraint))
1544
0
        {
1545
0
          list = add_end_constraint (list, n);
1546
0
          break;
1547
0
        }
1548
0
      free_element (&list->initial.element[s]);
1549
0
      list->initial.element[s].type = tmpelement.type;
1550
0
      list->initial.element[s].list = tmpelement.list;
1551
0
      n += list->initial.element[s].repcount;
1552
0
      s++;
1553
0
    }
1554
1555
0
  if (list != NULL)
1556
0
    VERIFY_LIST (list);
1557
1558
0
  return list;
1559
0
}
1560
1561
1562
/* Add a constraint to an argument list, namely that all the arguments
1563
   n, n+1, n+2, ..., if they exist, are of a given list type or (if sublist is
1564
   NULL) of a given non-list type.  NULL stands for an impossible situation,
1565
   i.e. a contradiction.  */
1566
/* Memory effects: list is freed.  The result is freshly allocated.  */
1567
static struct format_arg_list *
1568
add_repeated_opt_type_constraint (struct format_arg_list *list,
1569
                                  size_t n,
1570
                                  enum format_arg_type type,
1571
                                  struct format_arg_list *sublist)
1572
0
{
1573
0
  if (list == NULL)
1574
0
    return NULL;
1575
1576
0
  struct format_arg newconstraint;
1577
0
  newconstraint.presence = FCT_OPTIONAL;
1578
0
  newconstraint.type = type;
1579
0
  newconstraint.list = sublist;
1580
1581
  /* Modify the initial elements that represent the indices >= n.  */
1582
0
  {
1583
0
    size_t s = initial_splitelement (list, n);
1584
1585
0
    for (; s < list->initial.count; s++)
1586
0
      {
1587
0
        struct format_arg tmpelement;
1588
0
        if (!make_intersected_element (&tmpelement,
1589
0
                                       &list->initial.element[s], &newconstraint))
1590
0
          {
1591
0
            list = add_end_constraint (list, n);
1592
0
            goto done;
1593
0
          }
1594
0
        free_element (&list->initial.element[s]);
1595
0
        list->initial.element[s].type = tmpelement.type;
1596
0
        list->initial.element[s].list = tmpelement.list;
1597
0
        n += list->initial.element[s].repcount;
1598
0
      }
1599
0
  }
1600
1601
  /* Modify the repeated elements.  */
1602
0
  for (size_t s = 0; s < list->repeated.count; s++)
1603
0
    {
1604
0
      struct format_arg tmpelement;
1605
0
      if (!make_intersected_element (&tmpelement,
1606
0
                                     &list->repeated.element[s], &newconstraint))
1607
0
        {
1608
0
          list = add_end_constraint (list, n);
1609
0
          goto done;
1610
0
        }
1611
0
      free_element (&list->repeated.element[s]);
1612
0
      list->repeated.element[s].type = tmpelement.type;
1613
0
      list->repeated.element[s].list = tmpelement.list;
1614
0
      n += list->repeated.element[s].repcount;
1615
0
    }
1616
1617
0
 done:
1618
0
  if (list != NULL)
1619
0
    VERIFY_LIST (list);
1620
1621
0
  return list;
1622
0
}
1623
1624
1625
/* ============= Subroutines used by the format string parser ============= */
1626
1627
static void
1628
add_req_type_constraint (struct format_arg_list **listp,
1629
                         size_t position1, size_t position2,
1630
                         enum format_arg_type type,
1631
                         struct format_arg_list *sublist)
1632
0
{
1633
0
  *listp = add_required_constraint (*listp, position2);
1634
0
  if (type & FAT_ELEMENTWISE)
1635
0
    {
1636
0
      ASSERT (sublist != NULL);
1637
0
      *listp = add_type_constraint (*listp, position1, position2,
1638
0
                                    type, sublist);
1639
0
    }
1640
0
  else
1641
0
    {
1642
0
      ASSERT (sublist == NULL);
1643
0
      *listp = add_type_constraint (*listp, position1, position2, type, NULL);
1644
0
    }
1645
0
}
1646
1647
1648
/* ======================= The format string parser ======================= */
1649
1650
#define INVALID_ARGNO_ORDER(directive_number) \
1651
0
  xasprintf (_("In the directive number %zu, the first argument number is greater than the second argument number."), directive_number)
1652
1653
#define INVALID_COMPOUND_VARARG(directive_number) \
1654
0
  xasprintf (_("In the directive number %zu, the compound specifier consumes a variable number of arguments."), directive_number)
1655
1656
#define INVALID_COMPOUND_ARGCOUNT(directive_number, num_arguments) \
1657
0
  xasprintf (_("In the directive number %zu, the compound specifier consumes %zu arguments."), directive_number, num_arguments)
1658
1659
#define INVALID_BAR_OUTSIDE_COMPOUND() \
1660
0
  xstrdup (_("Found '%|' outside of '%(...%)'."))
1661
1662
#define INVALID_UNTERMINATED_COMPOUND() \
1663
0
  xstrdup (_("The string ends in the middle of a compound specifier."))
1664
1665
#define INVALID_COMPOUND_DELIMITER(directive_number) \
1666
0
  xasprintf (_("In the directive number %zu, there is an invalid directive in the delimiter part of a compound specifier."), directive_number)
1667
1668
#define INVALID_NESTING(found_char, notfound_char) \
1669
0
  xasprintf (_("Found '%%%c' without matching '%%%c'."), found_char, notfound_char)
1670
1671
#define INVALID_ARG_PAST_LAST(directive_number) \
1672
0
  xasprintf (_("The directive number %zu references an argument after the last argument."), directive_number)
1673
1674
#undef INVALID_INCOMPATIBLE_ARG_TYPES
1675
#define INVALID_INCOMPATIBLE_ARG_TYPES() \
1676
0
  xstrdup (_("The string refers to some argument in incompatible ways."))
1677
1678
/* Parse a piece of format string, until the matching terminating format
1679
   directive is encountered.
1680
   spec is the global struct spec.
1681
   format is the remainder of the format string.
1682
   It is updated upon valid return.
1683
   compound is true inside a compound specifier.
1684
   fdi is an array to be filled with format directive indicators, or NULL.
1685
   If the format string is invalid, false is returned and *invalid_reason is
1686
   set to an error message explaining why.  */
1687
static bool
1688
parse_upto (struct spec *spec,
1689
            const char **formatp, bool compound,
1690
            char *fdi, char **invalid_reason)
1691
0
{
1692
0
  const char *format = *formatp;
1693
0
  const char *const format_start = format;
1694
0
  size_t arg_count = 0;
1695
1696
0
  for (; *format != '\0'; )
1697
0
    {
1698
0
      char c = *format++;
1699
1700
0
      if (c == '%')
1701
0
        {
1702
0
          FDI_SET (format - 1, FMTDIR_START);
1703
1704
          /* Count number of directives.  */
1705
0
          spec->directives++;
1706
1707
0
          bool likely_intentional = true;
1708
1709
0
          if (*format == '\0')
1710
0
            {
1711
0
              *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
1712
0
              FDI_SET (format - 1, FMTDIR_ERROR);
1713
0
              return false;
1714
0
            }
1715
0
          if (*format == '%')
1716
            /* A doubled percent-sign.  */
1717
0
            ;
1718
0
          else
1719
0
            {
1720
              /* A directive.  */
1721
1722
              /* Parse position.  */
1723
0
              size_t first_number = 0;
1724
0
              size_t second_number = 0;
1725
0
              bool second_is_last = false;
1726
0
              if (c_isdigit (*format))
1727
0
                {
1728
0
                  const char *f = format;
1729
0
                  size_t m = 0;
1730
1731
0
                  do
1732
0
                    {
1733
0
                      m = 10 * m + (*f - '0');
1734
0
                      f++;
1735
0
                    }
1736
0
                  while (c_isdigit (*f));
1737
1738
0
                  if (*f == '$')
1739
0
                    {
1740
0
                      if (m == 0)
1741
0
                        {
1742
0
                          *invalid_reason = INVALID_ARGNO_0 (spec->directives);
1743
0
                          FDI_SET (f, FMTDIR_ERROR);
1744
0
                          return false;
1745
0
                        }
1746
0
                      first_number = m;
1747
0
                      format = ++f;
1748
0
                    }
1749
0
                  else if (*f == ':')
1750
0
                    {
1751
0
                      f++;
1752
0
                      if (c_isdigit (*f))
1753
0
                        {
1754
0
                          size_t m2 = 0;
1755
1756
0
                          do
1757
0
                            {
1758
0
                              m2 = 10 * m2 + (*f - '0');
1759
0
                              f++;
1760
0
                            }
1761
0
                          while (c_isdigit (*f));
1762
1763
0
                          if (*f == '$')
1764
0
                            {
1765
0
                              if (m2 == 0)
1766
0
                                {
1767
0
                                  *invalid_reason = INVALID_ARGNO_0 (spec->directives);
1768
0
                                  FDI_SET (f, FMTDIR_ERROR);
1769
0
                                  return false;
1770
0
                                }
1771
0
                              if (m > m2)
1772
0
                                {
1773
0
                                  *invalid_reason = INVALID_ARGNO_ORDER (spec->directives);
1774
0
                                  FDI_SET (f, FMTDIR_ERROR);
1775
0
                                  return false;
1776
0
                                }
1777
0
                              first_number = m;
1778
0
                              second_number = m2;
1779
0
                              format = ++f;
1780
0
                            }
1781
0
                        }
1782
0
                      else if (*f == '$')
1783
0
                        {
1784
0
                          first_number = m;
1785
0
                          second_is_last = true;
1786
0
                          format = ++f;
1787
0
                        }
1788
0
                    }
1789
0
                }
1790
1791
              /* Parse flags.  */
1792
0
              while (*format == ' ' || *format == '+' || *format == '-'
1793
0
                     || *format == '#' || *format == '0' || *format == '=')
1794
0
                {
1795
0
                  if (*format == ' ')
1796
0
                    likely_intentional = false;
1797
0
                  format++;
1798
0
                }
1799
1800
              /* Parse width.  */
1801
0
              size_t width_number = 0;
1802
0
              bool width_from_arg = false;
1803
0
              if (c_isdigit (*format))
1804
0
                {
1805
0
                  do format++; while (c_isdigit (*format));
1806
0
                }
1807
0
              else if (*format == '*')
1808
0
                {
1809
0
                  format++;
1810
0
                  if (c_isdigit (*format))
1811
0
                    {
1812
0
                      const char *f = format;
1813
0
                      size_t m = 0;
1814
1815
0
                      do
1816
0
                        {
1817
0
                          m = 10 * m + (*f - '0');
1818
0
                          f++;
1819
0
                        }
1820
0
                      while (c_isdigit (*f));
1821
1822
0
                      if (*f == '$')
1823
0
                        {
1824
0
                          if (m == 0)
1825
0
                            {
1826
0
                              *invalid_reason = INVALID_WIDTH_ARGNO_0 (spec->directives);
1827
0
                              FDI_SET (f, FMTDIR_ERROR);
1828
0
                              return false;
1829
0
                            }
1830
0
                          width_number = m;
1831
0
                          format = ++f;
1832
0
                        }
1833
0
                    }
1834
0
                  if (width_number == 0)
1835
0
                    width_from_arg = true;
1836
0
                }
1837
1838
              /* Parse precision.  */
1839
0
              size_t precision_number = 0;
1840
0
              bool precision_from_arg = false;
1841
0
              if (*format == '.')
1842
0
                {
1843
0
                  format++;
1844
1845
0
                  if (c_isdigit (*format))
1846
0
                    {
1847
0
                      do format++; while (c_isdigit (*format));
1848
0
                    }
1849
0
                  else if (*format == '*')
1850
0
                    {
1851
0
                      format++;
1852
0
                      if (c_isdigit (*format))
1853
0
                        {
1854
0
                          const char *f = format;
1855
0
                          size_t m = 0;
1856
1857
0
                          do
1858
0
                            {
1859
0
                              m = 10 * m + (*f - '0');
1860
0
                              f++;
1861
0
                            }
1862
0
                          while (c_isdigit (*f));
1863
1864
0
                          if (*f == '$')
1865
0
                            {
1866
0
                              if (m == 0)
1867
0
                                {
1868
0
                                  *invalid_reason = INVALID_WIDTH_ARGNO_0 (spec->directives);
1869
0
                                  FDI_SET (f, FMTDIR_ERROR);
1870
0
                                  return false;
1871
0
                                }
1872
0
                              precision_number = m;
1873
0
                              format = ++f;
1874
0
                            }
1875
0
                        }
1876
0
                      if (precision_number == 0)
1877
0
                        precision_from_arg = true;
1878
0
                    }
1879
0
                }
1880
1881
              /* Parse separator.  */
1882
0
              bool separator_digits_from_arg = false;
1883
0
              bool separator_char_from_arg = false;
1884
0
              if (*format == ',')
1885
0
                {
1886
0
                  format++;
1887
1888
0
                  if (c_isdigit (*format))
1889
0
                    {
1890
0
                      do format++; while (c_isdigit (*format));
1891
0
                    }
1892
0
                  else if (*format == '*')
1893
0
                    {
1894
0
                      format++;
1895
0
                      separator_digits_from_arg = true;
1896
0
                    }
1897
1898
0
                  if (*format == '?')
1899
0
                    {
1900
0
                      format++;
1901
0
                      separator_char_from_arg = true;
1902
0
                    }
1903
0
                }
1904
1905
0
              enum format_arg_type type;
1906
0
              struct format_arg_list *elementwise_list = NULL;
1907
1908
              /* Parse specifier.  */
1909
0
              switch (*format)
1910
0
                {
1911
0
                case 's':
1912
0
                  type = FAT_BOOL | FAT_INTEGER | FAT_FLOATINGPOINT | FAT_CHAR | FAT_ARRAY | FAT_ASSOCIATIVE | FAT_IRANGE | FAT_STRUCT | FAT_POINTER;
1913
0
                  break;
1914
0
                case 'c':
1915
0
                  type = FAT_CHAR;
1916
0
                  break;
1917
0
                case 'd': case 'u': case 'b': case 'o':
1918
0
                  type = FAT_BOOL | FAT_INTEGER | FAT_CHAR;
1919
0
                  break;
1920
0
                case 'x': case 'X':
1921
0
                  type = FAT_BOOL | FAT_INTEGER | FAT_CHAR | FAT_POINTER;
1922
0
                  break;
1923
0
                case 'e': case 'E': case 'f': case 'F':
1924
0
                case 'g': case 'G': case 'a': case 'A':
1925
0
                  type = FAT_INTEGER | FAT_FLOATINGPOINT;
1926
0
                  break;
1927
0
                case 'r':
1928
0
                  type = FAT_BOOL | FAT_INTEGER | FAT_FLOATINGPOINT | FAT_CHAR | FAT_ARRAY | FAT_IRANGE;
1929
0
                  break;
1930
0
                case '(':
1931
                  /* A compound specifier.  */
1932
0
                  format++;
1933
0
                  {
1934
0
                    struct spec sub_spec;
1935
0
                    sub_spec.directives = 0;
1936
0
                    sub_spec.list = make_unconstrained_list ();
1937
0
                    *formatp = format;
1938
0
                    if (!parse_upto (&sub_spec, formatp, true, fdi, invalid_reason))
1939
0
                      {
1940
0
                        FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
1941
0
                                 FMTDIR_ERROR);
1942
0
                        return false;
1943
0
                      }
1944
0
                    format = *formatp;
1945
0
                    elementwise_list = sub_spec.list;
1946
0
                    if (elementwise_list->repeated.count > 0)
1947
0
                      {
1948
                        /* Test case: "%(%1:$s%)"  */
1949
0
                        *invalid_reason = INVALID_COMPOUND_VARARG (spec->directives);
1950
0
                        FDI_SET (format - 1, FMTDIR_ERROR);
1951
0
                        return false;
1952
0
                      }
1953
0
                    if (elementwise_list->initial.length == 1)
1954
0
                      type = FAT_ELEMENTWISE_1;
1955
0
                    else if (elementwise_list->initial.length == 2)
1956
0
                      type = FAT_ELEMENTWISE_2;
1957
0
                    else
1958
0
                      {
1959
                        /* Test case: "%(%s %s %s%)"  */
1960
0
                        *invalid_reason = INVALID_COMPOUND_ARGCOUNT (spec->directives, elementwise_list->initial.length);
1961
0
                        FDI_SET (format - 1, FMTDIR_ERROR);
1962
0
                        return false;
1963
0
                      }
1964
0
                  }
1965
0
                  break;
1966
0
                case '|':
1967
0
                  if (!compound)
1968
0
                    {
1969
0
                      *invalid_reason = INVALID_BAR_OUTSIDE_COMPOUND ();
1970
0
                      FDI_SET (format, FMTDIR_ERROR);
1971
0
                      return false;
1972
0
                    }
1973
                  /* Parse the second part of a compound specifier.  */
1974
0
                  format++;
1975
0
                  for (;;)
1976
0
                    {
1977
0
                      if (*format == '\0')
1978
0
                        {
1979
0
                          *invalid_reason = INVALID_UNTERMINATED_COMPOUND ();
1980
0
                          FDI_SET (format - 1, FMTDIR_ERROR);
1981
0
                          return false;
1982
0
                        }
1983
0
                      if (*format == '%')
1984
0
                        {
1985
0
                          format++;
1986
0
                          if (*format == '%')
1987
0
                            format++;
1988
0
                          else if (*format == ')')
1989
0
                            break;
1990
0
                          else
1991
0
                            {
1992
0
                              *invalid_reason = INVALID_COMPOUND_DELIMITER (spec->directives);
1993
0
                              FDI_SET (format, FMTDIR_ERROR);
1994
0
                              return false;
1995
0
                            }
1996
0
                        }
1997
0
                      else
1998
0
                        format++;
1999
0
                    }
2000
                  /* Here (*format == ')').  */
2001
0
                  FALLTHROUGH;
2002
0
                case ')':
2003
0
                  if (!compound)
2004
0
                    {
2005
0
                      *invalid_reason = INVALID_NESTING (')', '(');
2006
0
                      FDI_SET (format, FMTDIR_ERROR);
2007
0
                      return false;
2008
0
                    }
2009
0
                  goto done;
2010
0
                default:
2011
0
                  if (*format == '\0')
2012
0
                    {
2013
0
                      *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
2014
0
                      FDI_SET (format - 1, FMTDIR_ERROR);
2015
0
                    }
2016
0
                  else
2017
0
                    {
2018
0
                      *invalid_reason = INVALID_CONVERSION_SPECIFIER (spec->directives, *format);
2019
0
                      FDI_SET (format, FMTDIR_ERROR);
2020
0
                    }
2021
0
                  return false;
2022
0
                }
2023
2024
0
              if (width_number > 0)
2025
0
                {
2026
0
                  add_req_type_constraint (&spec->list, width_number - 1, width_number - 1,
2027
0
                                           FAT_INTEGER, NULL);
2028
0
                  if (arg_count < width_number)
2029
0
                    arg_count = width_number;
2030
0
                }
2031
0
              else if (width_from_arg)
2032
0
                {
2033
0
                  if (arg_count == SIZE_MAX)
2034
0
                    {
2035
0
                      *invalid_reason = INVALID_ARG_PAST_LAST (spec->directives);
2036
0
                      FDI_SET (format, FMTDIR_ERROR);
2037
0
                      return false;
2038
0
                    }
2039
0
                  add_req_type_constraint (&spec->list, arg_count, arg_count,
2040
0
                                           FAT_INTEGER, NULL);
2041
0
                  arg_count++;
2042
0
                }
2043
2044
0
              if (precision_number > 0)
2045
0
                {
2046
0
                  add_req_type_constraint (&spec->list, precision_number - 1, precision_number - 1,
2047
0
                                           FAT_INTEGER, NULL);
2048
0
                  if (arg_count < precision_number)
2049
0
                    arg_count = precision_number;
2050
0
                }
2051
0
              else if (precision_from_arg)
2052
0
                {
2053
0
                  if (arg_count == SIZE_MAX)
2054
0
                    {
2055
0
                      *invalid_reason = INVALID_ARG_PAST_LAST (spec->directives);
2056
0
                      FDI_SET (format, FMTDIR_ERROR);
2057
0
                      return false;
2058
0
                    }
2059
0
                  add_req_type_constraint (&spec->list, arg_count, arg_count,
2060
0
                                           FAT_INTEGER, NULL);
2061
0
                  arg_count++;
2062
0
                }
2063
2064
0
              if (separator_digits_from_arg)
2065
0
                {
2066
0
                  if (arg_count == SIZE_MAX)
2067
0
                    {
2068
0
                      *invalid_reason = INVALID_ARG_PAST_LAST (spec->directives);
2069
0
                      FDI_SET (format, FMTDIR_ERROR);
2070
0
                      return false;
2071
0
                    }
2072
0
                  add_req_type_constraint (&spec->list, arg_count, arg_count,
2073
0
                                           FAT_INTEGER, NULL);
2074
0
                  arg_count++;
2075
0
                }
2076
2077
0
              if (separator_char_from_arg)
2078
0
                {
2079
0
                  if (arg_count == SIZE_MAX)
2080
0
                    {
2081
0
                      *invalid_reason = INVALID_ARG_PAST_LAST (spec->directives);
2082
0
                      FDI_SET (format, FMTDIR_ERROR);
2083
0
                      return false;
2084
0
                    }
2085
0
                  add_req_type_constraint (&spec->list, arg_count, arg_count,
2086
0
                                           FAT_CHAR, NULL);
2087
0
                  arg_count++;
2088
0
                }
2089
2090
0
              if (first_number > 0)
2091
0
                {
2092
0
                  if (second_number > 0)
2093
0
                    {
2094
0
                      add_req_type_constraint (&spec->list, first_number - 1, second_number - 1,
2095
0
                                               type, elementwise_list);
2096
0
                      if (arg_count < second_number)
2097
0
                        arg_count = second_number;
2098
0
                    }
2099
0
                  else if (second_is_last)
2100
0
                    {
2101
0
                      add_req_type_constraint (&spec->list, first_number - 1, first_number - 1,
2102
0
                                               type, elementwise_list);
2103
0
                      spec->list = add_repeated_opt_type_constraint (spec->list, first_number,
2104
0
                                                                     type, elementwise_list);
2105
0
                      arg_count = SIZE_MAX;
2106
0
                    }
2107
0
                  else
2108
0
                    {
2109
0
                      add_req_type_constraint (&spec->list, first_number - 1, first_number - 1,
2110
0
                                               type, elementwise_list);
2111
0
                      if (arg_count < first_number)
2112
0
                        arg_count = first_number;
2113
0
                    }
2114
0
                }
2115
0
              else
2116
0
                {
2117
0
                  if (arg_count == SIZE_MAX)
2118
0
                    {
2119
0
                      *invalid_reason = INVALID_ARG_PAST_LAST (spec->directives);
2120
0
                      FDI_SET (format, FMTDIR_ERROR);
2121
0
                      return false;
2122
0
                    }
2123
0
                  add_req_type_constraint (&spec->list, arg_count, arg_count,
2124
0
                                           type, elementwise_list);
2125
0
                  arg_count++;
2126
0
                }
2127
2128
0
              if (type & FAT_ELEMENTWISE)
2129
0
                free_list (elementwise_list);
2130
0
            }
2131
2132
0
          if (likely_intentional)
2133
0
            spec->likely_intentional_directives++;
2134
0
          FDI_SET (format, FMTDIR_END);
2135
2136
0
          format++;
2137
0
        }
2138
0
    }
2139
2140
0
  if (compound)
2141
0
    {
2142
0
      *invalid_reason = INVALID_NESTING ('(', ')');
2143
0
      return false;
2144
0
    }
2145
2146
0
 done:
2147
0
  *formatp = format;
2148
2149
  /* Extra arguments at the end are not allowed.  */
2150
0
  if (arg_count != SIZE_MAX)
2151
0
    {
2152
0
      spec->list = add_end_constraint (spec->list, arg_count);
2153
0
      if (spec->list == NULL)
2154
0
        return false;
2155
0
    }
2156
2157
0
  return true;
2158
0
}
2159
2160
2161
/* ============== Top level format string handling functions ============== */
2162
2163
static void *
2164
format_parse (const char *format, bool translated, char *fdi,
2165
              char **invalid_reason)
2166
0
{
2167
0
  struct spec spec;
2168
0
  spec.directives = 0;
2169
0
  spec.likely_intentional_directives = 0;
2170
0
  spec.list = make_unconstrained_list ();
2171
2172
0
  if (!parse_upto (&spec, &format, false,
2173
0
                   fdi, invalid_reason))
2174
    /* Invalid format string.  */
2175
0
    return NULL;
2176
2177
0
  if (spec.list == NULL)
2178
0
    {
2179
      /* Contradictory argument type information.  */
2180
0
      *invalid_reason = INVALID_INCOMPATIBLE_ARG_TYPES ();
2181
0
      return NULL;
2182
0
    }
2183
2184
  /* Normalize the result.  */
2185
0
  normalize_list (spec.list);
2186
2187
0
  struct spec *result = XMALLOC (struct spec);
2188
0
  *result = spec;
2189
0
  return result;
2190
0
}
2191
2192
static void
2193
format_free (void *descr)
2194
0
{
2195
0
  struct spec *spec = (struct spec *) descr;
2196
2197
0
  free_list (spec->list);
2198
0
}
2199
2200
static int
2201
format_get_number_of_directives (void *descr)
2202
0
{
2203
0
  struct spec *spec = (struct spec *) descr;
2204
2205
0
  return spec->directives;
2206
0
}
2207
2208
static bool
2209
format_is_unlikely_intentional (void *descr)
2210
0
{
2211
0
  struct spec *spec = (struct spec *) descr;
2212
2213
0
  return spec->likely_intentional_directives == 0;
2214
0
}
2215
2216
static bool
2217
format_check (void *msgid_descr, void *msgstr_descr, bool equality,
2218
              formatstring_error_logger_t error_logger, void *error_logger_data,
2219
              const char *pretty_msgid, const char *pretty_msgstr)
2220
0
{
2221
0
  struct spec *spec1 = (struct spec *) msgid_descr;
2222
0
  struct spec *spec2 = (struct spec *) msgstr_descr;
2223
2224
  /* The formatting functions in the D module std.format treat an unused
2225
     argument at the end of the argument list as an error.  Therefore here
2226
     the translator must not omit some of the arguments.
2227
     This could be mitigated in format strings with two or more directives.
2228
     Example:
2229
       "%2$s bought a piece." vs. "%2$s bought %1$d pieces."
2230
     Here the unused argument (argument 1) would not be at the end of the
2231
     argument list.  But this does not help with the more frequent case:
2232
       "a piece" vs. "%d pieces"
2233
     Therefore we recommend the zero-precision workaround in the documentation:
2234
       "%.0sa piece" vs. "%s pieces"
2235
   */
2236
0
  equality = true;
2237
2238
0
  bool err = false;
2239
2240
0
  if (equality)
2241
0
    {
2242
0
      if (!equal_list (spec1->list, spec2->list))
2243
0
        {
2244
0
          if (error_logger)
2245
0
            error_logger (error_logger_data,
2246
0
                          _("format specifications in '%s' and '%s' are not equivalent"),
2247
0
                          pretty_msgid, pretty_msgstr);
2248
0
          err = true;
2249
0
        }
2250
0
    }
2251
0
  else
2252
0
    {
2253
0
      struct format_arg_list *intersection =
2254
0
        make_intersected_list (copy_list (spec1->list),
2255
0
                               copy_list (spec2->list));
2256
2257
0
      if (!(intersection != NULL
2258
0
            && (normalize_list (intersection),
2259
0
                equal_list (intersection, spec2->list))))
2260
0
        {
2261
0
          if (error_logger)
2262
0
            error_logger (error_logger_data,
2263
0
                          _("format specifications in '%s' are not a subset of those in '%s'"),
2264
0
                          pretty_msgstr, pretty_msgid);
2265
0
          err = true;
2266
0
        }
2267
0
    }
2268
2269
0
  return err;
2270
0
}
2271
2272
2273
struct formatstring_parser formatstring_d =
2274
{
2275
  format_parse,
2276
  format_free,
2277
  format_get_number_of_directives,
2278
  format_is_unlikely_intentional,
2279
  format_check
2280
};
2281
2282
2283
/* ============================= Testing code ============================= */
2284
2285
#ifdef TEST
2286
2287
/* Test program: Print the argument list specification returned by
2288
   format_parse for strings read from standard input.  */
2289
2290
#include <stdio.h>
2291
2292
static void print_list (struct format_arg_list *list);
2293
2294
static void
2295
print_element (struct format_arg *element)
2296
{
2297
  switch (element->presence)
2298
    {
2299
    case FCT_REQUIRED:
2300
      break;
2301
    case FCT_OPTIONAL:
2302
      printf (". ");
2303
      break;
2304
    default:
2305
      abort ();
2306
    }
2307
2308
  if (element->type == FAT_NONE)
2309
    abort ();
2310
  if (element->type & FAT_ELEMENTWISE)
2311
    {
2312
      switch (element->type)
2313
        {
2314
        case FAT_ELEMENTWISE_1:
2315
          printf ("1");
2316
          break;
2317
        case FAT_ELEMENTWISE_2:
2318
          printf ("2");
2319
          break;
2320
        default:
2321
          abort ();
2322
        }
2323
      print_list (element->list);
2324
    }
2325
  else
2326
    {
2327
      if (element->type == FAT_ANY_TYPE)
2328
        printf ("*");
2329
      else
2330
        {
2331
          if (element->type & FAT_BOOL)
2332
            printf ("b");
2333
          if (element->type & FAT_INTEGER)
2334
            printf ("i");
2335
          if (element->type & FAT_FLOATINGPOINT)
2336
            printf ("f");
2337
          if (element->type & FAT_CHAR)
2338
            printf ("c");
2339
          if (element->type & FAT_ARRAY)
2340
            printf ("a");
2341
          if (element->type & FAT_ASSOCIATIVE)
2342
            printf ("@");
2343
          if (element->type & FAT_IRANGE)
2344
            printf ("r");
2345
          if (element->type & FAT_STRUCT)
2346
            printf ("s");
2347
          if (element->type & FAT_POINTER)
2348
            printf ("p");
2349
        }
2350
    }
2351
}
2352
2353
static void
2354
print_list (struct format_arg_list *list)
2355
{
2356
  printf ("(");
2357
2358
  for (size_t i = 0; i < list->initial.count; i++)
2359
    for (size_t j = 0; j < list->initial.element[i].repcount; j++)
2360
      {
2361
        if (i > 0 || j > 0)
2362
          printf (" ");
2363
        print_element (&list->initial.element[i]);
2364
      }
2365
2366
  if (list->repeated.count > 0)
2367
    {
2368
      printf (" |");
2369
      for (size_t i = 0; i < list->repeated.count; i++)
2370
        for (size_t j = 0; j < list->repeated.element[i].repcount; j++)
2371
          {
2372
            printf (" ");
2373
            print_element (&list->repeated.element[i]);
2374
          }
2375
    }
2376
2377
  printf (")");
2378
}
2379
2380
static void
2381
format_print (void *descr)
2382
{
2383
  struct spec *spec = (struct spec *) descr;
2384
2385
  if (spec == NULL)
2386
    {
2387
      printf ("INVALID");
2388
      return;
2389
    }
2390
2391
  print_list (spec->list);
2392
}
2393
2394
int
2395
main ()
2396
{
2397
  for (;;)
2398
    {
2399
      char *line = NULL;
2400
      size_t line_size = 0;
2401
      int line_len = getline (&line, &line_size, stdin);
2402
      if (line_len < 0)
2403
        break;
2404
      if (line_len > 0 && line[line_len - 1] == '\n')
2405
        line[--line_len] = '\0';
2406
2407
      char *invalid_reason = NULL;
2408
      void *descr = format_parse (line, false, NULL, &invalid_reason);
2409
2410
      format_print (descr);
2411
      printf ("\n");
2412
      if (descr == NULL)
2413
        printf ("%s\n", invalid_reason);
2414
2415
      free (invalid_reason);
2416
      free (line);
2417
    }
2418
2419
  return 0;
2420
}
2421
2422
/*
2423
 * For Emacs M-x compile
2424
 * Local Variables:
2425
 * 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-d.c ../gnulib-lib/libgettextlib.la"
2426
 * End:
2427
 */
2428
2429
#endif /* TEST */