Coverage Report

Created: 2026-05-31 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gettext/gettext-tools/src/format-lisp.c
Line
Count
Source
1
/* Lisp format strings.
2
   Copyright (C) 2001-2026 Free Software Foundation, Inc.
3
4
   This program is free software: you can redistribute it and/or modify
5
   it under the terms of the GNU General Public License as published by
6
   the Free Software Foundation; either version 3 of the License, or
7
   (at your option) any later version.
8
9
   This program is distributed in the hope that it will be useful,
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
11
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
   GNU General Public License for more details.
13
14
   You should have received a copy of the GNU General Public License
15
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16
17
/* Written by Bruno Haible.  */
18
19
#include <config.h>
20
21
#include <stdbool.h>
22
#include <stdlib.h>
23
24
#include "format.h"
25
#include "attribute.h"
26
#include "c-ctype.h"
27
#include "gcd.h"
28
#include "xalloc.h"
29
#include "xvasprintf.h"
30
#include "format-invalid.h"
31
#include "minmax.h"
32
#include "gettext.h"
33
34
0
#define _(str) gettext (str)
35
36
37
/* Assertion macro.  Could be defined to empty for speed.  */
38
0
#define ASSERT(expr) if (!(expr)) abort ();
39
40
41
/* Lisp format strings are described in the Common Lisp HyperSpec,
42
   chapter 22.3 "Formatted Output".  */
43
44
/* Data structure describing format string derived constraints for an
45
   argument list.  It is a recursive list structure.  Structure sharing
46
   is not allowed.  */
47
48
enum format_cdr_type
49
{
50
  FCT_REQUIRED, /* The format argument list cannot end before this argument.  */
51
  FCT_OPTIONAL  /* The format argument list may end before this argument.  */
52
};
53
54
enum format_arg_type
55
{
56
  FAT_OBJECT,                   /* Any object, type T.  */
57
  FAT_CHARACTER_INTEGER_NULL,   /* Type (OR CHARACTER INTEGER NULL).  */
58
  FAT_CHARACTER_NULL,           /* Type (OR CHARACTER NULL).  */
59
  FAT_CHARACTER,                /* Type CHARACTER.  */
60
  FAT_INTEGER_NULL,             /* Type (OR INTEGER NULL).  */
61
  FAT_INTEGER,                  /* Meant for objects of type INTEGER.  */
62
  FAT_REAL,                     /* Meant for objects of type REAL.  */
63
  FAT_LIST,                     /* Meant for proper lists.  */
64
  FAT_FORMATSTRING,             /* Format strings.  */
65
  FAT_FUNCTION                  /* Function.  */
66
};
67
68
struct format_arg
69
{
70
  size_t repcount;       /* Number of consecutive arguments this constraint
71
                            applies to.  Normally 1, but unconstrained
72
                            arguments are often repeated.  */
73
  enum format_cdr_type presence; /* Can the argument list end right before
74
                                    this argument?  */
75
  enum format_arg_type type;    /* Possible values for this argument.  */
76
  struct format_arg_list *list; /* For FAT_LIST: List elements.  */
77
};
78
79
struct segment
80
{
81
  size_t count;          /* Number of format_arg records used.  */
82
  size_t allocated;
83
  struct format_arg *element    /* Argument constraints.  */
84
    COUNTED_BY (count);
85
  size_t length;         /* Number of arguments represented by this segment.
86
                            This is the sum of all repcounts in the segment.  */
87
};
88
89
struct format_arg_list
90
{
91
  /* The constraints for the potentially infinite argument list are assumed
92
     to become ultimately periodic.  (Too complicated argument lists without
93
     a-priori period, like
94
            (format t "~@{~:[-~;~S~]~}" nil t 1 t 3 nil t 4)
95
     are described by a constraint that ends in a length 1 period of
96
     unconstrained arguments.)  Such a periodic sequence can be split into
97
     an initial segment and an endlessly repeated loop segment.
98
     A finite sequence is represented entirely in the initial segment; the
99
     loop segment is empty.  */
100
101
  struct segment initial;       /* Initial arguments segment.  */
102
  struct segment repeated;      /* Endlessly repeated segment.  */
103
};
104
105
struct spec
106
{
107
  size_t directives;
108
  struct format_arg_list *list;
109
};
110
111
112
/* Parameter for a directive.  */
113
enum param_type
114
{
115
  PT_NIL,       /* param not present */
116
  PT_CHARACTER, /* character */
117
  PT_INTEGER,   /* integer */
118
  PT_ARGCOUNT,  /* number of remaining arguments */
119
  PT_V          /* variable taken from argument list */
120
};
121
122
struct param
123
{
124
  enum param_type type;
125
  int value;            /* for PT_INTEGER: the value, for PT_V: the position */
126
};
127
128
129
/* Forward declaration of local functions.  */
130
0
#define union make_union
131
static void verify_list (const struct format_arg_list *list);
132
static void free_list (struct format_arg_list *list);
133
static struct format_arg_list * copy_list (const struct format_arg_list *list);
134
static bool equal_list (const struct format_arg_list *list1,
135
                        const struct format_arg_list *list2);
136
static struct format_arg_list * make_intersected_list
137
                                               (struct format_arg_list *list1,
138
                                                struct format_arg_list *list2);
139
static struct format_arg_list * make_intersection_with_empty_list
140
                                                (struct format_arg_list *list);
141
static struct format_arg_list * make_union_list
142
                                               (struct format_arg_list *list1,
143
                                                struct format_arg_list *list2);
144
145
146
/* ======================= Verify a format_arg_list ======================= */
147
148
/* Verify some invariants.  */
149
static void
150
verify_element (const struct format_arg * e)
151
0
{
152
0
  ASSERT (e->repcount > 0);
153
0
  if (e->type == FAT_LIST)
154
0
    verify_list (e->list);
155
0
}
156
157
/* Verify some invariants.  */
158
/* Memory effects: none.  */
159
static void
160
verify_list (const struct format_arg_list *list)
161
0
{
162
0
  ASSERT (list->initial.count <= list->initial.allocated);
163
0
  {
164
0
    size_t total_repcount;
165
166
0
    total_repcount = 0;
167
0
    for (size_t i = 0; i < list->initial.count; i++)
168
0
      {
169
0
        verify_element (&list->initial.element[i]);
170
0
        total_repcount += list->initial.element[i].repcount;
171
0
      }
172
173
0
    ASSERT (total_repcount == list->initial.length);
174
0
  }
175
176
0
  ASSERT (list->repeated.count <= list->repeated.allocated);
177
0
  {
178
0
    size_t total_repcount;
179
180
0
    total_repcount = 0;
181
0
    for (size_t i = 0; i < list->repeated.count; i++)
182
0
      {
183
0
        verify_element (&list->repeated.element[i]);
184
0
        total_repcount += list->repeated.element[i].repcount;
185
0
      }
186
187
0
    ASSERT (total_repcount == list->repeated.length);
188
0
  }
189
0
}
190
191
/* Assertion macro.  Could be defined to empty for speed.  */
192
0
#define VERIFY_LIST(list) verify_list (list)
193
194
195
/* ======================== Free a format_arg_list ======================== */
196
197
/* Free the data belonging to an argument list element.  */
198
static inline void
199
free_element (struct format_arg *element)
200
0
{
201
0
  if (element->type == FAT_LIST)
202
0
    free_list (element->list);
203
0
}
204
205
/* Free an argument list.  */
206
/* Memory effects: Frees list.  */
207
static void
208
free_list (struct format_arg_list *list)
209
0
{
210
0
  for (size_t i = 0; i < list->initial.count; i++)
211
0
    free_element (&list->initial.element[i]);
212
0
  if (list->initial.element != NULL)
213
0
    free (list->initial.element);
214
215
0
  for (size_t i = 0; i < list->repeated.count; i++)
216
0
    free_element (&list->repeated.element[i]);
217
0
  if (list->repeated.element != NULL)
218
0
    free (list->repeated.element);
219
0
}
220
221
222
/* ======================== Copy a format_arg_list ======================== */
223
224
/* Copy the data belonging to an argument list element.  */
225
static inline void
226
copy_element (struct format_arg *newelement,
227
              const struct format_arg *oldelement)
228
0
{
229
0
  newelement->repcount = oldelement->repcount;
230
0
  newelement->presence = oldelement->presence;
231
0
  newelement->type = oldelement->type;
232
0
  if (oldelement->type == FAT_LIST)
233
0
    newelement->list = copy_list (oldelement->list);
234
0
}
235
236
/* Copy an argument list.  */
237
/* Memory effects: Freshly allocated result.  */
238
static struct format_arg_list *
239
copy_list (const struct format_arg_list *list)
240
0
{
241
0
  VERIFY_LIST (list);
242
243
0
  struct format_arg_list *newlist = XMALLOC (struct format_arg_list);
244
245
0
  newlist->initial.count = newlist->initial.allocated = list->initial.count;
246
0
  {
247
0
    size_t length = 0;
248
0
    if (list->initial.count == 0)
249
0
      newlist->initial.element = NULL;
250
0
    else
251
0
      {
252
0
        newlist->initial.element =
253
0
          XNMALLOC (newlist->initial.allocated, struct format_arg);
254
0
        for (size_t i = 0; i < list->initial.count; i++)
255
0
          {
256
0
            copy_element (&newlist->initial.element[i],
257
0
                          &list->initial.element[i]);
258
0
            length += list->initial.element[i].repcount;
259
0
          }
260
0
      }
261
0
    ASSERT (length == list->initial.length);
262
0
    newlist->initial.length = length;
263
0
  }
264
265
0
  newlist->repeated.count = newlist->repeated.allocated = list->repeated.count;
266
0
  {
267
0
    size_t length = 0;
268
0
    if (list->repeated.count == 0)
269
0
      newlist->repeated.element = NULL;
270
0
    else
271
0
      {
272
0
        newlist->repeated.element =
273
0
          XNMALLOC (newlist->repeated.allocated, struct format_arg);
274
0
        for (size_t i = 0; i < list->repeated.count; i++)
275
0
          {
276
0
            copy_element (&newlist->repeated.element[i],
277
0
                          &list->repeated.element[i]);
278
0
            length += list->repeated.element[i].repcount;
279
0
          }
280
0
      }
281
0
    ASSERT (length == list->repeated.length);
282
0
    newlist->repeated.length = length;
283
0
  }
284
285
0
  VERIFY_LIST (newlist);
286
287
0
  return newlist;
288
0
}
289
290
291
/* ===================== Compare two format_arg_lists ===================== */
292
293
/* Tests whether two normalized argument constraints are equivalent,
294
   ignoring the repcount.  */
295
static bool
296
equal_element (const struct format_arg * e1, const struct format_arg * e2)
297
0
{
298
0
  return (e1->presence == e2->presence
299
0
          && e1->type == e2->type
300
0
          && (e1->type == FAT_LIST ? equal_list (e1->list, e2->list) : true));
301
0
}
302
303
/* Tests whether two normalized argument list constraints are equivalent.  */
304
/* Memory effects: none.  */
305
static bool
306
equal_list (const struct format_arg_list *list1,
307
            const struct format_arg_list *list2)
308
0
{
309
0
  VERIFY_LIST (list1);
310
0
  VERIFY_LIST (list2);
311
312
0
  {
313
0
    size_t n = list1->initial.count;
314
0
    if (n != list2->initial.count)
315
0
      return false;
316
0
    for (size_t i = 0; i < n; i++)
317
0
      {
318
0
        const struct format_arg * e1 = &list1->initial.element[i];
319
0
        const struct format_arg * e2 = &list2->initial.element[i];
320
321
0
        if (!(e1->repcount == e2->repcount && equal_element (e1, e2)))
322
0
          return false;
323
0
      }
324
0
  }
325
0
  {
326
0
    size_t n = list1->repeated.count;
327
0
    if (n != list2->repeated.count)
328
0
      return false;
329
0
    for (size_t i = 0; i < n; i++)
330
0
      {
331
0
        const struct format_arg * e1 = &list1->repeated.element[i];
332
0
        const struct format_arg * e2 = &list2->repeated.element[i];
333
334
0
        if (!(e1->repcount == e2->repcount && equal_element (e1, e2)))
335
0
          return false;
336
0
      }
337
0
  }
338
339
0
  return true;
340
0
}
341
342
343
/* ===================== Incremental memory allocation ===================== */
344
345
/* Ensure list->initial.allocated >= newcount.  */
346
static inline void
347
ensure_initial_alloc (struct format_arg_list *list, size_t newcount)
348
0
{
349
0
  if (newcount > list->initial.allocated)
350
0
    {
351
0
      list->initial.allocated =
352
0
        MAX (2 * list->initial.allocated + 1, newcount);
353
0
      list->initial.element =
354
0
        (struct format_arg *)
355
0
        xrealloc (list->initial.element,
356
0
                  list->initial.allocated * sizeof (struct format_arg));
357
0
    }
358
0
}
359
360
/* Ensure list->initial.allocated > list->initial.count.  */
361
static inline void
362
grow_initial_alloc (struct format_arg_list *list)
363
0
{
364
0
  if (list->initial.count >= list->initial.allocated)
365
0
    {
366
0
      list->initial.allocated =
367
0
        MAX (2 * list->initial.allocated + 1, list->initial.count + 1);
368
0
      list->initial.element =
369
0
        (struct format_arg *)
370
0
        xrealloc (list->initial.element,
371
0
                  list->initial.allocated * sizeof (struct format_arg));
372
0
    }
373
0
}
374
375
/* Ensure list->repeated.allocated >= newcount.  */
376
static inline void
377
ensure_repeated_alloc (struct format_arg_list *list, size_t newcount)
378
0
{
379
0
  if (newcount > list->repeated.allocated)
380
0
    {
381
0
      list->repeated.allocated =
382
0
        MAX (2 * list->repeated.allocated + 1, newcount);
383
0
      list->repeated.element =
384
0
        (struct format_arg *)
385
0
        xrealloc (list->repeated.element,
386
0
                  list->repeated.allocated * sizeof (struct format_arg));
387
0
    }
388
0
}
389
390
/* Ensure list->repeated.allocated > list->repeated.count.  */
391
static inline void
392
grow_repeated_alloc (struct format_arg_list *list)
393
0
{
394
0
  if (list->repeated.count >= list->repeated.allocated)
395
0
    {
396
0
      list->repeated.allocated =
397
0
        MAX (2 * list->repeated.allocated + 1, list->repeated.count + 1);
398
0
      list->repeated.element =
399
0
        (struct format_arg *)
400
0
        xrealloc (list->repeated.element,
401
0
                  list->repeated.allocated * sizeof (struct format_arg));
402
0
    }
403
0
}
404
405
406
/* ====================== Normalize a format_arg_list ====================== */
407
408
/* Normalize an argument list constraint, assuming all sublists are already
409
   normalized.  */
410
/* Memory effects: Destructively modifies list.  */
411
static void
412
normalize_outermost_list (struct format_arg_list *list)
413
0
{
414
  /* Step 1: Combine adjacent elements.
415
     Copy from i to j, keeping 0 <= j <= i.  */
416
0
  {
417
0
    size_t n = list->initial.count;
418
0
    size_t i, j;
419
0
    for (i = j = 0; i < n; i++)
420
0
      if (j > 0
421
0
          && equal_element (&list->initial.element[i],
422
0
                            &list->initial.element[j-1]))
423
0
        {
424
0
          list->initial.element[j-1].repcount +=
425
0
            list->initial.element[i].repcount;
426
0
          free_element (&list->initial.element[i]);
427
0
        }
428
0
      else
429
0
        {
430
0
          if (j < i)
431
0
            list->initial.element[j] = list->initial.element[i];
432
0
          j++;
433
0
        }
434
0
    list->initial.count = j;
435
0
  }
436
0
  {
437
0
    size_t n = list->repeated.count;
438
0
    size_t i, j;
439
0
    for (i = j = 0; i < n; i++)
440
0
      if (j > 0
441
0
          && equal_element (&list->repeated.element[i],
442
0
                            &list->repeated.element[j-1]))
443
0
        {
444
0
          list->repeated.element[j-1].repcount +=
445
0
            list->repeated.element[i].repcount;
446
0
          free_element (&list->repeated.element[i]);
447
0
        }
448
0
      else
449
0
        {
450
0
          if (j < i)
451
0
            list->repeated.element[j] = list->repeated.element[i];
452
0
          j++;
453
0
        }
454
0
    list->repeated.count = j;
455
0
  }
456
457
  /* Nothing more to be done if the loop segment is empty.  */
458
0
  if (list->repeated.count > 0)
459
0
    {
460
0
      size_t repcount0_extra;
461
462
      /* Step 2: Reduce the loop period.  */
463
0
      size_t n = list->repeated.count;
464
0
      repcount0_extra = 0;
465
0
      if (n > 1
466
0
          && equal_element (&list->repeated.element[0],
467
0
                            &list->repeated.element[n-1]))
468
0
        {
469
0
          repcount0_extra = list->repeated.element[n-1].repcount;
470
0
          n--;
471
0
        }
472
      /* Proceed as if the loop period were n, with
473
         list->repeated.element[0].repcount incremented by repcount0_extra.  */
474
0
      for (size_t m = 2; m <= n / 2; m++)
475
0
        if ((n % m) == 0)
476
0
          {
477
            /* m is a divisor of n.  Try to reduce the loop period to n.  */
478
0
            bool ok = true;
479
480
0
            for (size_t i = 0; i < n - m; i++)
481
0
              if (!((list->repeated.element[i].repcount
482
0
                     + (i == 0 ? repcount0_extra : 0)
483
0
                     == list->repeated.element[i+m].repcount)
484
0
                    && equal_element (&list->repeated.element[i],
485
0
                                      &list->repeated.element[i+m])))
486
0
                {
487
0
                  ok = false;
488
0
                  break;
489
0
                }
490
0
            if (ok)
491
0
              {
492
0
                for (size_t i = m; i < n; i++)
493
0
                  free_element (&list->repeated.element[i]);
494
0
                if (n < list->repeated.count)
495
0
                  list->repeated.element[m] = list->repeated.element[n];
496
0
                list->repeated.count = list->repeated.count - n + m;
497
0
                list->repeated.length /= n / m;
498
0
                break;
499
0
              }
500
0
          }
501
0
      if (list->repeated.count == 1)
502
0
        {
503
          /* The loop has period 1.  Normalize the repcount.  */
504
0
          list->repeated.element[0].repcount = 1;
505
0
          list->repeated.length = 1;
506
0
        }
507
508
      /* Step 3: Roll as much as possible of the initial segment's tail
509
         into the loop.  */
510
0
      if (list->repeated.count == 1)
511
0
        {
512
0
          if (list->initial.count > 0
513
0
              && equal_element (&list->initial.element[list->initial.count-1],
514
0
                                &list->repeated.element[0]))
515
0
            {
516
              /* Roll the last element of the initial segment into the loop.
517
                 Its repcount is irrelevant.  The second-to-last element is
518
                 certainly different and doesn't need to be considered.  */
519
0
              list->initial.length -=
520
0
                list->initial.element[list->initial.count-1].repcount;
521
0
              free_element (&list->initial.element[list->initial.count-1]);
522
0
              list->initial.count--;
523
0
            }
524
0
        }
525
0
      else
526
0
        {
527
0
          while (list->initial.count > 0
528
0
                 && equal_element (&list->initial.element[list->initial.count-1],
529
0
                                   &list->repeated.element[list->repeated.count-1]))
530
0
            {
531
0
              size_t moved_repcount =
532
0
                MIN (list->initial.element[list->initial.count-1].repcount,
533
0
                     list->repeated.element[list->repeated.count-1].repcount);
534
535
              /* Add the element at the start of list->repeated.  */
536
0
              if (equal_element (&list->repeated.element[0],
537
0
                                 &list->repeated.element[list->repeated.count-1]))
538
0
                list->repeated.element[0].repcount += moved_repcount;
539
0
              else
540
0
                {
541
0
                  size_t oldcount = list->repeated.count;
542
0
                  size_t newcount = oldcount + 1;
543
0
                  ensure_repeated_alloc (list, newcount);
544
0
                  list->repeated.count = newcount;
545
0
                  for (size_t i = oldcount; i > 0; i--)
546
0
                    list->repeated.element[i] = list->repeated.element[i-1];
547
0
                  copy_element (&list->repeated.element[0],
548
0
                                &list->repeated.element[list->repeated.count-1]);
549
0
                  list->repeated.element[0].repcount = moved_repcount;
550
0
                }
551
552
              /* Remove the element from the end of list->repeated.  */
553
0
              list->repeated.element[list->repeated.count-1].repcount -=
554
0
                moved_repcount;
555
0
              if (list->repeated.element[list->repeated.count-1].repcount == 0)
556
0
                {
557
0
                  free_element (&list->repeated.element[list->repeated.count-1]);
558
0
                  list->repeated.count--;
559
0
                }
560
561
              /* Remove the element from the end of list->initial.  */
562
0
              list->initial.element[list->initial.count-1].repcount -=
563
0
                moved_repcount;
564
0
              if (list->initial.element[list->initial.count-1].repcount == 0)
565
0
                {
566
0
                  free_element (&list->initial.element[list->initial.count-1]);
567
0
                  list->initial.count--;
568
0
                }
569
0
              list->initial.length -= moved_repcount;
570
0
            }
571
0
        }
572
0
    }
573
0
}
574
575
/* Normalize an argument list constraint.  */
576
/* Memory effects: Destructively modifies list.  */
577
static void
578
normalize_list (struct format_arg_list *list)
579
0
{
580
0
  VERIFY_LIST (list);
581
582
  /* First normalize all elements, recursively.  */
583
0
  {
584
0
    size_t n = list->initial.count;
585
0
    for (size_t i = 0; i < n; i++)
586
0
      if (list->initial.element[i].type == FAT_LIST)
587
0
        normalize_list (list->initial.element[i].list);
588
0
  }
589
0
  {
590
0
    size_t n = list->repeated.count;
591
0
    for (size_t i = 0; i < n; i++)
592
0
      if (list->repeated.element[i].type == FAT_LIST)
593
0
        normalize_list (list->repeated.element[i].list);
594
0
  }
595
596
  /* Then normalize the top level list.  */
597
0
  normalize_outermost_list (list);
598
599
0
  VERIFY_LIST (list);
600
0
}
601
602
603
/* ===================== Unconstrained and empty lists ===================== */
604
605
/* It's easier to allocate these on demand, than to be careful not to
606
   accidentally modify statically allocated lists.  */
607
608
609
/* Create an unconstrained argument list.  */
610
/* Memory effects: Freshly allocated result.  */
611
static struct format_arg_list *
612
make_unconstrained_list ()
613
0
{
614
0
  struct format_arg_list *list = XMALLOC (struct format_arg_list);
615
0
  list->initial.count = 0;
616
0
  list->initial.allocated = 0;
617
0
  list->initial.element = NULL;
618
0
  list->initial.length = 0;
619
0
  list->repeated.count = 1;
620
0
  list->repeated.allocated = 1;
621
0
  list->repeated.element = XNMALLOC (1, struct format_arg);
622
0
  list->repeated.element[0].repcount = 1;
623
0
  list->repeated.element[0].presence = FCT_OPTIONAL;
624
0
  list->repeated.element[0].type = FAT_OBJECT;
625
0
  list->repeated.length = 1;
626
627
0
  VERIFY_LIST (list);
628
629
0
  return list;
630
0
}
631
632
633
/* Create an empty argument list.  */
634
/* Memory effects: Freshly allocated result.  */
635
static struct format_arg_list *
636
make_empty_list ()
637
0
{
638
0
  struct format_arg_list *list = XMALLOC (struct format_arg_list);
639
0
  list->initial.count = 0;
640
0
  list->initial.allocated = 0;
641
0
  list->initial.element = NULL;
642
0
  list->initial.length = 0;
643
0
  list->repeated.count = 0;
644
0
  list->repeated.allocated = 0;
645
0
  list->repeated.element = NULL;
646
0
  list->repeated.length = 0;
647
648
0
  VERIFY_LIST (list);
649
650
0
  return list;
651
0
}
652
653
654
/* Test for an empty list.  */
655
/* Memory effects: none.  */
656
static bool
657
is_empty_list (const struct format_arg_list *list)
658
0
{
659
0
  return (list->initial.count == 0 && list->repeated.count == 0);
660
0
}
661
662
663
/* ======================== format_arg_list surgery ======================== */
664
665
/* Unfold list->repeated m times, where m >= 1.
666
   Assumes list->repeated.count > 0.  */
667
/* Memory effects: list is destructively modified.  */
668
static void
669
unfold_loop (struct format_arg_list *list, size_t m)
670
0
{
671
0
  if (m > 1)
672
0
    {
673
0
      size_t oldcount = list->repeated.count;
674
0
      size_t newcount = oldcount * m;
675
0
      ensure_repeated_alloc (list, newcount);
676
0
      list->repeated.count = newcount;
677
0
      size_t i = oldcount;
678
0
      for (size_t k = 1; k < m; k++)
679
0
        for (size_t j = 0; j < oldcount; j++)
680
0
          {
681
0
            copy_element (&list->repeated.element[i], &list->repeated.element[j]);
682
0
            i++;
683
0
          }
684
0
      list->repeated.length = list->repeated.length * m;
685
0
    }
686
0
}
687
688
/* Ensure list->initial.length := m, where m >= list->initial.length.
689
   Assumes list->repeated.count > 0.  */
690
/* Memory effects: list is destructively modified.  */
691
static void
692
rotate_loop (struct format_arg_list *list, size_t m)
693
0
{
694
0
  if (m == list->initial.length)
695
0
    return;
696
697
0
  if (list->repeated.count == 1)
698
0
    {
699
      /* Instead of multiple copies of list->repeated.element[0], a single
700
         copy with higher repcount is appended to list->initial.  */
701
0
      size_t oldcount = list->initial.count;
702
0
      size_t newcount = oldcount + 1;
703
0
      ensure_initial_alloc (list, newcount);
704
0
      list->initial.count = newcount;
705
0
      size_t i = oldcount;
706
0
      copy_element (&list->initial.element[i], &list->repeated.element[0]);
707
0
      list->initial.element[i].repcount = m - list->initial.length;
708
0
      list->initial.length = m;
709
0
    }
710
0
  else
711
0
    {
712
0
      size_t n = list->repeated.length;
713
714
      /* Write m = list->initial.length + q * n + r with 0 <= r < n.  */
715
0
      size_t q = (m - list->initial.length) / n;
716
0
      size_t r = (m - list->initial.length) % n;
717
718
      /* Determine how many entries of list->repeated are needed for
719
         length r.  */
720
0
      size_t s;
721
0
      size_t t;
722
723
0
      for (t = r, s = 0;
724
0
           s < list->repeated.count && t >= list->repeated.element[s].repcount;
725
0
           t -= list->repeated.element[s].repcount, s++)
726
0
        ;
727
728
      /* s must be < list->repeated.count, otherwise r would have been >= n.  */
729
0
      ASSERT (s < list->repeated.count);
730
731
      /* So we need to add to list->initial:
732
         q full copies of list->repeated,
733
         plus the s first elements of list->repeated,
734
         plus, if t > 0, a splitoff of list->repeated.element[s].  */
735
0
      {
736
0
        size_t oldcount = list->initial.count;
737
0
        size_t i = oldcount;
738
0
        size_t newcount = i + q * list->repeated.count + s + (t > 0 ? 1 : 0);
739
0
        ensure_initial_alloc (list, newcount);
740
0
        list->initial.count = newcount;
741
0
        for (size_t k = 0; k < q; k++)
742
0
          for (size_t j = 0; j < list->repeated.count; j++)
743
0
            {
744
0
              copy_element (&list->initial.element[i], &list->repeated.element[j]);
745
0
              i++;
746
0
            }
747
0
        for (size_t j = 0; j < s; j++)
748
0
          {
749
0
            copy_element (&list->initial.element[i], &list->repeated.element[j]);
750
0
            i++;
751
0
          }
752
0
        if (t > 0)
753
0
          {
754
0
            copy_element (&list->initial.element[i], &list->repeated.element[s]);
755
0
            list->initial.element[i].repcount = t;
756
0
            i++;
757
0
          }
758
0
        ASSERT (i == newcount);
759
        /* The new length of the initial segment is
760
           = list->initial.length
761
             + q * list->repeated.length
762
             + list->repeated[0..s-1].repcount + t
763
           = list->initial.length + q * n + r
764
           = m.
765
         */
766
0
        list->initial.length = m;
767
0
      }
768
769
      /* And rotate list->repeated.  */
770
0
      if (r > 0)
771
0
        {
772
0
          size_t oldcount = list->repeated.count;
773
0
          size_t newcount = list->repeated.count + (t > 0 ? 1 : 0);
774
0
          struct format_arg *newelement = XNMALLOC (newcount, struct format_arg);
775
0
          size_t i = 0;
776
0
          for (size_t j = s; j < oldcount; j++)
777
0
            {
778
0
              newelement[i] = list->repeated.element[j];
779
0
              i++;
780
0
            }
781
0
          for (size_t j = 0; j < s; j++)
782
0
            {
783
0
              newelement[i] = list->repeated.element[j];
784
0
              i++;
785
0
            }
786
0
          if (t > 0)
787
0
            {
788
0
              copy_element (&newelement[oldcount], &newelement[0]);
789
0
              newelement[0].repcount -= t;
790
0
              newelement[oldcount].repcount = t;
791
0
            }
792
0
          free (list->repeated.element);
793
0
          list->repeated.element = newelement;
794
0
          list->repeated.count = newcount;
795
0
        }
796
0
    }
797
0
}
798
799
800
/* Ensure index n in the initial segment falls on a split between elements,
801
   i.e. if 0 < n < list->initial.length, then n-1 and n are covered by two
802
   different adjacent elements.  */
803
/* Memory effects: list is destructively modified.  */
804
static size_t
805
initial_splitelement (struct format_arg_list *list, size_t n)
806
0
{
807
0
  VERIFY_LIST (list);
808
809
0
  if (n > list->initial.length)
810
0
    {
811
0
      ASSERT (list->repeated.count > 0);
812
0
      rotate_loop (list, n);
813
0
      ASSERT (n <= list->initial.length);
814
0
    }
815
816
  /* Determine how many entries of list->initial need to be skipped.  */
817
0
  size_t s;
818
0
  size_t t;
819
0
  for (t = n, s = 0;
820
0
       s < list->initial.count && t >= list->initial.element[s].repcount;
821
0
       t -= list->initial.element[s].repcount, s++)
822
0
    ;
823
824
0
  if (t == 0)
825
0
    return s;
826
827
0
  ASSERT (s < list->initial.count);
828
829
  /* Split the entry into two entries.  */
830
0
  size_t oldrepcount = list->initial.element[s].repcount;
831
0
  size_t oldcount = list->initial.count;
832
0
  size_t newcount = oldcount + 1;
833
0
  ensure_initial_alloc (list, newcount);
834
0
  list->initial.count = newcount;
835
0
  for (size_t i = oldcount - 1; i > s; i--)
836
0
    list->initial.element[i+1] = list->initial.element[i];
837
0
  copy_element (&list->initial.element[s+1], &list->initial.element[s]);
838
0
  list->initial.element[s].repcount = t;
839
0
  list->initial.element[s+1].repcount = oldrepcount - t;
840
841
0
  VERIFY_LIST (list);
842
843
0
  return s+1;
844
0
}
845
846
847
/* Ensure index n in the initial segment is not shared.  Return its index.  */
848
/* Memory effects: list is destructively modified.  */
849
static size_t
850
initial_unshare (struct format_arg_list *list, size_t n)
851
0
{
852
  /* This does the same side effects as
853
       initial_splitelement (list, n);
854
       initial_splitelement (list, n + 1);
855
   */
856
857
0
  VERIFY_LIST (list);
858
859
0
  if (n >= list->initial.length)
860
0
    {
861
0
      ASSERT (list->repeated.count > 0);
862
0
      rotate_loop (list, n + 1);
863
0
      ASSERT (n < list->initial.length);
864
0
    }
865
866
  /* Determine how many entries of list->initial need to be skipped.  */
867
0
  size_t s;
868
0
  size_t t;
869
0
  for (t = n, s = 0;
870
0
       s < list->initial.count && t >= list->initial.element[s].repcount;
871
0
       t -= list->initial.element[s].repcount, s++)
872
0
    ;
873
874
  /* s must be < list->initial.count.  */
875
0
  ASSERT (s < list->initial.count);
876
877
0
  if (list->initial.element[s].repcount > 1)
878
0
    {
879
      /* Split the entry into at most three entries: for indices < n,
880
         for index n, and for indices > n.  */
881
0
      size_t oldrepcount = list->initial.element[s].repcount;
882
0
      size_t oldcount = list->initial.count;
883
0
      size_t newcount = oldcount + (t == 0 || t == oldrepcount - 1 ? 1 : 2);
884
0
      ensure_initial_alloc (list, newcount);
885
0
      list->initial.count = newcount;
886
0
      if (t == 0 || t == oldrepcount - 1)
887
0
        {
888
0
          for (size_t i = oldcount - 1; i > s; i--)
889
0
            list->initial.element[i+1] = list->initial.element[i];
890
0
          copy_element (&list->initial.element[s+1], &list->initial.element[s]);
891
0
          if (t == 0)
892
0
            {
893
0
              list->initial.element[s].repcount = 1;
894
0
              list->initial.element[s+1].repcount = oldrepcount - 1;
895
0
            }
896
0
          else
897
0
            {
898
0
              list->initial.element[s].repcount = oldrepcount - 1;
899
0
              list->initial.element[s+1].repcount = 1;
900
0
            }
901
0
        }
902
0
      else
903
0
        {
904
0
          for (size_t i = oldcount - 1; i > s; i--)
905
0
            list->initial.element[i+2] = list->initial.element[i];
906
0
          copy_element (&list->initial.element[s+2], &list->initial.element[s]);
907
0
          copy_element (&list->initial.element[s+1], &list->initial.element[s]);
908
0
          list->initial.element[s].repcount = t;
909
0
          list->initial.element[s+1].repcount = 1;
910
0
          list->initial.element[s+2].repcount = oldrepcount - 1 - t;
911
0
        }
912
0
      if (t > 0)
913
0
        s++;
914
0
    }
915
916
  /* Now the entry for index n has repcount 1.  */
917
0
  ASSERT (list->initial.element[s].repcount == 1);
918
919
0
  VERIFY_LIST (list);
920
921
0
  return s;
922
0
}
923
924
925
/* Add n unconstrained elements at the front of the list.  */
926
/* Memory effects: list is destructively modified.  */
927
static void
928
shift_list (struct format_arg_list *list, size_t n)
929
0
{
930
0
  VERIFY_LIST (list);
931
932
0
  if (n > 0)
933
0
    {
934
0
      grow_initial_alloc (list);
935
0
      size_t oldcount = list->initial.count++;
936
0
      for (size_t i = oldcount; i > 0; i--)
937
0
        list->initial.element[i] = list->initial.element[i-1];
938
0
      list->initial.element[0].repcount = n;
939
0
      list->initial.element[0].presence = FCT_REQUIRED;
940
0
      list->initial.element[0].type = FAT_OBJECT;
941
0
      list->initial.length += n;
942
943
0
      normalize_outermost_list (list);
944
0
    }
945
946
0
  VERIFY_LIST (list);
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_OBJECT)
969
0
    {
970
0
      re->type = e2->type;
971
0
      if (re->type == FAT_LIST)
972
0
        re->list = copy_list (e2->list);
973
0
    }
974
0
  else if (e2->type == FAT_OBJECT)
975
0
    {
976
0
      re->type = e1->type;
977
0
      if (re->type == FAT_LIST)
978
0
        re->list = copy_list (e1->list);
979
0
    }
980
0
  else if (e1->type == FAT_LIST
981
0
           && (e2->type == FAT_CHARACTER_INTEGER_NULL
982
0
               || e2->type == FAT_CHARACTER_NULL
983
0
               || e2->type == FAT_INTEGER_NULL))
984
0
    {
985
0
      re->type = e1->type;
986
0
      re->list = make_intersection_with_empty_list (e1->list);
987
0
      if (re->list == NULL)
988
0
        return false;
989
0
    }
990
0
  else if (e2->type == FAT_LIST
991
0
           && (e1->type == FAT_CHARACTER_INTEGER_NULL
992
0
               || e1->type == FAT_CHARACTER_NULL
993
0
               || e1->type == FAT_INTEGER_NULL))
994
0
    {
995
0
      re->type = e2->type;
996
0
      re->list = make_intersection_with_empty_list (e2->list);
997
0
      if (re->list == NULL)
998
0
        return false;
999
0
    }
1000
0
  else if (e1->type == FAT_CHARACTER_INTEGER_NULL
1001
0
           && (e2->type == FAT_CHARACTER_NULL || e2->type == FAT_CHARACTER
1002
0
               || e2->type == FAT_INTEGER_NULL || e2->type == FAT_INTEGER))
1003
0
    {
1004
0
      re->type = e2->type;
1005
0
    }
1006
0
  else if (e2->type == FAT_CHARACTER_INTEGER_NULL
1007
0
           && (e1->type == FAT_CHARACTER_NULL || e1->type == FAT_CHARACTER
1008
0
               || e1->type == FAT_INTEGER_NULL || e1->type == FAT_INTEGER))
1009
0
    {
1010
0
      re->type = e1->type;
1011
0
    }
1012
0
  else if (e1->type == FAT_CHARACTER_NULL && e2->type == FAT_CHARACTER)
1013
0
    {
1014
0
      re->type = e2->type;
1015
0
    }
1016
0
  else if (e2->type == FAT_CHARACTER_NULL && e1->type == FAT_CHARACTER)
1017
0
    {
1018
0
      re->type = e1->type;
1019
0
    }
1020
0
  else if (e1->type == FAT_INTEGER_NULL && e2->type == FAT_INTEGER)
1021
0
    {
1022
0
      re->type = e2->type;
1023
0
    }
1024
0
  else if (e2->type == FAT_INTEGER_NULL && e1->type == FAT_INTEGER)
1025
0
    {
1026
0
      re->type = e1->type;
1027
0
    }
1028
0
  else if (e1->type == FAT_REAL && e2->type == FAT_INTEGER)
1029
0
    {
1030
0
      re->type = e2->type;
1031
0
    }
1032
0
  else if (e2->type == FAT_REAL && e1->type == FAT_INTEGER)
1033
0
    {
1034
0
      re->type = e1->type;
1035
0
    }
1036
0
  else if (e1->type == e2->type)
1037
0
    {
1038
0
      re->type = e1->type;
1039
0
      if (re->type == FAT_LIST)
1040
0
        {
1041
0
          re->list = make_intersected_list (copy_list (e1->list),
1042
0
                                            copy_list (e2->list));
1043
0
          if (re->list == NULL)
1044
0
            return false;
1045
0
        }
1046
0
    }
1047
0
  else
1048
    /* Each of FAT_CHARACTER, FAT_INTEGER, FAT_LIST, FAT_FORMATSTRING,
1049
       FAT_FUNCTION matches only itself.  Contradiction.  */
1050
0
    return false;
1051
1052
0
  return true;
1053
0
}
1054
1055
/* Append list->repeated to list->initial, and clear list->repeated.  */
1056
/* Memory effects: list is destructively modified.  */
1057
static void
1058
append_repeated_to_initial (struct format_arg_list *list)
1059
0
{
1060
0
  if (list->repeated.count > 0)
1061
0
    {
1062
      /* Move list->repeated over to list->initial.  */
1063
0
      size_t oldcount = list->initial.count;
1064
0
      size_t newcount = oldcount + list->repeated.count;
1065
0
      ensure_initial_alloc (list, newcount);
1066
0
      list->initial.count = newcount;
1067
0
      size_t i = oldcount;
1068
0
      for (size_t j = 0; j < list->repeated.count; j++)
1069
0
        {
1070
0
          list->initial.element[i] = list->repeated.element[j];
1071
0
          i++;
1072
0
        }
1073
0
      list->initial.length = list->initial.length + list->repeated.length;
1074
0
      free (list->repeated.element);
1075
0
      list->repeated.element = NULL;
1076
0
      list->repeated.allocated = 0;
1077
0
      list->repeated.count = 0;
1078
0
      list->repeated.length = 0;
1079
0
    }
1080
0
}
1081
1082
/* Handle a contradiction during building of a format_arg_list.
1083
   The list consists only of an initial segment.  The repeated segment is
1084
   empty.  This function searches the last FCT_OPTIONAL and cuts off the
1085
   list at this point, or - if none is found - returns NULL.  */
1086
/* Memory effects: list is destructively modified.  If NULL is returned,
1087
   list is freed.  */
1088
static struct format_arg_list *
1089
backtrack_in_initial (struct format_arg_list *list)
1090
0
{
1091
0
  ASSERT (list->repeated.count == 0);
1092
1093
0
  while (list->initial.count > 0)
1094
0
    {
1095
0
      size_t i = list->initial.count - 1;
1096
0
      if (list->initial.element[i].presence == FCT_REQUIRED)
1097
0
        {
1098
          /* Throw away this element.  */
1099
0
          list->initial.length -= list->initial.element[i].repcount;
1100
0
          free_element (&list->initial.element[i]);
1101
0
          list->initial.count = i;
1102
0
        }
1103
0
      else /* list->initial.element[i].presence == FCT_OPTIONAL */
1104
0
        {
1105
          /* The list must end here.  */
1106
0
          list->initial.length--;
1107
0
          if (list->initial.element[i].repcount > 1)
1108
0
            list->initial.element[i].repcount--;
1109
0
          else
1110
0
            {
1111
0
              free_element (&list->initial.element[i]);
1112
0
              list->initial.count = i;
1113
0
            }
1114
0
          VERIFY_LIST (list);
1115
0
          return list;
1116
0
        }
1117
0
    }
1118
1119
0
  free_list (list);
1120
0
  return NULL;
1121
0
}
1122
1123
/* Create the intersection (i.e. combined constraints) of two argument list
1124
   constraints.  Free both argument lists when done.  Return NULL if the
1125
   intersection is empty, i.e. if the two constraints give a contradiction.  */
1126
/* Memory effects: list1 and list2 are freed.  The result, if non-NULL, is
1127
   freshly allocated.  */
1128
static struct format_arg_list *
1129
make_intersected_list (struct format_arg_list *list1,
1130
                       struct format_arg_list *list2)
1131
0
{
1132
0
  struct format_arg_list *result;
1133
1134
0
  VERIFY_LIST (list1);
1135
0
  VERIFY_LIST (list2);
1136
1137
0
  if (list1->repeated.length > 0 && list2->repeated.length > 0)
1138
    /* Step 1: Ensure list1->repeated.length == list2->repeated.length.  */
1139
0
    {
1140
0
      size_t n1 = list1->repeated.length;
1141
0
      size_t n2 = list2->repeated.length;
1142
0
      size_t g = gcd (n1, n2);
1143
0
      size_t m1 = n2 / g; /* = lcm(n1,n2) / n1 */
1144
0
      size_t m2 = n1 / g; /* = lcm(n1,n2) / n2 */
1145
1146
0
      unfold_loop (list1, m1);
1147
0
      unfold_loop (list2, m2);
1148
      /* Now list1->repeated.length = list2->repeated.length = lcm(n1,n2).  */
1149
0
    }
1150
1151
0
  if (list1->repeated.length > 0 || list2->repeated.length > 0)
1152
    /* Step 2: Ensure the initial segment of the result can be computed
1153
       from the initial segments of list1 and list2.  If both have a
1154
       repeated segment, this means to ensure
1155
       list1->initial.length == list2->initial.length.  */
1156
0
    {
1157
0
      size_t m = MAX (list1->initial.length, list2->initial.length);
1158
1159
0
      if (list1->repeated.length > 0)
1160
0
        rotate_loop (list1, m);
1161
0
      if (list2->repeated.length > 0)
1162
0
        rotate_loop (list2, m);
1163
0
    }
1164
1165
0
  if (list1->repeated.length > 0 && list2->repeated.length > 0)
1166
0
    {
1167
0
      ASSERT (list1->initial.length == list2->initial.length);
1168
0
      ASSERT (list1->repeated.length == list2->repeated.length);
1169
0
    }
1170
1171
  /* Step 3: Allocate the result.  */
1172
0
  result = XMALLOC (struct format_arg_list);
1173
0
  result->initial.count = 0;
1174
0
  result->initial.allocated = 0;
1175
0
  result->initial.element = NULL;
1176
0
  result->initial.length = 0;
1177
0
  result->repeated.count = 0;
1178
0
  result->repeated.allocated = 0;
1179
0
  result->repeated.element = NULL;
1180
0
  result->repeated.length = 0;
1181
1182
  /* Step 4: Elementwise intersection of list1->initial, list2->initial.  */
1183
0
  {
1184
0
    struct format_arg *e1 = list1->initial.element;
1185
0
    size_t c1 = list1->initial.count;
1186
0
    struct format_arg *e2 = list2->initial.element;
1187
0
    size_t c2 = list2->initial.count;
1188
0
    while (c1 > 0 && c2 > 0)
1189
0
      {
1190
        /* Ensure room in result->initial.  */
1191
0
        grow_initial_alloc (result);
1192
0
        size_t initial_index = result->initial.count++;
1193
0
        struct format_arg *re = &result->initial.element[initial_index];
1194
0
        re->repcount = MIN (e1->repcount, e2->repcount);
1195
1196
        /* Intersect the argument types.  */
1197
0
        if (!make_intersected_element (re, e1, e2))
1198
0
          {
1199
0
            bool re_is_required = re->presence == FCT_REQUIRED;
1200
0
            result->initial.count--;
1201
1202
            /* If re->presence == FCT_OPTIONAL, the result list ends here.  */
1203
0
            if (re_is_required)
1204
              /* Contradiction.  Backtrack.  */
1205
0
              result = backtrack_in_initial (result);
1206
0
            goto done;
1207
0
          }
1208
1209
0
        result->initial.length += re->repcount;
1210
1211
0
        e1->repcount -= re->repcount;
1212
0
        if (e1->repcount == 0)
1213
0
          {
1214
0
            e1++;
1215
0
            c1--;
1216
0
          }
1217
0
        e2->repcount -= re->repcount;
1218
0
        if (e2->repcount == 0)
1219
0
          {
1220
0
            e2++;
1221
0
            c2--;
1222
0
          }
1223
0
      }
1224
1225
0
    if (list1->repeated.count == 0 && list2->repeated.count == 0)
1226
0
      {
1227
        /* Intersecting two finite lists.  */
1228
0
        if (c1 > 0)
1229
0
          {
1230
            /* list1 longer than list2.  */
1231
0
            if (e1->presence == FCT_REQUIRED)
1232
              /* Contradiction.  Backtrack.  */
1233
0
              result = backtrack_in_initial (result);
1234
0
          }
1235
0
        else if (c2 > 0)
1236
0
          {
1237
            /* list2 longer than list1.  */
1238
0
            if (e2->presence == FCT_REQUIRED)
1239
              /* Contradiction.  Backtrack.  */
1240
0
              result = backtrack_in_initial (result);
1241
0
          }
1242
0
        goto done;
1243
0
      }
1244
0
    else if (list1->repeated.count == 0)
1245
0
      {
1246
        /* Intersecting a finite and an infinite list.  */
1247
0
        ASSERT (c1 == 0);
1248
0
        if ((c2 > 0 ? e2->presence : list2->repeated.element[0].presence)
1249
0
            == FCT_REQUIRED)
1250
          /* Contradiction.  Backtrack.  */
1251
0
          result = backtrack_in_initial (result);
1252
0
        goto done;
1253
0
      }
1254
0
    else if (list2->repeated.count == 0)
1255
0
      {
1256
        /* Intersecting an infinite and a finite list.  */
1257
0
        ASSERT (c2 == 0);
1258
0
        if ((c1 > 0 ? e1->presence : list1->repeated.element[0].presence)
1259
0
            == FCT_REQUIRED)
1260
          /* Contradiction.  Backtrack.  */
1261
0
          result = backtrack_in_initial (result);
1262
0
        goto done;
1263
0
      }
1264
    /* Intersecting two infinite lists.  */
1265
0
    ASSERT (c1 == 0 && c2 == 0);
1266
0
  }
1267
1268
  /* Step 5: Elementwise intersection of list1->repeated, list2->repeated.  */
1269
0
  {
1270
0
    struct format_arg *e1 = list1->repeated.element;
1271
0
    size_t c1 = list1->repeated.count;
1272
0
    struct format_arg *e2 = list2->repeated.element;
1273
0
    size_t c2 = list2->repeated.count;
1274
0
    while (c1 > 0 && c2 > 0)
1275
0
      {
1276
        /* Ensure room in result->repeated.  */
1277
0
        grow_repeated_alloc (result);
1278
0
        size_t repeated_index = result->repeated.count++;
1279
0
        struct format_arg *re = &result->repeated.element[repeated_index];
1280
0
        re->repcount = MIN (e1->repcount, e2->repcount);
1281
1282
        /* Intersect the argument types.  */
1283
0
        if (!make_intersected_element (re, e1, e2))
1284
0
          {
1285
0
            bool re_is_required = re->presence == FCT_REQUIRED;
1286
0
            result->repeated.count--;
1287
1288
0
            append_repeated_to_initial (result);
1289
1290
            /* If re->presence == FCT_OPTIONAL, the result list ends here.  */
1291
0
            if (re_is_required)
1292
              /* Contradiction.  Backtrack.  */
1293
0
              result = backtrack_in_initial (result);
1294
1295
0
            goto done;
1296
0
          }
1297
1298
0
        result->repeated.length += re->repcount;
1299
1300
0
        e1->repcount -= re->repcount;
1301
0
        if (e1->repcount == 0)
1302
0
          {
1303
0
            e1++;
1304
0
            c1--;
1305
0
          }
1306
0
        e2->repcount -= re->repcount;
1307
0
        if (e2->repcount == 0)
1308
0
          {
1309
0
            e2++;
1310
0
            c2--;
1311
0
          }
1312
0
      }
1313
0
    ASSERT (c1 == 0 && c2 == 0);
1314
0
  }
1315
1316
0
 done:
1317
0
  free_list (list1);
1318
0
  free_list (list2);
1319
0
  if (result != NULL)
1320
0
    {
1321
      /* Undo the loop unfolding and unrolling done above.  */
1322
0
      normalize_outermost_list (result);
1323
0
      VERIFY_LIST (result);
1324
0
    }
1325
0
  return result;
1326
0
}
1327
1328
1329
/* Create the intersection of an argument list and the empty list.
1330
   Return NULL if the intersection is empty.  */
1331
/* Memory effects: The result, if non-NULL, is freshly allocated.  */
1332
static struct format_arg_list *
1333
make_intersection_with_empty_list (struct format_arg_list *list)
1334
0
{
1335
#if 0 /* equivalent but slower */
1336
  return make_intersected_list (copy_list (list), make_empty_list ());
1337
#else
1338
0
  if (list->initial.count > 0
1339
0
      ? list->initial.element[0].presence == FCT_REQUIRED
1340
0
      : list->repeated.count > 0
1341
0
        && list->repeated.element[0].presence == FCT_REQUIRED)
1342
0
    return NULL;
1343
0
  else
1344
0
    return make_empty_list ();
1345
0
#endif
1346
0
}
1347
1348
1349
/* Create the intersection of two argument list constraints.  NULL stands
1350
   for an impossible situation, i.e. a contradiction.  */
1351
/* Memory effects: list1 and list2 are freed if non-NULL.  The result,
1352
   if non-NULL, is freshly allocated.  */
1353
MAYBE_UNUSED static struct format_arg_list *
1354
intersection (struct format_arg_list *list1, struct format_arg_list *list2)
1355
0
{
1356
0
  if (list1 != NULL)
1357
0
    {
1358
0
      if (list2 != NULL)
1359
0
        return make_intersected_list (list1, list2);
1360
0
      else
1361
0
        {
1362
0
          free_list (list1);
1363
0
          return NULL;
1364
0
        }
1365
0
    }
1366
0
  else
1367
0
    {
1368
0
      if (list2 != NULL)
1369
0
        {
1370
0
          free_list (list2);
1371
0
          return NULL;
1372
0
        }
1373
0
      else
1374
0
        return NULL;
1375
0
    }
1376
0
}
1377
1378
1379
/* ===================== Union of two format_arg_lists ===================== */
1380
1381
/* Create the union (i.e. alternative constraints) of two argument
1382
   constraints.  */
1383
static void
1384
make_union_element (struct format_arg *re,
1385
                    const struct format_arg * e1,
1386
                    const struct format_arg * e2)
1387
0
{
1388
  /* Union of the cdr types.  */
1389
0
  if (e1->presence == FCT_REQUIRED && e2->presence == FCT_REQUIRED)
1390
0
    re->presence = FCT_REQUIRED;
1391
0
  else /* Either one of them is FCT_OPTIONAL.  */
1392
0
    re->presence = FCT_OPTIONAL;
1393
1394
  /* Union of the arg types.  */
1395
0
  if (e1->type == e2->type)
1396
0
    {
1397
0
      re->type = e1->type;
1398
0
      if (re->type == FAT_LIST)
1399
0
        re->list = make_union_list (copy_list (e1->list),
1400
0
                                    copy_list (e2->list));
1401
0
    }
1402
0
  else if (e1->type == FAT_CHARACTER_INTEGER_NULL
1403
0
           && (e2->type == FAT_CHARACTER_NULL || e2->type == FAT_CHARACTER
1404
0
               || e2->type == FAT_INTEGER_NULL || e2->type == FAT_INTEGER))
1405
0
    {
1406
0
      re->type = e1->type;
1407
0
    }
1408
0
  else if (e2->type == FAT_CHARACTER_INTEGER_NULL
1409
0
           && (e1->type == FAT_CHARACTER_NULL || e1->type == FAT_CHARACTER
1410
0
               || e1->type == FAT_INTEGER_NULL || e1->type == FAT_INTEGER))
1411
0
    {
1412
0
      re->type = e2->type;
1413
0
    }
1414
0
  else if (e1->type == FAT_CHARACTER_NULL && e2->type == FAT_CHARACTER)
1415
0
    {
1416
0
      re->type = e1->type;
1417
0
    }
1418
0
  else if (e2->type == FAT_CHARACTER_NULL && e1->type == FAT_CHARACTER)
1419
0
    {
1420
0
      re->type = e2->type;
1421
0
    }
1422
0
  else if (e1->type == FAT_INTEGER_NULL && e2->type == FAT_INTEGER)
1423
0
    {
1424
0
      re->type = e1->type;
1425
0
    }
1426
0
  else if (e2->type == FAT_INTEGER_NULL && e1->type == FAT_INTEGER)
1427
0
    {
1428
0
      re->type = e2->type;
1429
0
    }
1430
0
  else if (e1->type == FAT_REAL && e2->type == FAT_INTEGER)
1431
0
    {
1432
0
      re->type = e1->type;
1433
0
    }
1434
0
  else if (e2->type == FAT_REAL && e1->type == FAT_INTEGER)
1435
0
    {
1436
0
      re->type = e2->type;
1437
0
    }
1438
0
  else if (e1->type == FAT_LIST && is_empty_list (e1->list))
1439
0
    {
1440
0
      if (e2->type == FAT_CHARACTER_INTEGER_NULL
1441
0
          || e2->type == FAT_CHARACTER_NULL
1442
0
          || e2->type == FAT_INTEGER_NULL)
1443
0
        re->type = e2->type;
1444
0
      else if (e2->type == FAT_CHARACTER)
1445
0
        re->type = FAT_CHARACTER_NULL;
1446
0
      else if (e2->type == FAT_INTEGER)
1447
0
        re->type = FAT_INTEGER_NULL;
1448
0
      else
1449
0
        re->type = FAT_OBJECT;
1450
0
    }
1451
0
  else if (e2->type == FAT_LIST && is_empty_list (e2->list))
1452
0
    {
1453
0
      if (e1->type == FAT_CHARACTER_INTEGER_NULL
1454
0
          || e1->type == FAT_CHARACTER_NULL
1455
0
          || e1->type == FAT_INTEGER_NULL)
1456
0
        re->type = e1->type;
1457
0
      else if (e1->type == FAT_CHARACTER)
1458
0
        re->type = FAT_CHARACTER_NULL;
1459
0
      else if (e1->type == FAT_INTEGER)
1460
0
        re->type = FAT_INTEGER_NULL;
1461
0
      else
1462
0
        re->type = FAT_OBJECT;
1463
0
    }
1464
0
  else if ((e1->type == FAT_CHARACTER || e1->type == FAT_CHARACTER_NULL)
1465
0
           && (e2->type == FAT_INTEGER || e2->type == FAT_INTEGER_NULL))
1466
0
    {
1467
0
      re->type = FAT_CHARACTER_INTEGER_NULL;
1468
0
    }
1469
0
  else if ((e2->type == FAT_CHARACTER || e2->type == FAT_CHARACTER_NULL)
1470
0
           && (e1->type == FAT_INTEGER || e1->type == FAT_INTEGER_NULL))
1471
0
    {
1472
0
      re->type = FAT_CHARACTER_INTEGER_NULL;
1473
0
    }
1474
0
  else
1475
0
    {
1476
      /* Other union types are too hard to describe precisely.  */
1477
0
      re->type = FAT_OBJECT;
1478
0
    }
1479
0
}
1480
1481
/* Create the union (i.e. alternative constraints) of two argument list
1482
   constraints.  Free both argument lists when done.  */
1483
/* Memory effects: list1 and list2 are freed.  The result is freshly
1484
   allocated.  */
1485
static struct format_arg_list *
1486
make_union_list (struct format_arg_list *list1, struct format_arg_list *list2)
1487
0
{
1488
0
  struct format_arg_list *result;
1489
1490
0
  VERIFY_LIST (list1);
1491
0
  VERIFY_LIST (list2);
1492
1493
0
  if (list1->repeated.length > 0 && list2->repeated.length > 0)
1494
0
    {
1495
      /* Step 1: Ensure list1->repeated.length == list2->repeated.length.  */
1496
0
      {
1497
0
        size_t n1 = list1->repeated.length;
1498
0
        size_t n2 = list2->repeated.length;
1499
0
        size_t g = gcd (n1, n2);
1500
0
        size_t m1 = n2 / g; /* = lcm(n1,n2) / n1 */
1501
0
        size_t m2 = n1 / g; /* = lcm(n1,n2) / n2 */
1502
1503
0
        unfold_loop (list1, m1);
1504
0
        unfold_loop (list2, m2);
1505
        /* Now list1->repeated.length = list2->repeated.length = lcm(n1,n2).  */
1506
0
      }
1507
1508
      /* Step 2: Ensure that list1->initial.length == list2->initial.length.  */
1509
0
      {
1510
0
        size_t m = MAX (list1->initial.length, list2->initial.length);
1511
1512
0
        rotate_loop (list1, m);
1513
0
        rotate_loop (list2, m);
1514
0
      }
1515
1516
0
      ASSERT (list1->initial.length == list2->initial.length);
1517
0
      ASSERT (list1->repeated.length == list2->repeated.length);
1518
0
    }
1519
0
  else if (list1->repeated.length > 0)
1520
0
    {
1521
      /* Ensure the initial segment of the result can be computed from the
1522
         initial segment of list1.  */
1523
0
      if (list2->initial.length >= list1->initial.length)
1524
0
        {
1525
0
          rotate_loop (list1, list2->initial.length);
1526
0
          if (list1->repeated.element[0].presence == FCT_REQUIRED)
1527
0
            rotate_loop (list1, list1->initial.length + 1);
1528
0
        }
1529
0
    }
1530
0
  else if (list2->repeated.length > 0)
1531
0
    {
1532
      /* Ensure the initial segment of the result can be computed from the
1533
         initial segment of list2.  */
1534
0
      if (list1->initial.length >= list2->initial.length)
1535
0
        {
1536
0
          rotate_loop (list2, list1->initial.length);
1537
0
          if (list2->repeated.element[0].presence == FCT_REQUIRED)
1538
0
            rotate_loop (list2, list2->initial.length + 1);
1539
0
        }
1540
0
    }
1541
1542
  /* Step 3: Allocate the result.  */
1543
0
  result = XMALLOC (struct format_arg_list);
1544
0
  result->initial.count = 0;
1545
0
  result->initial.allocated = 0;
1546
0
  result->initial.element = NULL;
1547
0
  result->initial.length = 0;
1548
0
  result->repeated.count = 0;
1549
0
  result->repeated.allocated = 0;
1550
0
  result->repeated.element = NULL;
1551
0
  result->repeated.length = 0;
1552
1553
  /* Step 4: Elementwise union of list1->initial, list2->initial.  */
1554
0
  {
1555
0
    struct format_arg *e1 = list1->initial.element; size_t c1 = list1->initial.count;
1556
0
    struct format_arg *e2 = list2->initial.element; size_t c2 = list2->initial.count;
1557
0
    while (c1 > 0 && c2 > 0)
1558
0
      {
1559
        /* Ensure room in result->initial.  */
1560
0
        grow_initial_alloc (result);
1561
0
        size_t initial_index = result->initial.count++;
1562
0
        struct format_arg *re = &result->initial.element[initial_index];
1563
0
        re->repcount = MIN (e1->repcount, e2->repcount);
1564
1565
        /* Union of the argument types.  */
1566
0
        make_union_element (re, e1, e2);
1567
1568
0
        result->initial.length += re->repcount;
1569
1570
0
        e1->repcount -= re->repcount;
1571
0
        if (e1->repcount == 0)
1572
0
          {
1573
0
            e1++;
1574
0
            c1--;
1575
0
          }
1576
0
        e2->repcount -= re->repcount;
1577
0
        if (e2->repcount == 0)
1578
0
          {
1579
0
            e2++;
1580
0
            c2--;
1581
0
          }
1582
0
       }
1583
1584
0
    if (c1 > 0)
1585
0
      {
1586
        /* list2 already terminated, but still more elements in list1->initial.
1587
           Copy them all, but turn the first presence to FCT_OPTIONAL.  */
1588
0
        ASSERT (list2->repeated.count == 0);
1589
1590
0
        if (e1->presence == FCT_REQUIRED)
1591
0
          {
1592
            /* Ensure room in result->initial.  */
1593
0
            grow_initial_alloc (result);
1594
0
            size_t initial_index = result->initial.count++;
1595
0
            struct format_arg *re = &result->initial.element[initial_index];
1596
0
            copy_element (re, e1);
1597
0
            re->presence = FCT_OPTIONAL;
1598
0
            re->repcount = 1;
1599
0
            result->initial.length += 1;
1600
0
            e1->repcount -= 1;
1601
0
            if (e1->repcount == 0)
1602
0
              {
1603
0
                e1++;
1604
0
                c1--;
1605
0
              }
1606
0
          }
1607
1608
        /* Ensure room in result->initial.  */
1609
0
        ensure_initial_alloc (result, result->initial.count + c1);
1610
0
        while (c1 > 0)
1611
0
          {
1612
0
            size_t initial_index = result->initial.count++;
1613
0
            struct format_arg *re = &result->initial.element[initial_index];
1614
0
            copy_element (re, e1);
1615
0
            result->initial.length += re->repcount;
1616
0
            e1++;
1617
0
            c1--;
1618
0
          }
1619
0
      }
1620
0
    else if (c2 > 0)
1621
0
      {
1622
        /* list1 already terminated, but still more elements in list2->initial.
1623
           Copy them all, but turn the first presence to FCT_OPTIONAL.  */
1624
0
        ASSERT (list1->repeated.count == 0);
1625
1626
0
        if (e2->presence == FCT_REQUIRED)
1627
0
          {
1628
            /* Ensure room in result->initial.  */
1629
0
            grow_initial_alloc (result);
1630
0
            size_t initial_index = result->initial.count++;
1631
0
            struct format_arg *re = &result->initial.element[initial_index];
1632
0
            copy_element (re, e2);
1633
0
            re->presence = FCT_OPTIONAL;
1634
0
            re->repcount = 1;
1635
0
            result->initial.length += 1;
1636
0
            e2->repcount -= 1;
1637
0
            if (e2->repcount == 0)
1638
0
              {
1639
0
                e2++;
1640
0
                c2--;
1641
0
              }
1642
0
          }
1643
1644
        /* Ensure room in result->initial.  */
1645
0
        ensure_initial_alloc (result, result->initial.count + c2);
1646
0
        while (c2 > 0)
1647
0
          {
1648
0
            size_t initial_index = result->initial.count++;
1649
0
            struct format_arg *re = &result->initial.element[initial_index];
1650
0
            copy_element (re, e2);
1651
0
            result->initial.length += re->repcount;
1652
0
            e2++;
1653
0
            c2--;
1654
0
          }
1655
0
      }
1656
0
    ASSERT (c1 == 0 && c2 == 0);
1657
0
  }
1658
1659
0
  if (list1->repeated.length > 0 && list2->repeated.length > 0)
1660
    /* Step 5: Elementwise union of list1->repeated, list2->repeated.  */
1661
0
    {
1662
0
      struct format_arg *e1 = list1->repeated.element;
1663
0
      size_t c1 = list1->repeated.count;
1664
0
      struct format_arg *e2 = list2->repeated.element;
1665
0
      size_t c2 = list2->repeated.count;
1666
0
      while (c1 > 0 && c2 > 0)
1667
0
        {
1668
          /* Ensure room in result->repeated.  */
1669
0
          grow_repeated_alloc (result);
1670
0
          size_t repeated_index = result->repeated.count++;
1671
0
          struct format_arg *re = &result->repeated.element[repeated_index];
1672
0
          re->repcount = MIN (e1->repcount, e2->repcount);
1673
1674
          /* Union of the argument types.  */
1675
0
          make_union_element (re, e1, e2);
1676
1677
0
          result->repeated.length += re->repcount;
1678
1679
0
          e1->repcount -= re->repcount;
1680
0
          if (e1->repcount == 0)
1681
0
            {
1682
0
              e1++;
1683
0
              c1--;
1684
0
            }
1685
0
          e2->repcount -= re->repcount;
1686
0
          if (e2->repcount == 0)
1687
0
            {
1688
0
              e2++;
1689
0
              c2--;
1690
0
            }
1691
0
        }
1692
0
      ASSERT (c1 == 0 && c2 == 0);
1693
0
    }
1694
0
  else if (list1->repeated.length > 0)
1695
0
    {
1696
      /* Turning FCT_REQUIRED into FCT_OPTIONAL was already handled in the
1697
         initial segment.  Just copy the repeated segment of list1.  */
1698
0
      result->repeated.count = list1->repeated.count;
1699
0
      result->repeated.allocated = result->repeated.count;
1700
0
      result->repeated.element =
1701
0
        XNMALLOC (result->repeated.allocated, struct format_arg);
1702
0
      for (size_t i = 0; i < list1->repeated.count; i++)
1703
0
        copy_element (&result->repeated.element[i],
1704
0
                      &list1->repeated.element[i]);
1705
0
      result->repeated.length = list1->repeated.length;
1706
0
    }
1707
0
  else if (list2->repeated.length > 0)
1708
0
    {
1709
      /* Turning FCT_REQUIRED into FCT_OPTIONAL was already handled in the
1710
         initial segment.  Just copy the repeated segment of list2.  */
1711
0
      result->repeated.count = list2->repeated.count;
1712
0
      result->repeated.allocated = result->repeated.count;
1713
0
      result->repeated.element =
1714
0
        XNMALLOC (result->repeated.allocated, struct format_arg);
1715
0
      for (size_t i = 0; i < list2->repeated.count; i++)
1716
0
        copy_element (&result->repeated.element[i],
1717
0
                      &list2->repeated.element[i]);
1718
0
      result->repeated.length = list2->repeated.length;
1719
0
    }
1720
1721
0
  free_list (list1);
1722
0
  free_list (list2);
1723
  /* Undo the loop unfolding and unrolling done above.  */
1724
0
  normalize_outermost_list (result);
1725
0
  VERIFY_LIST (result);
1726
0
  return result;
1727
0
}
1728
1729
1730
/* Create the union of an argument list and the empty list.  */
1731
/* Memory effects: list is freed.  The result is freshly allocated.  */
1732
static struct format_arg_list *
1733
make_union_with_empty_list (struct format_arg_list *list)
1734
0
{
1735
#if 0 /* equivalent but slower */
1736
  return make_union_list (list, make_empty_list ());
1737
#else
1738
0
  VERIFY_LIST (list);
1739
1740
0
  if (list->initial.count > 0
1741
0
      ? list->initial.element[0].presence == FCT_REQUIRED
1742
0
      : list->repeated.count > 0
1743
0
        && list->repeated.element[0].presence == FCT_REQUIRED)
1744
0
    {
1745
0
      initial_splitelement (list, 1);
1746
0
      ASSERT (list->initial.count > 0);
1747
0
      ASSERT (list->initial.element[0].repcount == 1);
1748
0
      ASSERT (list->initial.element[0].presence == FCT_REQUIRED);
1749
0
      list->initial.element[0].presence = FCT_OPTIONAL;
1750
1751
      /* We might need to merge list->initial.element[0] and
1752
         list->initial.element[1].  */
1753
0
      normalize_outermost_list (list);
1754
0
    }
1755
1756
0
  VERIFY_LIST (list);
1757
1758
0
  return list;
1759
0
#endif
1760
0
}
1761
1762
1763
/* Create the union of two argument list constraints.  NULL stands for an
1764
   impossible situation, i.e. a contradiction.  */
1765
/* Memory effects: list1 and list2 are freed if non-NULL.  The result,
1766
   if non-NULL, is freshly allocated.  */
1767
static struct format_arg_list *
1768
union (struct format_arg_list *list1, struct format_arg_list *list2)
1769
0
{
1770
0
  if (list1 != NULL)
1771
0
    {
1772
0
      if (list2 != NULL)
1773
0
        return make_union_list (list1, list2);
1774
0
      else
1775
0
        return list1;
1776
0
    }
1777
0
  else
1778
0
    {
1779
0
      if (list2 != NULL)
1780
0
        return list2;
1781
0
      else
1782
0
        return NULL;
1783
0
    }
1784
0
}
1785
1786
1787
/* =========== Adding specific constraints to a format_arg_list =========== */
1788
1789
1790
/* Test whether arguments 0..n are required arguments in a list.  */
1791
static bool
1792
is_required (const struct format_arg_list *list, size_t n)
1793
0
{
1794
0
  size_t t;
1795
1796
  /* We'll check whether the first n+1 presence flags are FCT_REQUIRED.  */
1797
0
  t = n + 1;
1798
1799
  /* Walk the list->initial segment.  */
1800
0
  {
1801
0
    size_t s;
1802
1803
0
    for (s = 0;
1804
0
         s < list->initial.count && t >= list->initial.element[s].repcount;
1805
0
         t -= list->initial.element[s].repcount, s++)
1806
0
      if (list->initial.element[s].presence != FCT_REQUIRED)
1807
0
        return false;
1808
1809
0
    if (t == 0)
1810
0
      return true;
1811
1812
0
    if (s < list->initial.count)
1813
0
      {
1814
0
        if (list->initial.element[s].presence != FCT_REQUIRED)
1815
0
          return false;
1816
0
        else
1817
0
          return true;
1818
0
      }
1819
0
  }
1820
1821
  /* Walk the list->repeated segment.  */
1822
0
  if (list->repeated.count == 0)
1823
0
    return false;
1824
1825
0
  {
1826
0
    size_t s;
1827
1828
0
    for (s = 0;
1829
0
         s < list->repeated.count && t >= list->repeated.element[s].repcount;
1830
0
         t -= list->repeated.element[s].repcount, s++)
1831
0
      if (list->repeated.element[s].presence != FCT_REQUIRED)
1832
0
        return false;
1833
1834
0
    if (t == 0)
1835
0
      return true;
1836
1837
0
    if (s < list->repeated.count)
1838
0
      {
1839
0
        if (list->repeated.element[s].presence != FCT_REQUIRED)
1840
0
          return false;
1841
0
        else
1842
0
          return true;
1843
0
      }
1844
0
  }
1845
1846
  /* The list->repeated segment consists only of FCT_REQUIRED.  So,
1847
     regardless how many more passes through list->repeated would be
1848
     needed until t becomes 0, the result is true.  */
1849
0
  return true;
1850
0
}
1851
1852
1853
/* Add a constraint to an argument list, namely that the arguments 0...n are
1854
   present.  NULL stands for an impossible situation, i.e. a contradiction.  */
1855
/* Memory effects: list is freed.  The result is freshly allocated.  */
1856
static struct format_arg_list *
1857
add_required_constraint (struct format_arg_list *list, size_t n)
1858
0
{
1859
0
  if (list == NULL)
1860
0
    return NULL;
1861
1862
0
  VERIFY_LIST (list);
1863
1864
0
  if (list->repeated.count == 0 && list->initial.length <= n)
1865
0
    {
1866
      /* list is already constrained to have at most length n.
1867
         Contradiction.  */
1868
0
      free_list (list);
1869
0
      return NULL;
1870
0
    }
1871
1872
0
  initial_splitelement (list, n + 1);
1873
1874
0
  {
1875
0
    size_t i = 0;
1876
0
    for (size_t rest = n + 1; rest > 0; )
1877
0
      {
1878
0
        list->initial.element[i].presence = FCT_REQUIRED;
1879
0
        rest -= list->initial.element[i].repcount;
1880
0
        i++;
1881
0
      }
1882
0
  }
1883
1884
0
  VERIFY_LIST (list);
1885
1886
0
  return list;
1887
0
}
1888
1889
1890
/* Add a constraint to an argument list, namely that the argument n is
1891
   never present.  NULL stands for an impossible situation, i.e. a
1892
   contradiction.  */
1893
/* Memory effects: list is freed.  The result is freshly allocated.  */
1894
static struct format_arg_list *
1895
add_end_constraint (struct format_arg_list *list, size_t n)
1896
0
{
1897
0
  if (list == NULL)
1898
0
    return NULL;
1899
1900
0
  VERIFY_LIST (list);
1901
1902
0
  if (list->repeated.count == 0 && list->initial.length <= n)
1903
    /* list is already constrained to have at most length n.  */
1904
0
    return list;
1905
1906
0
  size_t s = initial_splitelement (list, n);
1907
0
  enum format_cdr_type n_presence =
1908
0
    (s < list->initial.count
1909
0
     ? /* n < list->initial.length */ list->initial.element[s].presence
1910
0
     : /* n >= list->initial.length */ list->repeated.element[0].presence);
1911
1912
0
  for (size_t i = s; i < list->initial.count; i++)
1913
0
    {
1914
0
      list->initial.length -= list->initial.element[i].repcount;
1915
0
      free_element (&list->initial.element[i]);
1916
0
    }
1917
0
  list->initial.count = s;
1918
1919
0
  for (size_t i = 0; i < list->repeated.count; i++)
1920
0
    free_element (&list->repeated.element[i]);
1921
0
  if (list->repeated.element != NULL)
1922
0
    free (list->repeated.element);
1923
0
  list->repeated.element = NULL;
1924
0
  list->repeated.allocated = 0;
1925
0
  list->repeated.count = 0;
1926
0
  list->repeated.length = 0;
1927
1928
0
  if (n_presence == FCT_REQUIRED)
1929
0
    return backtrack_in_initial (list);
1930
0
  else
1931
0
    return list;
1932
0
}
1933
1934
1935
/* Add a constraint to an argument list, namely that the argument n is
1936
   of a given type.  NULL stands for an impossible situation, i.e. a
1937
   contradiction.  Assumes a preceding add_required_constraint (list, n).  */
1938
/* Memory effects: list is freed.  The result is freshly allocated.  */
1939
static struct format_arg_list *
1940
add_type_constraint (struct format_arg_list *list, size_t n,
1941
                     enum format_arg_type type)
1942
0
{
1943
0
  if (list == NULL)
1944
0
    return NULL;
1945
1946
  /* Through the previous add_required_constraint, we can assume
1947
     list->initial.length >= n+1.  */
1948
1949
0
  size_t s = initial_unshare (list, n);
1950
1951
0
  struct format_arg newconstraint;
1952
0
  newconstraint.presence = FCT_OPTIONAL;
1953
0
  newconstraint.type = type;
1954
1955
0
  struct format_arg tmpelement;
1956
0
  if (!make_intersected_element (&tmpelement,
1957
0
                                 &list->initial.element[s], &newconstraint))
1958
0
    list = add_end_constraint (list, n);
1959
0
  else
1960
0
    {
1961
0
      free_element (&list->initial.element[s]);
1962
0
      list->initial.element[s].type = tmpelement.type;
1963
0
      list->initial.element[s].list = tmpelement.list;
1964
0
    }
1965
1966
0
  if (list != NULL)
1967
0
    VERIFY_LIST (list);
1968
1969
0
  return list;
1970
0
}
1971
1972
1973
/* Add a constraint to an argument list, namely that the argument n is
1974
   of a given list type.  NULL stands for an impossible situation, i.e. a
1975
   contradiction.  Assumes a preceding add_required_constraint (list, n).  */
1976
/* Memory effects: list is freed.  The result is freshly allocated.  */
1977
static struct format_arg_list *
1978
add_listtype_constraint (struct format_arg_list *list, size_t n,
1979
                         enum format_arg_type type,
1980
                         struct format_arg_list *sublist)
1981
0
{
1982
0
  if (list == NULL)
1983
0
    return NULL;
1984
1985
  /* Through the previous add_required_constraint, we can assume
1986
     list->initial.length >= n+1.  */
1987
1988
0
  size_t s = initial_unshare (list, n);
1989
1990
0
  struct format_arg newconstraint;
1991
0
  newconstraint.presence = FCT_OPTIONAL;
1992
0
  newconstraint.type = type;
1993
0
  newconstraint.list = sublist;
1994
1995
0
  struct format_arg tmpelement;
1996
0
  if (!make_intersected_element (&tmpelement,
1997
0
                                 &list->initial.element[s], &newconstraint))
1998
0
    list = add_end_constraint (list, n);
1999
0
  else
2000
0
    {
2001
0
      free_element (&list->initial.element[s]);
2002
0
      list->initial.element[s].type = tmpelement.type;
2003
0
      list->initial.element[s].list = tmpelement.list;
2004
0
    }
2005
2006
0
  if (list != NULL)
2007
0
    VERIFY_LIST (list);
2008
2009
0
  return list;
2010
0
}
2011
2012
2013
/* ============= Subroutines used by the format string parser ============= */
2014
2015
static void
2016
add_req_type_constraint (struct format_arg_list **listp,
2017
                         size_t position, enum format_arg_type type)
2018
0
{
2019
0
  *listp = add_required_constraint (*listp, position);
2020
0
  *listp = add_type_constraint (*listp, position, type);
2021
0
}
2022
2023
2024
static void
2025
add_req_listtype_constraint (struct format_arg_list **listp,
2026
                             size_t position, enum format_arg_type type,
2027
                             struct format_arg_list *sublist)
2028
0
{
2029
0
  *listp = add_required_constraint (*listp, position);
2030
0
  *listp = add_listtype_constraint (*listp, position, type, sublist);
2031
0
}
2032
2033
2034
/* Create an endless repeated list whose elements are lists constrained
2035
   by sublist.  */
2036
/* Memory effects: sublist is freed.  The result is freshly allocated.  */
2037
static struct format_arg_list *
2038
make_repeated_list_of_lists (struct format_arg_list *sublist)
2039
0
{
2040
0
  if (sublist == NULL)
2041
    /* The list cannot have a single element.  */
2042
0
    return make_empty_list ();
2043
0
  else
2044
0
    {
2045
0
      struct format_arg_list *listlist = XMALLOC (struct format_arg_list);
2046
0
      listlist->initial.count = 0;
2047
0
      listlist->initial.allocated = 0;
2048
0
      listlist->initial.element = NULL;
2049
0
      listlist->initial.length = 0;
2050
0
      listlist->repeated.count = 1;
2051
0
      listlist->repeated.allocated = 1;
2052
0
      listlist->repeated.element = XNMALLOC (1, struct format_arg);
2053
0
      listlist->repeated.element[0].repcount = 1;
2054
0
      listlist->repeated.element[0].presence = FCT_OPTIONAL;
2055
0
      listlist->repeated.element[0].type = FAT_LIST;
2056
0
      listlist->repeated.element[0].list = sublist;
2057
0
      listlist->repeated.length = 1;
2058
2059
0
      VERIFY_LIST (listlist);
2060
2061
0
      return listlist;
2062
0
    }
2063
0
}
2064
2065
2066
/* Create an endless repeated list which represents the union of a finite
2067
   number of copies of L, each time shifted by period:
2068
     ()
2069
     L
2070
     L and (*^period L)
2071
     L and (*^period L) and (*^{2 period} L)
2072
     L and (*^period L) and (*^{2 period} L) and (*^{3 period} L)
2073
     ...
2074
 */
2075
/* Memory effects: sublist is freed.  The result is freshly allocated.  */
2076
static struct format_arg_list *
2077
make_repeated_list (struct format_arg_list *sublist, size_t period)
2078
0
{
2079
0
  VERIFY_LIST (sublist);
2080
2081
0
  ASSERT (period > 0);
2082
2083
0
  struct segment *srcseg;
2084
0
  struct segment tmp;
2085
0
  size_t p;
2086
0
  if (sublist->repeated.count == 0)
2087
0
    {
2088
      /* L is a finite list.  */
2089
2090
0
      if (sublist->initial.length < period)
2091
        /* L and (*^period L) is a contradition, so we need to consider
2092
           only 1 and 0 iterations.  */
2093
0
        return make_union_with_empty_list (sublist);
2094
2095
0
      srcseg = &sublist->initial;
2096
0
      p = period;
2097
0
    }
2098
0
  else
2099
0
    {
2100
      /* L is an infinite list.  */
2101
      /* p := lcm (period, period of L)  */
2102
0
      size_t Lp = sublist->repeated.length;
2103
0
      size_t m = period / gcd (period, Lp); /* = lcm(period,Lp) / Lp */
2104
2105
0
      unfold_loop (sublist, m);
2106
0
      p = m * Lp;
2107
2108
      /* Concatenate the initial and the repeated segments into a single
2109
         segment.  */
2110
0
      tmp.count = sublist->initial.count + sublist->repeated.count;
2111
0
      tmp.allocated = tmp.count;
2112
0
      tmp.element = XNMALLOC (tmp.allocated, struct format_arg);
2113
0
      {
2114
0
        size_t i;
2115
0
        for (i = 0; i < sublist->initial.count; i++)
2116
0
          tmp.element[i] = sublist->initial.element[i];
2117
0
        for (size_t j = 0; j < sublist->repeated.count; j++)
2118
0
          {
2119
0
            tmp.element[i] = sublist->repeated.element[j];
2120
0
            i++;
2121
0
          }
2122
0
      }
2123
0
      tmp.length = sublist->initial.length + sublist->repeated.length;
2124
2125
0
      srcseg = &tmp;
2126
0
    }
2127
2128
0
  size_t n = srcseg->length;
2129
2130
  /* Example: n = 7, p = 2
2131
     Let L = (A B C D E F G).
2132
2133
     L                 =    A     B     C     D      E      F      G
2134
     L & L<<p          =    A     B    C&A   D&B    E&C    F&D    G&E
2135
     L & L<<p & L<<2p  =    A     B    C&A   D&B   E&C&A  F&D&B  G&E&C
2136
     ...               =    A     B    C&A   D&B   E&C&A  F&D&B G&E&C&A
2137
2138
     Thus the result has an initial segment of length n - p and a period
2139
     of p, and can be computed by floor(n/p) intersection operations.
2140
     Or by a single incremental intersection operation, going from left
2141
     to right.  */
2142
2143
0
  struct format_arg_list *list = XMALLOC (struct format_arg_list);
2144
0
  list->initial.count = 0;
2145
0
  list->initial.allocated = 0;
2146
0
  list->initial.element = NULL;
2147
0
  list->initial.length = 0;
2148
0
  list->repeated.count = 0;
2149
0
  list->repeated.allocated = 0;
2150
0
  list->repeated.element = NULL;
2151
0
  list->repeated.length = 0;
2152
2153
  /* Sketch:
2154
     for (i = 0; i < p; i++)
2155
       list->initial.element[i] = srcseg->element[i];
2156
     list->initial.element[0].presence = FCT_OPTIONAL;  // union with empty list
2157
     for (i = p, j = 0; i < n; i++, j++)
2158
       list->initial.element[i] = srcseg->element[i] & list->initial.element[j];
2159
   */
2160
2161
0
  bool ended = false;
2162
2163
0
  {
2164
0
    size_t i = 0;
2165
0
    size_t ti = 0;
2166
0
    size_t si = 0;
2167
0
    while (i < p)
2168
0
      {
2169
0
        size_t k = MIN (srcseg->element[si].repcount - ti, p - i);
2170
2171
        /* Ensure room in list->initial.  */
2172
0
        grow_initial_alloc (list);
2173
0
        size_t initial_index = list->initial.count++;
2174
0
        copy_element (&list->initial.element[initial_index],
2175
0
                      &srcseg->element[si]);
2176
0
        list->initial.element[initial_index].repcount = k;
2177
0
        list->initial.length += k;
2178
2179
0
        i += k;
2180
0
        ti += k;
2181
0
        if (ti == srcseg->element[si].repcount)
2182
0
          {
2183
0
            ti = 0;
2184
0
            si++;
2185
0
          }
2186
0
      }
2187
2188
0
    ASSERT (list->initial.count > 0);
2189
0
    if (list->initial.element[0].presence == FCT_REQUIRED)
2190
0
      {
2191
0
        initial_splitelement (list, 1);
2192
0
        ASSERT (list->initial.element[0].presence == FCT_REQUIRED);
2193
0
        ASSERT (list->initial.element[0].repcount == 1);
2194
0
        list->initial.element[0].presence = FCT_OPTIONAL;
2195
0
      }
2196
2197
0
    size_t j = 0;
2198
0
    size_t tj = 0;
2199
0
    size_t sj = 0;
2200
0
    while (i < n)
2201
0
      {
2202
0
        size_t k =
2203
0
          MIN (srcseg->element[si].repcount - ti,
2204
0
               list->initial.element[sj].repcount - tj);
2205
2206
        /* Ensure room in list->initial.  */
2207
0
        grow_initial_alloc (list);
2208
0
        size_t initial_index = list->initial.count++;
2209
0
        if (!make_intersected_element (&list->initial.element[initial_index],
2210
0
                                       &srcseg->element[si],
2211
0
                                       &list->initial.element[sj]))
2212
0
          {
2213
0
            bool ie_is_required =
2214
0
              list->initial.element[initial_index].presence == FCT_REQUIRED;
2215
0
            list->initial.count--;
2216
0
            if (ie_is_required)
2217
0
              {
2218
                /* Contradiction.  Backtrack.  */
2219
0
                list = backtrack_in_initial (list);
2220
0
                ASSERT (list != NULL); /* at least the empty list is valid */
2221
0
                return list;
2222
0
              }
2223
0
            else
2224
0
              {
2225
                /* The list ends here.  */
2226
0
                ended = true;
2227
0
                break;
2228
0
              }
2229
0
          }
2230
0
        list->initial.element[initial_index].repcount = k;
2231
0
        list->initial.length += k;
2232
2233
0
        i += k;
2234
0
        ti += k;
2235
0
        if (ti == srcseg->element[si].repcount)
2236
0
          {
2237
0
            ti = 0;
2238
0
            si++;
2239
0
          }
2240
2241
0
        j += k;
2242
0
        tj += k;
2243
0
        if (tj == list->initial.element[sj].repcount)
2244
0
          {
2245
0
            tj = 0;
2246
0
            sj++;
2247
0
          }
2248
0
      }
2249
0
    if (!ended)
2250
0
      ASSERT (list->initial.length == n);
2251
0
  }
2252
2253
  /* Add optional exit points at 0, period, 2*period etc.
2254
     FIXME: Not sure this is correct in all cases.  */
2255
0
  for (size_t i = 0; i < list->initial.length; i += period)
2256
0
    {
2257
0
      size_t si = initial_unshare (list, i);
2258
0
      list->initial.element[si].presence = FCT_OPTIONAL;
2259
0
    }
2260
2261
0
  if (!ended)
2262
0
    {
2263
      /* Now split off the repeated part.  */
2264
0
      size_t splitindex = initial_splitelement (list, n - p);
2265
0
      size_t newcount = list->initial.count - splitindex;
2266
0
      if (newcount > list->repeated.allocated)
2267
0
        {
2268
0
          list->repeated.allocated = newcount;
2269
0
          list->repeated.element = XNMALLOC (newcount, struct format_arg);
2270
0
        }
2271
0
      list->repeated.count = newcount;
2272
0
      {
2273
0
        size_t i = splitindex;
2274
0
        for (size_t j = 0; j < newcount; j++)
2275
0
          {
2276
0
            list->repeated.element[j] = list->initial.element[i];
2277
0
            i++;
2278
0
          }
2279
0
      }
2280
0
      list->repeated.length = p;
2281
0
      list->initial.count = splitindex;
2282
0
      list->initial.length = n - p;
2283
0
    }
2284
2285
0
  VERIFY_LIST (list);
2286
2287
0
  return list;
2288
0
}
2289
2290
2291
/* ================= Handling of format string directives ================= */
2292
2293
/* Possible signatures of format directives.  */
2294
static const enum format_arg_type I [1] = { FAT_INTEGER_NULL };
2295
static const enum format_arg_type II [2] = {
2296
  FAT_INTEGER_NULL, FAT_INTEGER_NULL
2297
};
2298
static const enum format_arg_type ICCI [4] = {
2299
  FAT_INTEGER_NULL, FAT_CHARACTER_NULL, FAT_CHARACTER_NULL, FAT_INTEGER_NULL
2300
};
2301
static const enum format_arg_type IIIC [4] = {
2302
  FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_CHARACTER_NULL
2303
};
2304
static const enum format_arg_type IICCI [5] = {
2305
  FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_CHARACTER_NULL, FAT_CHARACTER_NULL,
2306
  FAT_INTEGER_NULL
2307
};
2308
static const enum format_arg_type IIICC [5] = {
2309
  FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_CHARACTER_NULL,
2310
  FAT_CHARACTER_NULL
2311
};
2312
static const enum format_arg_type IIIICCC [7] = {
2313
  FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_INTEGER_NULL,
2314
  FAT_CHARACTER_NULL, FAT_CHARACTER_NULL, FAT_CHARACTER_NULL
2315
};
2316
static const enum format_arg_type THREE [3] = {
2317
  FAT_CHARACTER_INTEGER_NULL, FAT_CHARACTER_INTEGER_NULL,
2318
  FAT_CHARACTER_INTEGER_NULL
2319
};
2320
2321
2322
/* Check the parameters.  For V params, add the constraint to the argument
2323
   list.  Return false and fill in *invalid_reason if the format string is
2324
   invalid.  */
2325
static bool
2326
check_params (struct format_arg_list **listp,
2327
              size_t paramcount, struct param *params,
2328
              size_t t_count, const enum format_arg_type *t_types,
2329
              size_t directives, char **invalid_reason)
2330
0
{
2331
0
  size_t orig_paramcount = paramcount;
2332
0
  size_t orig_t_count = t_count;
2333
2334
0
  for (; paramcount > 0 && t_count > 0;
2335
0
         params++, paramcount--, t_types++, t_count--)
2336
0
    {
2337
0
      switch (*t_types)
2338
0
        {
2339
0
        case FAT_CHARACTER_INTEGER_NULL:
2340
0
          break;
2341
0
        case FAT_CHARACTER_NULL:
2342
0
          switch (params->type)
2343
0
            {
2344
0
            case PT_NIL: case PT_CHARACTER: case PT_V:
2345
0
              break;
2346
0
            case PT_INTEGER: case PT_ARGCOUNT:
2347
              /* wrong param type */
2348
0
              *invalid_reason =
2349
0
                xasprintf (_("In the directive number %zu, parameter %zu is of type '%s' but a parameter of type '%s' is expected."), directives, orig_paramcount - paramcount + 1, "integer", "character");
2350
0
              return false;
2351
0
            }
2352
0
          break;
2353
0
        case FAT_INTEGER_NULL:
2354
0
          switch (params->type)
2355
0
            {
2356
0
            case PT_NIL: case PT_INTEGER: case PT_ARGCOUNT: case PT_V:
2357
0
              break;
2358
0
            case PT_CHARACTER:
2359
              /* wrong param type */
2360
0
              *invalid_reason =
2361
0
                xasprintf (_("In the directive number %zu, parameter %zu is of type '%s' but a parameter of type '%s' is expected."), directives, orig_paramcount - paramcount + 1, "character", "integer");
2362
0
              return false;
2363
0
            }
2364
0
          break;
2365
0
        default:
2366
0
          abort ();
2367
0
        }
2368
0
      if (params->type == PT_V)
2369
0
        {
2370
0
          int position = params->value;
2371
0
          if (position >= 0)
2372
0
            add_req_type_constraint (listp, position, *t_types);
2373
0
        }
2374
0
    }
2375
2376
0
  for (; paramcount > 0; params++, paramcount--)
2377
0
    switch (params->type)
2378
0
      {
2379
0
      case PT_NIL:
2380
0
        break;
2381
0
      case PT_CHARACTER: case PT_INTEGER: case PT_ARGCOUNT:
2382
        /* too many params for directive */
2383
0
        *invalid_reason =
2384
0
          xasprintf (ngettext ("In the directive number %zu, too many parameters are given; expected at most %zu parameter.",
2385
0
                               "In the directive number %zu, too many parameters are given; expected at most %zu parameters.",
2386
0
                               orig_t_count),
2387
0
                     directives, orig_t_count);
2388
0
        return false;
2389
0
      case PT_V:
2390
        /* Force argument to be NIL.  */
2391
0
        {
2392
0
          int position = params->value;
2393
0
          if (position >= 0)
2394
0
            {
2395
0
              struct format_arg_list *empty_list = make_empty_list ();
2396
0
              add_req_listtype_constraint (listp, position,
2397
0
                                           FAT_LIST, empty_list);
2398
0
              free_list (empty_list);
2399
0
            }
2400
0
        }
2401
0
        break;
2402
0
      }
2403
2404
0
  return true;
2405
0
}
2406
2407
2408
/* Handle the parameters, without a priori type information.
2409
   For V params, add the constraint to the argument list.
2410
   Return false and fill in *invalid_reason if the format string is
2411
   invalid.  */
2412
static bool
2413
nocheck_params (struct format_arg_list **listp,
2414
                size_t paramcount, struct param *params,
2415
                size_t directives, char **invalid_reason)
2416
0
{
2417
0
  (void) directives;
2418
0
  (void) invalid_reason;
2419
2420
0
  for (; paramcount > 0; params++, paramcount--)
2421
0
    if (params->type == PT_V)
2422
0
      {
2423
0
        int position = params->value;
2424
0
        if (position >= 0)
2425
0
          add_req_type_constraint (listp, position, FAT_CHARACTER_INTEGER_NULL);
2426
0
      }
2427
2428
0
  return true;
2429
0
}
2430
2431
2432
/* ======================= The format string parser ======================= */
2433
2434
/* Parse a piece of format string, until the matching terminating format
2435
   directive is encountered.
2436
   format is the remainder of the format string.
2437
   position is the position in this argument list, if known, or -1 if unknown.
2438
   list represents the argument list constraints at the current parse point.
2439
   NULL stands for a contradiction.
2440
   escape represents the union of the argument list constraints at all the
2441
   currently pending FORMAT-UP-AND-OUT points. NULL stands for a contradiction
2442
   or an empty union.
2443
   All four are updated upon valid return.
2444
   *separatorp is set to true if the parse terminated due to a ~; separator,
2445
   more precisely to 2 if with colon, or to 1 if without colon.
2446
   spec is the global struct spec.
2447
   terminator is the directive that terminates this parse.
2448
   separator specifies if ~; separators are allowed.
2449
   fdi is an array to be filled with format directive indicators, or NULL.
2450
   If the format string is invalid, false is returned and *invalid_reason is
2451
   set to an error message explaining why.  */
2452
static bool
2453
parse_upto (const char **formatp,
2454
            int *positionp, struct format_arg_list **listp,
2455
            struct format_arg_list **escapep, int *separatorp,
2456
            struct spec *spec, char terminator, bool separator,
2457
            char *fdi, char **invalid_reason)
2458
0
{
2459
0
  const char *format = *formatp;
2460
0
  const char *const format_start = format;
2461
0
  int position = *positionp;
2462
0
  struct format_arg_list *list = *listp;
2463
0
  struct format_arg_list *escape = *escapep;
2464
2465
0
  for (; *format != '\0'; )
2466
0
    if (*format++ == '~')
2467
0
      {
2468
0
        FDI_SET (format - 1, FMTDIR_START);
2469
2470
        /* Count number of directives.  */
2471
0
        spec->directives++;
2472
2473
        /* Parse parameters.  */
2474
0
        size_t paramcount = 0;
2475
0
        struct param *params = NULL;
2476
0
        for (;;)
2477
0
          {
2478
0
            enum param_type type = PT_NIL;
2479
0
            int value = 0;
2480
2481
0
            if (c_isdigit (*format))
2482
0
              {
2483
0
                type = PT_INTEGER;
2484
0
                do
2485
0
                  {
2486
0
                    value = 10 * value + (*format - '0');
2487
0
                    format++;
2488
0
                  }
2489
0
                while (c_isdigit (*format));
2490
0
              }
2491
0
            else if (*format == '+' || *format == '-')
2492
0
              {
2493
0
                bool negative = (*format == '-');
2494
0
                type = PT_INTEGER;
2495
0
                format++;
2496
0
                if (!c_isdigit (*format))
2497
0
                  {
2498
0
                    if (*format == '\0')
2499
0
                      {
2500
0
                        *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
2501
0
                        FDI_SET (format - 1, FMTDIR_ERROR);
2502
0
                      }
2503
0
                    else
2504
0
                      {
2505
0
                        *invalid_reason =
2506
0
                          xasprintf (_("In the directive number %zu, '%c' is not followed by a digit."), spec->directives, format[-1]);
2507
0
                        FDI_SET (format, FMTDIR_ERROR);
2508
0
                      }
2509
0
                    return false;
2510
0
                  }
2511
0
                do
2512
0
                  {
2513
0
                    value = 10 * value + (*format - '0');
2514
0
                    format++;
2515
0
                  }
2516
0
                while (c_isdigit (*format));
2517
0
                if (negative)
2518
0
                  value = -value;
2519
0
              }
2520
0
            else if (*format == '\'')
2521
0
              {
2522
0
                type = PT_CHARACTER;
2523
0
                format++;
2524
0
                if (*format == '\0')
2525
0
                  {
2526
0
                    *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
2527
0
                    FDI_SET (format - 1, FMTDIR_ERROR);
2528
0
                    return false;
2529
0
                  }
2530
0
                format++;
2531
0
              }
2532
0
            else if (*format == 'V' || *format == 'v')
2533
0
              {
2534
0
                type = PT_V;
2535
0
                format++;
2536
0
                value = position;
2537
                /* Consumes an argument.  */
2538
0
                if (position >= 0)
2539
0
                  position++;
2540
0
              }
2541
0
            else if (*format == '#')
2542
0
              {
2543
0
                type = PT_ARGCOUNT;
2544
0
                format++;
2545
0
              }
2546
2547
0
            params =
2548
0
              (struct param *)
2549
0
              xrealloc (params, (paramcount + 1) * sizeof (struct param));
2550
0
            params[paramcount].type = type;
2551
0
            params[paramcount].value = value;
2552
0
            paramcount++;
2553
2554
0
            if (*format == ',')
2555
0
              format++;
2556
0
            else
2557
0
              break;
2558
0
          }
2559
2560
        /* Parse modifiers.  */
2561
0
        bool colon_p = false;
2562
0
        bool atsign_p = false;
2563
0
        for (;;)
2564
0
          {
2565
0
            if (*format == ':')
2566
0
              {
2567
0
                format++;
2568
0
                colon_p = true;
2569
0
              }
2570
0
            else if (*format == '@')
2571
0
              {
2572
0
                format++;
2573
0
                atsign_p = true;
2574
0
              }
2575
0
            else
2576
0
              break;
2577
0
          }
2578
2579
        /* Parse directive.  */
2580
0
        switch (*format++)
2581
0
          {
2582
0
          case 'A': case 'a': /* 22.3.4.1 FORMAT-ASCII */
2583
0
          case 'S': case 's': /* 22.3.4.2 FORMAT-S-EXPRESSION */
2584
0
            if (!check_params (&list, paramcount, params, 4, IIIC,
2585
0
                               spec->directives, invalid_reason))
2586
0
              {
2587
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2588
0
                return false;
2589
0
              }
2590
0
            if (position >= 0)
2591
0
              add_req_type_constraint (&list, position++, FAT_OBJECT);
2592
0
            break;
2593
2594
0
          case 'W': case 'w': /* 22.3.4.3 FORMAT-WRITE */
2595
0
            if (!check_params (&list, paramcount, params, 0, NULL,
2596
0
                               spec->directives, invalid_reason))
2597
0
              {
2598
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2599
0
                return false;
2600
0
              }
2601
0
            if (position >= 0)
2602
0
              add_req_type_constraint (&list, position++, FAT_OBJECT);
2603
0
            break;
2604
2605
0
          case 'D': case 'd': /* 22.3.2.2 FORMAT-DECIMAL */
2606
0
          case 'B': case 'b': /* 22.3.2.3 FORMAT-BINARY */
2607
0
          case 'O': case 'o': /* 22.3.2.4 FORMAT-OCTAL */
2608
0
          case 'X': case 'x': /* 22.3.2.5 FORMAT-HEXADECIMAL */
2609
0
            if (!check_params (&list, paramcount, params, 4, ICCI,
2610
0
                               spec->directives, invalid_reason))
2611
0
              {
2612
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2613
0
                return false;
2614
0
              }
2615
0
            if (position >= 0)
2616
0
              add_req_type_constraint (&list, position++, FAT_INTEGER);
2617
0
            break;
2618
2619
0
          case 'R': case 'r': /* 22.3.2.1 FORMAT-RADIX */
2620
0
            if (!check_params (&list, paramcount, params, 5, IICCI,
2621
0
                               spec->directives, invalid_reason))
2622
0
              {
2623
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2624
0
                return false;
2625
0
              }
2626
0
            if (position >= 0)
2627
0
              add_req_type_constraint (&list, position++, FAT_INTEGER);
2628
0
            break;
2629
2630
0
          case 'P': case 'p': /* 22.3.8.3 FORMAT-PLURAL */
2631
0
            if (!check_params (&list, paramcount, params, 0, NULL,
2632
0
                               spec->directives, invalid_reason))
2633
0
              {
2634
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2635
0
                return false;
2636
0
              }
2637
0
            if (colon_p)
2638
0
              {
2639
                /* Go back by 1 argument.  */
2640
0
                if (position > 0)
2641
0
                  position--;
2642
0
              }
2643
0
            if (position >= 0)
2644
0
              add_req_type_constraint (&list, position++, FAT_OBJECT);
2645
0
            break;
2646
2647
0
          case 'C': case 'c': /* 22.3.1.1 FORMAT-CHARACTER */
2648
0
            if (!check_params (&list, paramcount, params, 0, NULL,
2649
0
                               spec->directives, invalid_reason))
2650
0
              {
2651
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2652
0
                return false;
2653
0
              }
2654
0
            if (position >= 0)
2655
0
              add_req_type_constraint (&list, position++, FAT_CHARACTER);
2656
0
            break;
2657
2658
0
          case 'F': case 'f': /* 22.3.3.1 FORMAT-FIXED-FLOAT */
2659
0
            if (!check_params (&list, paramcount, params, 5, IIICC,
2660
0
                               spec->directives, invalid_reason))
2661
0
              {
2662
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2663
0
                return false;
2664
0
              }
2665
0
            if (position >= 0)
2666
0
              add_req_type_constraint (&list, position++, FAT_REAL);
2667
0
            break;
2668
2669
0
          case 'E': case 'e': /* 22.3.3.2 FORMAT-EXPONENTIAL-FLOAT */
2670
0
          case 'G': case 'g': /* 22.3.3.3 FORMAT-GENERAL-FLOAT */
2671
0
            if (!check_params (&list, paramcount, params, 7, IIIICCC,
2672
0
                               spec->directives, invalid_reason))
2673
0
              {
2674
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2675
0
                return false;
2676
0
              }
2677
0
            if (position >= 0)
2678
0
              add_req_type_constraint (&list, position++, FAT_REAL);
2679
0
            break;
2680
2681
0
          case '$': /* 22.3.3.4 FORMAT-DOLLARS-FLOAT */
2682
0
            if (!check_params (&list, paramcount, params, 4, IIIC,
2683
0
                               spec->directives, invalid_reason))
2684
0
              {
2685
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2686
0
                return false;
2687
0
              }
2688
0
            if (position >= 0)
2689
0
              add_req_type_constraint (&list, position++, FAT_REAL);
2690
0
            break;
2691
2692
0
          case '%': /* 22.3.1.2 FORMAT-TERPRI */
2693
0
          case '&': /* 22.3.1.3 FORMAT-FRESH-LINE */
2694
0
          case '|': /* 22.3.1.4 FORMAT-PAGE */
2695
0
          case '~': /* 22.3.1.5 FORMAT-TILDE */
2696
0
          case 'I': case 'i': /* 22.3.5.3 */
2697
0
            if (!check_params (&list, paramcount, params, 1, I,
2698
0
                               spec->directives, invalid_reason))
2699
0
              {
2700
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2701
0
                return false;
2702
0
              }
2703
0
            break;
2704
2705
0
          case '\n': /* 22.3.9.3 #\Newline */
2706
0
          case '_': /* 22.3.5.1 */
2707
0
            if (!check_params (&list, paramcount, params, 0, NULL,
2708
0
                               spec->directives, invalid_reason))
2709
0
              {
2710
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2711
0
                return false;
2712
0
              }
2713
0
            break;
2714
2715
0
          case 'T': case 't': /* 22.3.6.1 FORMAT-TABULATE */
2716
0
            if (!check_params (&list, paramcount, params, 2, II,
2717
0
                               spec->directives, invalid_reason))
2718
0
              {
2719
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2720
0
                return false;
2721
0
              }
2722
0
            break;
2723
2724
0
          case '*': /* 22.3.7.1 FORMAT-GOTO */
2725
0
            if (!check_params (&list, paramcount, params, 1, I,
2726
0
                               spec->directives, invalid_reason))
2727
0
              {
2728
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2729
0
                return false;
2730
0
              }
2731
0
            {
2732
0
              int n; /* value of first parameter */
2733
0
              if (paramcount == 0
2734
0
                  || (paramcount >= 1 && params[0].type == PT_NIL))
2735
0
                n = (atsign_p ? 0 : 1);
2736
0
              else if (paramcount >= 1 && params[0].type == PT_INTEGER)
2737
0
                n = params[0].value;
2738
0
              else
2739
0
                {
2740
                  /* Unknown argument, leads to an unknown position.  */
2741
0
                  position = -1;
2742
0
                  break;
2743
0
                }
2744
0
              if (n < 0)
2745
0
                {
2746
                  /* invalid argument */
2747
0
                  *invalid_reason =
2748
0
                    xasprintf (_("In the directive number %zu, the argument %d is negative."), spec->directives, n);
2749
0
                  FDI_SET (format - 1, FMTDIR_ERROR);
2750
0
                  return false;
2751
0
                }
2752
0
              if (atsign_p)
2753
0
                {
2754
                  /* Absolute goto.  */
2755
0
                  position = n;
2756
0
                }
2757
0
              else if (colon_p)
2758
0
                {
2759
                  /* Backward goto.  */
2760
0
                  if (n > 0)
2761
0
                    {
2762
0
                      if (position >= 0)
2763
0
                        {
2764
0
                          if (position >= n)
2765
0
                            position -= n;
2766
0
                          else
2767
0
                            position = 0;
2768
0
                        }
2769
0
                      else
2770
0
                        position = -1;
2771
0
                   }
2772
0
                }
2773
0
              else
2774
0
                {
2775
                  /* Forward goto.  */
2776
0
                  if (position >= 0)
2777
0
                    position += n;
2778
0
                }
2779
0
            }
2780
0
            break;
2781
2782
0
          case '?': /* 22.3.7.6 FORMAT-INDIRECTION */
2783
0
            if (!check_params (&list, paramcount, params, 0, NULL,
2784
0
                               spec->directives, invalid_reason))
2785
0
              {
2786
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2787
0
                return false;
2788
0
              }
2789
0
            if (position >= 0)
2790
0
              add_req_type_constraint (&list, position++, FAT_FORMATSTRING);
2791
0
            if (atsign_p)
2792
0
              position = -1;
2793
0
            else
2794
0
              if (position >= 0)
2795
0
                {
2796
0
                  struct format_arg_list *sublist = make_unconstrained_list ();
2797
0
                  add_req_listtype_constraint (&list, position++,
2798
0
                                               FAT_LIST, sublist);
2799
0
                  free_list (sublist);
2800
0
                }
2801
0
            break;
2802
2803
0
          case '/': /* 22.3.5.4 FORMAT-CALL-USER-FUNCTION */
2804
0
            if (!check_params (&list, paramcount, params, 0, NULL,
2805
0
                               spec->directives, invalid_reason))
2806
0
              {
2807
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2808
0
                return false;
2809
0
              }
2810
0
            if (position >= 0)
2811
0
              add_req_type_constraint (&list, position++, FAT_OBJECT);
2812
0
            while (*format != '\0' && *format != '/')
2813
0
              format++;
2814
0
            if (*format == '\0')
2815
0
              {
2816
0
                *invalid_reason =
2817
0
                  xstrdup (_("The string ends in the middle of a ~/.../ directive."));
2818
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2819
0
                return false;
2820
0
              }
2821
0
            format++;
2822
0
            break;
2823
2824
0
          case '(': /* 22.3.8.1 FORMAT-CASE-CONVERSION */
2825
0
            if (!check_params (&list, paramcount, params, 0, NULL,
2826
0
                               spec->directives, invalid_reason))
2827
0
              {
2828
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2829
0
                return false;
2830
0
              }
2831
0
            *formatp = format;
2832
0
            *positionp = position;
2833
0
            *listp = list;
2834
0
            *escapep = escape;
2835
0
            {
2836
0
              if (!parse_upto (formatp, positionp, listp, escapep,
2837
0
                               NULL, spec, ')', false,
2838
0
                               NULL, invalid_reason))
2839
0
                {
2840
0
                  FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
2841
0
                           FMTDIR_ERROR);
2842
0
                  return false;
2843
0
                }
2844
0
            }
2845
0
            format = *formatp;
2846
0
            position = *positionp;
2847
0
            list = *listp;
2848
0
            escape = *escapep;
2849
0
            break;
2850
2851
0
          case ')': /* 22.3.8.2 FORMAT-CASE-CONVERSION-END */
2852
0
            if (terminator != ')')
2853
0
              {
2854
0
                *invalid_reason =
2855
0
                  xasprintf (_("Found '~%c' without matching '~%c'."), ')', '(');
2856
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2857
0
                return false;
2858
0
              }
2859
0
            if (!check_params (&list, paramcount, params, 0, NULL,
2860
0
                               spec->directives, invalid_reason))
2861
0
              {
2862
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2863
0
                return false;
2864
0
              }
2865
0
            *formatp = format;
2866
0
            *positionp = position;
2867
0
            *listp = list;
2868
0
            *escapep = escape;
2869
0
            return true;
2870
2871
0
          case '[': /* 22.3.7.2 FORMAT-CONDITIONAL */
2872
0
            if (atsign_p && colon_p)
2873
0
              {
2874
0
                *invalid_reason =
2875
0
                  xasprintf (_("In the directive number %zu, both the @ and the : modifiers are given."), spec->directives);
2876
0
                FDI_SET (format - 1, FMTDIR_ERROR);
2877
0
                return false;
2878
0
              }
2879
0
            else if (atsign_p)
2880
0
              {
2881
0
                if (!check_params (&list, paramcount, params, 0, NULL,
2882
0
                                   spec->directives, invalid_reason))
2883
0
                  {
2884
0
                    FDI_SET (format - 1, FMTDIR_ERROR);
2885
0
                    return false;
2886
0
                  }
2887
2888
0
                *formatp = format;
2889
0
                *escapep = escape;
2890
2891
                /* First alternative: argument is NIL.  */
2892
0
                struct format_arg_list *nil_list =
2893
0
                  (list != NULL ? copy_list (list) : NULL);
2894
0
                if (position >= 0)
2895
0
                  {
2896
0
                    struct format_arg_list *empty_list = make_empty_list ();
2897
0
                    add_req_listtype_constraint (&nil_list, position,
2898
0
                                                 FAT_LIST, empty_list);
2899
0
                    free_list (empty_list);
2900
0
                  }
2901
2902
                /* Second alternative: use sub-format.  */
2903
0
                struct format_arg_list *union_list;
2904
0
                {
2905
0
                  int sub_position = position;
2906
0
                  struct format_arg_list *sub_list =
2907
0
                    (list != NULL ? copy_list (list) : NULL);
2908
0
                  if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
2909
0
                                   NULL, spec, ']', false,
2910
0
                                   NULL, invalid_reason))
2911
0
                    {
2912
0
                      FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
2913
0
                               FMTDIR_ERROR);
2914
0
                      return false;
2915
0
                    }
2916
0
                  if (sub_list != NULL)
2917
0
                    {
2918
0
                      if (position >= 0)
2919
0
                        {
2920
0
                          if (sub_position == position + 1)
2921
                            /* new position is branch independent */
2922
0
                            position = position + 1;
2923
0
                          else
2924
                            /* new position is branch dependent */
2925
0
                            position = -1;
2926
0
                        }
2927
0
                    }
2928
0
                  else
2929
0
                    {
2930
0
                      if (position >= 0)
2931
0
                        position = position + 1;
2932
0
                    }
2933
0
                  union_list = union (nil_list, sub_list);
2934
0
                }
2935
2936
0
                format = *formatp;
2937
0
                escape = *escapep;
2938
2939
0
                if (list != NULL)
2940
0
                  free_list (list);
2941
0
                list = union_list;
2942
0
              }
2943
0
            else if (colon_p)
2944
0
              {
2945
0
                if (!check_params (&list, paramcount, params, 0, NULL,
2946
0
                                   spec->directives, invalid_reason))
2947
0
                  {
2948
0
                    FDI_SET (format - 1, FMTDIR_ERROR);
2949
0
                    return false;
2950
0
                  }
2951
2952
0
                if (position >= 0)
2953
0
                  add_req_type_constraint (&list, position++, FAT_OBJECT);
2954
2955
0
                *formatp = format;
2956
0
                *escapep = escape;
2957
0
                int union_position = -2;
2958
0
                struct format_arg_list *union_list = NULL;
2959
2960
                /* First alternative.  */
2961
0
                {
2962
0
                  int sub_position = position;
2963
0
                  struct format_arg_list *sub_list =
2964
0
                    (list != NULL ? copy_list (list) : NULL);
2965
0
                  int sub_separator = 0;
2966
0
                  if (position >= 0)
2967
0
                    {
2968
0
                      struct format_arg_list *empty_list = make_empty_list ();
2969
0
                      add_req_listtype_constraint (&sub_list, position - 1,
2970
0
                                                   FAT_LIST, empty_list);
2971
0
                      free_list (empty_list);
2972
0
                    }
2973
0
                  if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
2974
0
                                   &sub_separator, spec, ']', true,
2975
0
                                   NULL, invalid_reason))
2976
0
                    {
2977
0
                      FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
2978
0
                               FMTDIR_ERROR);
2979
0
                      return false;
2980
0
                    }
2981
0
                  if (!sub_separator)
2982
0
                    {
2983
0
                      *invalid_reason =
2984
0
                        xasprintf (_("In the directive number %zu, '~:[' is not followed by two clauses, separated by '~;'."), spec->directives);
2985
0
                      FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
2986
0
                               FMTDIR_ERROR);
2987
0
                      return false;
2988
0
                    }
2989
0
                  if (sub_list != NULL)
2990
0
                    union_position = sub_position;
2991
0
                  union_list = union (union_list, sub_list);
2992
0
                }
2993
2994
                /* Second alternative.  */
2995
0
                {
2996
0
                  int sub_position = position;
2997
0
                  struct format_arg_list *sub_list =
2998
0
                    (list != NULL ? copy_list (list) : NULL);
2999
0
                  if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
3000
0
                                   NULL, spec, ']', false,
3001
0
                                   NULL, invalid_reason))
3002
0
                    {
3003
0
                      FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
3004
0
                               FMTDIR_ERROR);
3005
0
                      return false;
3006
0
                    }
3007
0
                  if (sub_list != NULL)
3008
0
                    {
3009
0
                      if (union_position == -2)
3010
0
                        union_position = sub_position;
3011
0
                      else if (sub_position < 0
3012
0
                               || sub_position != union_position)
3013
0
                        union_position = -1;
3014
0
                    }
3015
0
                  union_list = union (union_list, sub_list);
3016
0
                }
3017
3018
0
                format = *formatp;
3019
0
                escape = *escapep;
3020
3021
0
                if (union_position != -2)
3022
0
                  position = union_position;
3023
0
                if (list != NULL)
3024
0
                  free_list (list);
3025
0
                list = union_list;
3026
0
              }
3027
0
            else
3028
0
              {
3029
0
                if (!check_params (&list, paramcount, params, 1, I,
3030
0
                                   spec->directives, invalid_reason))
3031
0
                  {
3032
0
                    FDI_SET (format - 1, FMTDIR_ERROR);
3033
0
                    return false;
3034
0
                  }
3035
3036
                /* If there was no first parameter, an argument is consumed.  */
3037
0
                int arg_position = -1;
3038
0
                if (!(paramcount >= 1 && params[0].type != PT_NIL))
3039
0
                  if (position >= 0)
3040
0
                    {
3041
0
                      arg_position = position;
3042
0
                      add_req_type_constraint (&list, position++, FAT_OBJECT);
3043
0
                    }
3044
3045
0
                *formatp = format;
3046
0
                *escapep = escape;
3047
3048
0
                int union_position = -2;
3049
0
                struct format_arg_list *union_list = NULL;
3050
0
                bool last_alternative = false;
3051
0
                for (;;)
3052
0
                  {
3053
                    /* Next alternative.  */
3054
0
                    int sub_position = position;
3055
0
                    struct format_arg_list *sub_list =
3056
0
                      (list != NULL ? copy_list (list) : NULL);
3057
0
                    int sub_separator = 0;
3058
0
                    if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
3059
0
                                     &sub_separator, spec, ']', !last_alternative,
3060
0
                                     NULL, invalid_reason))
3061
0
                      {
3062
0
                        FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
3063
0
                                 FMTDIR_ERROR);
3064
0
                        return false;
3065
0
                      }
3066
                    /* If this alternative is chosen, the argument arg_position
3067
                       is an integer, namely the index of this alternative.  */
3068
0
                    if (!last_alternative && arg_position >= 0)
3069
0
                      add_req_type_constraint (&sub_list, arg_position,
3070
0
                                               FAT_INTEGER);
3071
0
                    if (sub_list != NULL)
3072
0
                      {
3073
0
                        if (union_position == -2)
3074
0
                          union_position = sub_position;
3075
0
                        else if (sub_position < 0
3076
0
                                 || sub_position != union_position)
3077
0
                          union_position = -1;
3078
0
                      }
3079
0
                    union_list = union (union_list, sub_list);
3080
0
                    if (sub_separator == 2)
3081
0
                      last_alternative = true;
3082
0
                    if (!sub_separator)
3083
0
                      break;
3084
0
                  }
3085
0
                if (!last_alternative)
3086
0
                  {
3087
                    /* An implicit default alternative.  */
3088
0
                    if (union_position == -2)
3089
0
                      union_position = position;
3090
0
                    else if (position < 0 || position != union_position)
3091
0
                      union_position = -1;
3092
0
                    if (list != NULL)
3093
0
                      union_list = union (union_list, copy_list (list));
3094
0
                  }
3095
3096
0
                format = *formatp;
3097
0
                escape = *escapep;
3098
3099
0
                if (union_position != -2)
3100
0
                  position = union_position;
3101
0
                if (list != NULL)
3102
0
                  free_list (list);
3103
0
                list = union_list;
3104
0
              }
3105
0
            break;
3106
3107
0
          case ']': /* 22.3.7.3 FORMAT-CONDITIONAL-END */
3108
0
            if (terminator != ']')
3109
0
              {
3110
0
                *invalid_reason =
3111
0
                  xasprintf (_("Found '~%c' without matching '~%c'."), ']', '[');
3112
0
                FDI_SET (format - 1, FMTDIR_ERROR);
3113
0
                return false;
3114
0
              }
3115
0
            if (!check_params (&list, paramcount, params, 0, NULL,
3116
0
                               spec->directives, invalid_reason))
3117
0
              {
3118
0
                FDI_SET (format - 1, FMTDIR_ERROR);
3119
0
                return false;
3120
0
              }
3121
0
            *formatp = format;
3122
0
            *positionp = position;
3123
0
            *listp = list;
3124
0
            *escapep = escape;
3125
0
            return true;
3126
3127
0
          case '{': /* 22.3.7.4 FORMAT-ITERATION */
3128
0
            if (!check_params (&list, paramcount, params, 1, I,
3129
0
                               spec->directives, invalid_reason))
3130
0
              {
3131
0
                FDI_SET (format - 1, FMTDIR_ERROR);
3132
0
                return false;
3133
0
              }
3134
0
            *formatp = format;
3135
0
            {
3136
0
              int sub_position = 0;
3137
0
              struct format_arg_list *sub_list = make_unconstrained_list ();
3138
0
              struct format_arg_list *sub_escape = NULL;
3139
0
              struct spec sub_spec;
3140
0
              sub_spec.directives = 0;
3141
0
              sub_spec.list = sub_list;
3142
0
              if (!parse_upto (formatp, &sub_position, &sub_list, &sub_escape,
3143
0
                               NULL, &sub_spec, '}', false,
3144
0
                               NULL, invalid_reason))
3145
0
                {
3146
0
                  FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
3147
0
                           FMTDIR_ERROR);
3148
0
                  return false;
3149
0
                }
3150
0
              spec->directives += sub_spec.directives;
3151
3152
              /* If the sub-formatstring is empty, except for the terminating
3153
                 ~} directive, a formatstring argument is consumed.  */
3154
0
              if (*format == '~' && sub_spec.directives == 1)
3155
0
                if (position >= 0)
3156
0
                  add_req_type_constraint (&list, position++, FAT_FORMATSTRING);
3157
3158
0
              if (colon_p)
3159
0
                {
3160
                  /* Each iteration uses a new sublist.  */
3161
3162
                  /* ~{ catches ~^.  */
3163
0
                  sub_list = union (sub_list, sub_escape);
3164
3165
0
                  struct format_arg_list *listlist =
3166
0
                    make_repeated_list_of_lists (sub_list);
3167
3168
0
                  sub_list = listlist;
3169
0
                }
3170
0
              else
3171
0
                {
3172
                  /* Each iteration's arguments are all concatenated in a
3173
                     single list.  */
3174
3175
                  /* FIXME: This is far from correct.  Test cases:
3176
                     abc~{~^~}
3177
                     abc~{~S~^~S~}
3178
                     abc~{~D~^~C~}
3179
                     abc~{~D~^~D~}
3180
                     abc~{~D~^~S~}
3181
                     abc~{~D~^~C~}~:*~{~S~^~D~}
3182
                   */
3183
3184
                  /* ~{ catches ~^.  */
3185
0
                  sub_list = union (sub_list, sub_escape);
3186
3187
0
                  struct format_arg_list *looplist;
3188
0
                  if (sub_list == NULL)
3189
0
                    looplist = make_empty_list ();
3190
0
                  else
3191
0
                    if (sub_position < 0 || sub_position == 0)
3192
                      /* Too hard to track the possible argument types
3193
                         when the iteration is performed 2 times or more.
3194
                         So be satisfied with the constraints of executing
3195
                         the iteration 1 or 0 times.  */
3196
0
                      looplist = make_union_with_empty_list (sub_list);
3197
0
                    else
3198
0
                      looplist = make_repeated_list (sub_list, sub_position);
3199
3200
0
                  sub_list = looplist;
3201
0
                }
3202
3203
0
              if (atsign_p)
3204
0
                {
3205
                  /* All remaining arguments are used.  */
3206
0
                  if (list != NULL && position >= 0)
3207
0
                    {
3208
0
                      shift_list (sub_list, position);
3209
0
                      list = make_intersected_list (list, sub_list);
3210
0
                    }
3211
0
                  position = -1;
3212
0
                }
3213
0
              else
3214
0
                {
3215
                  /* The argument is a list.  */
3216
0
                  if (position >= 0)
3217
0
                    add_req_listtype_constraint (&list, position++,
3218
0
                                                 FAT_LIST, sub_list);
3219
0
                }
3220
0
            }
3221
0
            format = *formatp;
3222
0
            break;
3223
3224
0
          case '}': /* 22.3.7.5 FORMAT-ITERATION-END */
3225
0
            if (terminator != '}')
3226
0
              {
3227
0
                *invalid_reason =
3228
0
                  xasprintf (_("Found '~%c' without matching '~%c'."), '}', '{');
3229
0
                FDI_SET (format - 1, FMTDIR_ERROR);
3230
0
                return false;
3231
0
              }
3232
0
            if (!check_params (&list, paramcount, params, 0, NULL,
3233
0
                               spec->directives, invalid_reason))
3234
0
              {
3235
0
                FDI_SET (format - 1, FMTDIR_ERROR);
3236
0
                return false;
3237
0
              }
3238
0
            *formatp = format;
3239
0
            *positionp = position;
3240
0
            *listp = list;
3241
0
            *escapep = escape;
3242
0
            return true;
3243
3244
0
          case '<': /* 22.3.6.2, 22.3.5.2 FORMAT-JUSTIFICATION */
3245
0
            if (!check_params (&list, paramcount, params, 4, IIIC,
3246
0
                               spec->directives, invalid_reason))
3247
0
              {
3248
0
                FDI_SET (format - 1, FMTDIR_ERROR);
3249
0
                return false;
3250
0
              }
3251
0
            {
3252
0
              struct format_arg_list *sub_escape = NULL;
3253
3254
0
              *formatp = format;
3255
0
              *positionp = position;
3256
0
              *listp = list;
3257
3258
0
              for (;;)
3259
0
                {
3260
0
                  int sub_separator = 0;
3261
0
                  if (!parse_upto (formatp, positionp, listp, &sub_escape,
3262
0
                                   &sub_separator, spec, '>', true,
3263
0
                                   NULL, invalid_reason))
3264
0
                    {
3265
0
                      FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
3266
0
                               FMTDIR_ERROR);
3267
0
                      return false;
3268
0
                    }
3269
0
                  if (!sub_separator)
3270
0
                    break;
3271
0
                }
3272
3273
0
              format = *formatp;
3274
0
              position = *positionp;
3275
0
              list = *listp;
3276
3277
              /* ~< catches ~^.  */
3278
0
              if (sub_escape != NULL)
3279
0
                position = -1;
3280
0
              list = union (list, sub_escape);
3281
0
            }
3282
0
            break;
3283
3284
0
          case '>': /* 22.3.6.3 FORMAT-JUSTIFICATION-END */
3285
0
            if (terminator != '>')
3286
0
              {
3287
0
                *invalid_reason =
3288
0
                  xasprintf (_("Found '~%c' without matching '~%c'."), '>', '<');
3289
0
                FDI_SET (format - 1, FMTDIR_ERROR);
3290
0
                return false;
3291
0
              }
3292
0
            if (!check_params (&list, paramcount, params, 0, NULL,
3293
0
                               spec->directives, invalid_reason))
3294
0
              {
3295
0
                FDI_SET (format - 1, FMTDIR_ERROR);
3296
0
                return false;
3297
0
              }
3298
0
            *formatp = format;
3299
0
            *positionp = position;
3300
0
            *listp = list;
3301
0
            *escapep = escape;
3302
0
            return true;
3303
3304
0
          case '^': /* 22.3.9.2 FORMAT-UP-AND-OUT */
3305
0
            if (!check_params (&list, paramcount, params, 3, THREE,
3306
0
                               spec->directives, invalid_reason))
3307
0
              {
3308
0
                FDI_SET (format - 1, FMTDIR_ERROR);
3309
0
                return false;
3310
0
              }
3311
0
            if (position >= 0 && list != NULL && is_required (list, position))
3312
              /* This ~^ can never be executed.  Ignore it.  */
3313
0
              break;
3314
0
            if (list != NULL)
3315
0
              {
3316
0
                struct format_arg_list *this_escape = copy_list (list);
3317
0
                if (position >= 0)
3318
0
                  this_escape = add_end_constraint (this_escape, position);
3319
0
                escape = union (escape, this_escape);
3320
0
              }
3321
0
            if (position >= 0)
3322
0
              list = add_required_constraint (list, position);
3323
0
            break;
3324
3325
0
          case ';': /* 22.3.9.1 FORMAT-SEPARATOR */
3326
0
            if (!separator)
3327
0
              {
3328
0
                *invalid_reason =
3329
0
                  xasprintf (_("In the directive number %zu, '~;' is used in an invalid position."), spec->directives);
3330
0
                FDI_SET (format - 1, FMTDIR_ERROR);
3331
0
                return false;
3332
0
              }
3333
0
            if (terminator == '>')
3334
0
              {
3335
0
                if (!check_params (&list, paramcount, params, 1, I,
3336
0
                                   spec->directives, invalid_reason))
3337
0
                  {
3338
0
                    FDI_SET (format - 1, FMTDIR_ERROR);
3339
0
                    return false;
3340
0
                  }
3341
0
              }
3342
0
            else
3343
0
              {
3344
0
                if (!check_params (&list, paramcount, params, 0, NULL,
3345
0
                                   spec->directives, invalid_reason))
3346
0
                  {
3347
0
                    FDI_SET (format - 1, FMTDIR_ERROR);
3348
0
                    return false;
3349
0
                  }
3350
0
              }
3351
0
            *formatp = format;
3352
0
            *positionp = position;
3353
0
            *listp = list;
3354
0
            *escapep = escape;
3355
0
            *separatorp = (colon_p ? 2 : 1);
3356
0
            return true;
3357
3358
0
          case '!': /* FORMAT-CALL, a CLISP extension */
3359
0
            if (!nocheck_params (&list, paramcount, params,
3360
0
                                 spec->directives, invalid_reason))
3361
0
              {
3362
0
                FDI_SET (format - 1, FMTDIR_ERROR);
3363
0
                return false;
3364
0
              }
3365
0
            if (position >= 0)
3366
0
              {
3367
0
                add_req_type_constraint (&list, position++, FAT_FUNCTION);
3368
0
                add_req_type_constraint (&list, position++, FAT_OBJECT);
3369
0
              }
3370
0
            break;
3371
3372
0
          default:
3373
0
            --format;
3374
0
            if (*format == '\0')
3375
0
              {
3376
0
                *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
3377
0
                FDI_SET (format - 1, FMTDIR_ERROR);
3378
0
              }
3379
0
            else
3380
0
              {
3381
0
                *invalid_reason =
3382
0
                  INVALID_CONVERSION_SPECIFIER (spec->directives, *format);
3383
0
                FDI_SET (format, FMTDIR_ERROR);
3384
0
              }
3385
0
            return false;
3386
0
          }
3387
3388
0
        FDI_SET (format - 1, FMTDIR_END);
3389
3390
0
        free (params);
3391
0
      }
3392
3393
0
  *formatp = format;
3394
0
  *positionp = position;
3395
0
  *listp = list;
3396
0
  *escapep = escape;
3397
0
  if (terminator != '\0')
3398
0
    {
3399
0
      *invalid_reason =
3400
0
        xasprintf (_("Found '~%c' without matching '~%c'."), terminator - 1, terminator);
3401
0
      return false;
3402
0
    }
3403
0
  return true;
3404
0
}
3405
3406
3407
/* ============== Top level format string handling functions ============== */
3408
3409
static void *
3410
format_parse (const char *format, bool translated, char *fdi,
3411
              char **invalid_reason)
3412
0
{
3413
0
  struct spec spec;
3414
0
  spec.directives = 0;
3415
0
  spec.list = make_unconstrained_list ();
3416
3417
0
  int position = 0;
3418
0
  struct format_arg_list *escape = NULL;
3419
3420
0
  if (!parse_upto (&format, &position, &spec.list, &escape,
3421
0
                   NULL, &spec, '\0', false,
3422
0
                   fdi, invalid_reason))
3423
    /* Invalid format string.  */
3424
0
    return NULL;
3425
3426
  /* Catch ~^ here.  */
3427
0
  spec.list = union (spec.list, escape);
3428
3429
0
  if (spec.list == NULL)
3430
0
    {
3431
      /* Contradictory argument type information.  */
3432
0
      *invalid_reason =
3433
0
        xstrdup (_("The string refers to some argument in incompatible ways."));
3434
0
      return NULL;
3435
0
    }
3436
3437
  /* Normalize the result.  */
3438
0
  normalize_list (spec.list);
3439
3440
0
  struct spec *result = XMALLOC (struct spec);
3441
0
  *result = spec;
3442
0
  return result;
3443
0
}
3444
3445
static void
3446
format_free (void *descr)
3447
0
{
3448
0
  struct spec *spec = (struct spec *) descr;
3449
3450
0
  free_list (spec->list);
3451
0
}
3452
3453
static int
3454
format_get_number_of_directives (void *descr)
3455
0
{
3456
0
  struct spec *spec = (struct spec *) descr;
3457
3458
0
  return spec->directives;
3459
0
}
3460
3461
static bool
3462
format_check (void *msgid_descr, void *msgstr_descr, bool equality,
3463
              formatstring_error_logger_t error_logger, void *error_logger_data,
3464
              const char *pretty_msgid, const char *pretty_msgstr)
3465
0
{
3466
0
  struct spec *spec1 = (struct spec *) msgid_descr;
3467
0
  struct spec *spec2 = (struct spec *) msgstr_descr;
3468
0
  bool err = false;
3469
3470
0
  if (equality)
3471
0
    {
3472
0
      if (!equal_list (spec1->list, spec2->list))
3473
0
        {
3474
0
          if (error_logger)
3475
0
            error_logger (error_logger_data,
3476
0
                          _("format specifications in '%s' and '%s' are not equivalent"),
3477
0
                          pretty_msgid, pretty_msgstr);
3478
0
          err = true;
3479
0
        }
3480
0
    }
3481
0
  else
3482
0
    {
3483
0
      struct format_arg_list *intersection =
3484
0
        make_intersected_list (copy_list (spec1->list),
3485
0
                               copy_list (spec2->list));
3486
3487
0
      if (!(intersection != NULL
3488
0
            && (normalize_list (intersection),
3489
0
                equal_list (intersection, spec1->list))))
3490
0
        {
3491
0
          if (error_logger)
3492
0
            error_logger (error_logger_data,
3493
0
                          _("format specifications in '%s' are not a subset of those in '%s'"),
3494
0
                          pretty_msgstr, pretty_msgid);
3495
0
          err = true;
3496
0
        }
3497
0
    }
3498
3499
0
  return err;
3500
0
}
3501
3502
3503
struct formatstring_parser formatstring_lisp =
3504
{
3505
  format_parse,
3506
  format_free,
3507
  format_get_number_of_directives,
3508
  NULL,
3509
  format_check
3510
};
3511
3512
3513
/* ============================= Testing code ============================= */
3514
3515
#undef union
3516
3517
#ifdef TEST
3518
3519
/* Test program: Print the argument list specification returned by
3520
   format_parse for strings read from standard input.  */
3521
3522
#include <stdio.h>
3523
3524
static void print_list (struct format_arg_list *list);
3525
3526
static void
3527
print_element (struct format_arg *element)
3528
{
3529
  switch (element->presence)
3530
    {
3531
    case FCT_REQUIRED:
3532
      break;
3533
    case FCT_OPTIONAL:
3534
      printf (". ");
3535
      break;
3536
    default:
3537
      abort ();
3538
    }
3539
3540
  switch (element->type)
3541
    {
3542
    case FAT_OBJECT:
3543
      printf ("*");
3544
      break;
3545
    case FAT_CHARACTER_INTEGER_NULL:
3546
      printf ("ci()");
3547
      break;
3548
    case FAT_CHARACTER_NULL:
3549
      printf ("c()");
3550
      break;
3551
    case FAT_CHARACTER:
3552
      printf ("c");
3553
      break;
3554
    case FAT_INTEGER_NULL:
3555
      printf ("i()");
3556
      break;
3557
    case FAT_INTEGER:
3558
      printf ("i");
3559
      break;
3560
    case FAT_REAL:
3561
      printf ("r");
3562
      break;
3563
    case FAT_LIST:
3564
      print_list (element->list);
3565
      break;
3566
    case FAT_FORMATSTRING:
3567
      printf ("~");
3568
      break;
3569
    case FAT_FUNCTION:
3570
      printf ("f");
3571
      break;
3572
    default:
3573
      abort ();
3574
    }
3575
}
3576
3577
static void
3578
print_list (struct format_arg_list *list)
3579
{
3580
  printf ("(");
3581
3582
  for (size_t i = 0; i < list->initial.count; i++)
3583
    for (size_t j = 0; j < list->initial.element[i].repcount; j++)
3584
      {
3585
        if (i > 0 || j > 0)
3586
          printf (" ");
3587
        print_element (&list->initial.element[i]);
3588
      }
3589
3590
  if (list->repeated.count > 0)
3591
    {
3592
      printf (" |");
3593
      for (size_t i = 0; i < list->repeated.count; i++)
3594
        for (size_t j = 0; j < list->repeated.element[i].repcount; j++)
3595
          {
3596
            printf (" ");
3597
            print_element (&list->repeated.element[i]);
3598
          }
3599
    }
3600
3601
  printf (")");
3602
}
3603
3604
static void
3605
format_print (void *descr)
3606
{
3607
  struct spec *spec = (struct spec *) descr;
3608
3609
  if (spec == NULL)
3610
    {
3611
      printf ("INVALID");
3612
      return;
3613
    }
3614
3615
  print_list (spec->list);
3616
}
3617
3618
int
3619
main ()
3620
{
3621
  for (;;)
3622
    {
3623
      char *line = NULL;
3624
      size_t line_size = 0;
3625
      int line_len = getline (&line, &line_size, stdin);
3626
      if (line_len < 0)
3627
        break;
3628
      if (line_len > 0 && line[line_len - 1] == '\n')
3629
        line[--line_len] = '\0';
3630
3631
      char *invalid_reason = NULL;
3632
      void *descr = format_parse (line, false, NULL, &invalid_reason);
3633
3634
      format_print (descr);
3635
      printf ("\n");
3636
      if (descr == NULL)
3637
        printf ("%s\n", invalid_reason);
3638
3639
      free (invalid_reason);
3640
      free (line);
3641
    }
3642
3643
  return 0;
3644
}
3645
3646
/*
3647
 * For Emacs M-x compile
3648
 * Local Variables:
3649
 * 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-lisp.c ../gnulib-lib/libgettextlib.la"
3650
 * End:
3651
 */
3652
3653
#endif /* TEST */