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