1 /* Lisp format strings.
2 Copyright (C) 2001-2004 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
31 #include "format-invalid.h"
35 #define _(str) gettext (str)
38 /* Assertion macros. Could be defined to empty for speed. */
39 #define ASSERT(expr) if (!(expr)) abort ();
40 #define VERIFY_LIST(list) verify_list (list)
43 /* Lisp format strings are described in the Common Lisp HyperSpec,
44 chapter 22.3 "Formatted Output". */
46 /* Data structure describing format string derived constraints for an
47 argument list. It is a recursive list structure. Structure sharing
52 FCT_REQUIRED
, /* The format argument list cannot end before this argument. */
53 FCT_OPTIONAL
/* The format argument list may end before this argument. */
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_LIST
, /* Meant for proper lists. */
66 FAT_FORMATSTRING
, /* Format strings. */
67 FAT_FUNCTION
/* Function. */
72 unsigned int 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
77 enum format_arg_type type
; /* Possible values for this argument. */
78 struct format_arg_list
*list
; /* For FAT_LIST: List elements. */
81 struct format_arg_list
83 /* The constraints for the potentially infinite argument list are assumed
84 to become ultimately periodic. (Too complicated argument lists without
86 (format t "~@{~:[-~;~S~]~}" nil t 1 t 3 nil t 4)
87 are described by a constraint that ends in a length 1 period of
88 unconstrained arguments.) Such a periodic sequence can be split into
89 an initial segment and an endlessly repeated loop segment.
90 A finite sequence is represented entirely in the initial segment; the
91 loop segment is empty. */
95 unsigned int count
; /* Number of format_arg records used. */
96 unsigned int allocated
;
97 struct format_arg
*element
; /* Argument constraints. */
98 unsigned int length
; /* Number of arguments represented by this segment.
99 This is the sum of all repcounts in the segment. */
101 initial
, /* Initial arguments segment. */
102 repeated
; /* Endlessly repeated segment. */
107 unsigned int directives
;
108 struct format_arg_list
*list
;
112 /* Parameter for a directive. */
115 PT_NIL
, /* param not present */
116 PT_CHARACTER
, /* character */
117 PT_INTEGER
, /* integer */
118 PT_ARGCOUNT
, /* number of remaining arguments */
119 PT_V
/* variable taken from argument list */
124 enum param_type type
;
125 int value
; /* for PT_INTEGER: the value, for PT_V: the position */
129 /* Forward declaration of local functions. */
130 #define union make_union
131 static void verify_list (const struct format_arg_list
*list
);
132 static void free_list (struct format_arg_list
*list
);
133 static struct format_arg_list
* copy_list (const struct format_arg_list
*list
);
134 static bool equal_list (const struct format_arg_list
*list1
,
135 const struct format_arg_list
*list2
);
136 static struct format_arg_list
* make_intersected_list
137 (struct format_arg_list
*list1
,
138 struct format_arg_list
*list2
);
139 static struct format_arg_list
* make_intersection_with_empty_list
140 (struct format_arg_list
*list
);
141 static struct format_arg_list
* make_union_list
142 (struct format_arg_list
*list1
,
143 struct format_arg_list
*list2
);
146 /* ======================= Verify a format_arg_list ======================= */
148 /* Verify some invariants. */
150 verify_element (const struct format_arg
* e
)
152 ASSERT (e
->repcount
> 0);
153 if (e
->type
== FAT_LIST
)
154 verify_list (e
->list
);
157 /* Verify some invariants. */
158 /* Memory effects: none. */
160 verify_list (const struct format_arg_list
*list
)
163 unsigned int total_repcount
;
165 ASSERT (list
->initial
.count
<= list
->initial
.allocated
);
167 for (i
= 0; i
< list
->initial
.count
; i
++)
169 verify_element (&list
->initial
.element
[i
]);
170 total_repcount
+= list
->initial
.element
[i
].repcount
;
172 ASSERT (total_repcount
== list
->initial
.length
);
174 ASSERT (list
->repeated
.count
<= list
->repeated
.allocated
);
176 for (i
= 0; i
< list
->repeated
.count
; i
++)
178 verify_element (&list
->repeated
.element
[i
]);
179 total_repcount
+= list
->repeated
.element
[i
].repcount
;
181 ASSERT (total_repcount
== list
->repeated
.length
);
184 #define VERIFY_LIST(list) verify_list (list)
187 /* ======================== Free a format_arg_list ======================== */
189 /* Free the data belonging to an argument list element. */
191 free_element (struct format_arg
*element
)
193 if (element
->type
== FAT_LIST
)
194 free_list (element
->list
);
197 /* Free an argument list. */
198 /* Memory effects: Frees list. */
200 free_list (struct format_arg_list
*list
)
204 for (i
= 0; i
< list
->initial
.count
; i
++)
205 free_element (&list
->initial
.element
[i
]);
206 if (list
->initial
.element
!= NULL
)
207 free (list
->initial
.element
);
209 for (i
= 0; i
< list
->repeated
.count
; i
++)
210 free_element (&list
->repeated
.element
[i
]);
211 if (list
->repeated
.element
!= NULL
)
212 free (list
->repeated
.element
);
216 /* ======================== Copy a format_arg_list ======================== */
218 /* Copy the data belonging to an argument list element. */
220 copy_element (struct format_arg
*newelement
,
221 const struct format_arg
*oldelement
)
223 newelement
->repcount
= oldelement
->repcount
;
224 newelement
->presence
= oldelement
->presence
;
225 newelement
->type
= oldelement
->type
;
226 if (oldelement
->type
== FAT_LIST
)
227 newelement
->list
= copy_list (oldelement
->list
);
230 /* Copy an argument list. */
231 /* Memory effects: Freshly allocated result. */
232 static struct format_arg_list
*
233 copy_list (const struct format_arg_list
*list
)
235 struct format_arg_list
*newlist
;
242 (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
244 newlist
->initial
.count
= newlist
->initial
.allocated
= list
->initial
.count
;
246 if (list
->initial
.count
== 0)
247 newlist
->initial
.element
= NULL
;
250 newlist
->initial
.element
=
251 (struct format_arg
*)
252 xmalloc (newlist
->initial
.allocated
* sizeof (struct format_arg
));
253 for (i
= 0; i
< list
->initial
.count
; i
++)
255 copy_element (&newlist
->initial
.element
[i
],
256 &list
->initial
.element
[i
]);
257 length
+= list
->initial
.element
[i
].repcount
;
260 ASSERT (length
== list
->initial
.length
);
261 newlist
->initial
.length
= length
;
263 newlist
->repeated
.count
= newlist
->repeated
.allocated
= list
->repeated
.count
;
265 if (list
->repeated
.count
== 0)
266 newlist
->repeated
.element
= NULL
;
269 newlist
->repeated
.element
=
270 (struct format_arg
*)
271 xmalloc (newlist
->repeated
.allocated
* sizeof (struct format_arg
));
272 for (i
= 0; i
< list
->repeated
.count
; i
++)
274 copy_element (&newlist
->repeated
.element
[i
],
275 &list
->repeated
.element
[i
]);
276 length
+= list
->repeated
.element
[i
].repcount
;
279 ASSERT (length
== list
->repeated
.length
);
280 newlist
->repeated
.length
= length
;
282 VERIFY_LIST (newlist
);
288 /* ===================== Compare two format_arg_lists ===================== */
290 /* Tests whether two normalized argument constraints are equivalent,
291 ignoring the repcount. */
293 equal_element (const struct format_arg
* e1
, const struct format_arg
* e2
)
295 return (e1
->presence
== e2
->presence
296 && e1
->type
== e2
->type
297 && (e1
->type
== FAT_LIST
? equal_list (e1
->list
, e2
->list
) : true));
300 /* Tests whether two normalized argument list constraints are equivalent. */
301 /* Memory effects: none. */
303 equal_list (const struct format_arg_list
*list1
,
304 const struct format_arg_list
*list2
)
311 n
= list1
->initial
.count
;
312 if (n
!= list2
->initial
.count
)
314 for (i
= 0; i
< n
; i
++)
316 const struct format_arg
* e1
= &list1
->initial
.element
[i
];
317 const struct format_arg
* e2
= &list2
->initial
.element
[i
];
319 if (!(e1
->repcount
== e2
->repcount
&& equal_element (e1
, e2
)))
323 n
= list1
->repeated
.count
;
324 if (n
!= list2
->repeated
.count
)
326 for (i
= 0; i
< n
; i
++)
328 const struct format_arg
* e1
= &list1
->repeated
.element
[i
];
329 const struct format_arg
* e2
= &list2
->repeated
.element
[i
];
331 if (!(e1
->repcount
== e2
->repcount
&& equal_element (e1
, e2
)))
339 /* ===================== Incremental memory allocation ===================== */
341 /* Ensure list->initial.allocated >= newcount. */
343 ensure_initial_alloc (struct format_arg_list
*list
, unsigned int newcount
)
345 if (newcount
> list
->initial
.allocated
)
347 list
->initial
.allocated
=
348 MAX (2 * list
->initial
.allocated
+ 1, newcount
);
349 list
->initial
.element
=
350 (struct format_arg
*)
351 xrealloc (list
->initial
.element
,
352 list
->initial
.allocated
* sizeof (struct format_arg
));
356 /* Ensure list->initial.allocated > list->initial.count. */
358 grow_initial_alloc (struct format_arg_list
*list
)
360 if (list
->initial
.count
>= list
->initial
.allocated
)
362 list
->initial
.allocated
=
363 MAX (2 * list
->initial
.allocated
+ 1, list
->initial
.count
+ 1);
364 list
->initial
.element
=
365 (struct format_arg
*)
366 xrealloc (list
->initial
.element
,
367 list
->initial
.allocated
* sizeof (struct format_arg
));
371 /* Ensure list->repeated.allocated >= newcount. */
373 ensure_repeated_alloc (struct format_arg_list
*list
, unsigned int newcount
)
375 if (newcount
> list
->repeated
.allocated
)
377 list
->repeated
.allocated
=
378 MAX (2 * list
->repeated
.allocated
+ 1, newcount
);
379 list
->repeated
.element
=
380 (struct format_arg
*)
381 xrealloc (list
->repeated
.element
,
382 list
->repeated
.allocated
* sizeof (struct format_arg
));
386 /* Ensure list->repeated.allocated > list->repeated.count. */
388 grow_repeated_alloc (struct format_arg_list
*list
)
390 if (list
->repeated
.count
>= list
->repeated
.allocated
)
392 list
->repeated
.allocated
=
393 MAX (2 * list
->repeated
.allocated
+ 1, list
->repeated
.count
+ 1);
394 list
->repeated
.element
=
395 (struct format_arg
*)
396 xrealloc (list
->repeated
.element
,
397 list
->repeated
.allocated
* sizeof (struct format_arg
));
402 /* ====================== Normalize a format_arg_list ====================== */
404 /* Normalize an argument list constraint, assuming all sublists are already
406 /* Memory effects: Destructively modifies list. */
408 normalize_outermost_list (struct format_arg_list
*list
)
410 unsigned int n
, i
, j
;
412 /* Step 1: Combine adjacent elements.
413 Copy from i to j, keeping 0 <= j <= i. */
415 n
= list
->initial
.count
;
416 for (i
= j
= 0; i
< n
; i
++)
418 && equal_element (&list
->initial
.element
[i
],
419 &list
->initial
.element
[j
-1]))
421 list
->initial
.element
[j
-1].repcount
+=
422 list
->initial
.element
[i
].repcount
;
423 free_element (&list
->initial
.element
[i
]);
428 list
->initial
.element
[j
] = list
->initial
.element
[i
];
431 list
->initial
.count
= j
;
433 n
= list
->repeated
.count
;
434 for (i
= j
= 0; i
< n
; i
++)
436 && equal_element (&list
->repeated
.element
[i
],
437 &list
->repeated
.element
[j
-1]))
439 list
->repeated
.element
[j
-1].repcount
+=
440 list
->repeated
.element
[i
].repcount
;
441 free_element (&list
->repeated
.element
[i
]);
446 list
->repeated
.element
[j
] = list
->repeated
.element
[i
];
449 list
->repeated
.count
= j
;
451 /* Nothing more to be done if the loop segment is empty. */
452 if (list
->repeated
.count
> 0)
454 unsigned int m
, repcount0_extra
;
456 /* Step 2: Reduce the loop period. */
457 n
= list
->repeated
.count
;
460 && equal_element (&list
->repeated
.element
[0],
461 &list
->repeated
.element
[n
-1]))
463 repcount0_extra
= list
->repeated
.element
[n
-1].repcount
;
466 /* Proceed as if the loop period were n, with
467 list->repeated.element[0].repcount incremented by repcount0_extra. */
468 for (m
= 2; m
<= n
/ 2; n
++)
471 /* m is a divisor of n. Try to reduce the loop period to n. */
474 for (i
= 0; i
< n
- m
; i
++)
475 if (!((list
->repeated
.element
[i
].repcount
476 + (i
== 0 ? repcount0_extra
: 0)
477 == list
->repeated
.element
[i
+m
].repcount
)
478 && equal_element (&list
->repeated
.element
[i
],
479 &list
->repeated
.element
[i
+m
])))
486 for (i
= m
; i
< n
; i
++)
487 free_element (&list
->repeated
.element
[i
]);
488 if (n
< list
->repeated
.count
)
489 list
->repeated
.element
[m
] = list
->repeated
.element
[n
];
490 list
->repeated
.count
= list
->repeated
.count
- n
+ m
;
491 list
->repeated
.length
/= n
/ m
;
496 /* Step 3: Roll as much as possible of the initial segment's tail
498 if (list
->repeated
.count
== 1)
500 if (list
->initial
.count
> 0
501 && equal_element (&list
->initial
.element
[list
->initial
.count
-1],
502 &list
->repeated
.element
[0]))
504 /* Roll the last element of the initial segment into the loop.
505 Its repcount is irrelevant. The second-to-last element is
506 certainly different and doesn't need to be considered. */
507 list
->initial
.length
-=
508 list
->initial
.element
[list
->initial
.count
-1].repcount
;
509 list
->initial
.count
--;
514 while (list
->initial
.count
> 0
515 && equal_element (&list
->initial
.element
[list
->initial
.count
-1],
516 &list
->repeated
.element
[list
->repeated
.count
-1]))
518 unsigned int moved_repcount
=
519 MIN (list
->initial
.element
[list
->initial
.count
-1].repcount
,
520 list
->repeated
.element
[list
->repeated
.count
-1].repcount
);
522 /* Add the element at the start of list->repeated. */
523 if (equal_element (&list
->repeated
.element
[0],
524 &list
->repeated
.element
[list
->repeated
.count
-1]))
525 list
->repeated
.element
[0].repcount
+= moved_repcount
;
528 unsigned int newcount
= list
->repeated
.count
+ 1;
529 ensure_repeated_alloc (list
, newcount
);
530 for (i
= newcount
- 1; i
> 0; i
--)
531 list
->repeated
.element
[i
] = list
->repeated
.element
[i
-1];
532 list
->repeated
.count
= newcount
;
533 copy_element (&list
->repeated
.element
[0],
534 &list
->repeated
.element
[list
->repeated
.count
-1]);
535 list
->repeated
.element
[0].repcount
= moved_repcount
;
538 /* Remove the element from the end of list->repeated. */
539 list
->repeated
.element
[list
->repeated
.count
-1].repcount
-=
541 if (list
->repeated
.element
[list
->repeated
.count
-1].repcount
== 0)
543 free_element (&list
->repeated
.element
[list
->repeated
.count
-1]);
544 list
->repeated
.count
--;
547 /* Remove the element from the end of list->initial. */
548 list
->initial
.element
[list
->initial
.count
-1].repcount
-=
550 if (list
->initial
.element
[list
->initial
.count
-1].repcount
== 0)
552 free_element (&list
->initial
.element
[list
->initial
.count
-1]);
553 list
->initial
.count
--;
555 list
->initial
.length
-= moved_repcount
;
561 /* Normalize an argument list constraint. */
562 /* Memory effects: Destructively modifies list. */
564 normalize_list (struct format_arg_list
*list
)
570 /* First normalize all elements, recursively. */
571 n
= list
->initial
.count
;
572 for (i
= 0; i
< n
; i
++)
573 if (list
->initial
.element
[i
].type
== FAT_LIST
)
574 normalize_list (list
->initial
.element
[i
].list
);
575 n
= list
->repeated
.count
;
576 for (i
= 0; i
< n
; i
++)
577 if (list
->repeated
.element
[i
].type
== FAT_LIST
)
578 normalize_list (list
->repeated
.element
[i
].list
);
580 /* Then normalize the top level list. */
581 normalize_outermost_list (list
);
587 /* ===================== Unconstrained and empty lists ===================== */
589 /* It's easier to allocate these on demand, than to be careful not to
590 accidentally modify statically allocated lists. */
593 /* Create an unconstrained argument list. */
594 /* Memory effects: Freshly allocated result. */
595 static struct format_arg_list
*
596 make_unconstrained_list ()
598 struct format_arg_list
*list
;
600 list
= (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
601 list
->initial
.count
= 0;
602 list
->initial
.allocated
= 0;
603 list
->initial
.element
= NULL
;
604 list
->initial
.length
= 0;
605 list
->repeated
.count
= 1;
606 list
->repeated
.allocated
= 1;
607 list
->repeated
.element
=
608 (struct format_arg
*) xmalloc (1 * sizeof (struct format_arg
));
609 list
->repeated
.element
[0].repcount
= 1;
610 list
->repeated
.element
[0].presence
= FCT_OPTIONAL
;
611 list
->repeated
.element
[0].type
= FAT_OBJECT
;
612 list
->repeated
.length
= 1;
620 /* Create an empty argument list. */
621 /* Memory effects: Freshly allocated result. */
622 static struct format_arg_list
*
625 struct format_arg_list
*list
;
627 list
= (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
628 list
->initial
.count
= 0;
629 list
->initial
.allocated
= 0;
630 list
->initial
.element
= NULL
;
631 list
->initial
.length
= 0;
632 list
->repeated
.count
= 0;
633 list
->repeated
.allocated
= 0;
634 list
->repeated
.element
= NULL
;
635 list
->repeated
.length
= 0;
643 /* Test for an empty list. */
644 /* Memory effects: none. */
646 is_empty_list (const struct format_arg_list
*list
)
648 return (list
->initial
.count
== 0 && list
->repeated
.count
== 0);
652 /* ======================== format_arg_list surgery ======================== */
654 /* Unfold list->repeated m times, where m >= 1.
655 Assumes list->repeated.count > 0. */
656 /* Memory effects: list is destructively modified. */
658 unfold_loop (struct format_arg_list
*list
, unsigned int m
)
660 unsigned int i
, j
, k
;
664 unsigned int newcount
= list
->repeated
.count
* m
;
665 ensure_repeated_alloc (list
, newcount
);
666 i
= list
->repeated
.count
;
667 for (k
= 1; k
< m
; k
++)
668 for (j
= 0; j
< list
->repeated
.count
; j
++, i
++)
669 copy_element (&list
->repeated
.element
[i
], &list
->repeated
.element
[j
]);
670 list
->repeated
.count
= newcount
;
671 list
->repeated
.length
= list
->repeated
.length
* m
;
675 /* Ensure list->initial.length := m, where m >= list->initial.length.
676 Assumes list->repeated.count > 0. */
677 /* Memory effects: list is destructively modified. */
679 rotate_loop (struct format_arg_list
*list
, unsigned int m
)
681 if (m
== list
->initial
.length
)
684 if (list
->repeated
.count
== 1)
686 /* Instead of multiple copies of list->repeated.element[0], a single
687 copy with higher repcount is appended to list->initial. */
688 unsigned int i
, newcount
;
690 newcount
= list
->initial
.count
+ 1;
691 ensure_initial_alloc (list
, newcount
);
692 i
= list
->initial
.count
;
693 copy_element (&list
->initial
.element
[i
], &list
->repeated
.element
[0]);
694 list
->initial
.element
[i
].repcount
= m
- list
->initial
.length
;
695 list
->initial
.count
= newcount
;
696 list
->initial
.length
= m
;
700 unsigned int n
= list
->repeated
.length
;
702 /* Write m = list->initial.length + q * n + r with 0 <= r < n. */
703 unsigned int q
= (m
- list
->initial
.length
) / n
;
704 unsigned int r
= (m
- list
->initial
.length
) % n
;
706 /* Determine how many entries of list->repeated are needed for
712 s
< list
->repeated
.count
&& t
>= list
->repeated
.element
[s
].repcount
;
713 t
-= list
->repeated
.element
[s
].repcount
, s
++)
716 /* s must be < list->repeated.count, otherwise r would have been >= n. */
717 ASSERT (s
< list
->repeated
.count
);
719 /* So we need to add to list->initial:
720 q full copies of list->repeated,
721 plus the s first elements of list->repeated,
722 plus, if t > 0, a splitoff of list->repeated.element[s]. */
724 unsigned int i
, j
, k
, newcount
;
726 i
= list
->initial
.count
;
727 newcount
= i
+ q
* list
->repeated
.count
+ s
+ (t
> 0 ? 1 : 0);
728 ensure_initial_alloc (list
, newcount
);
729 for (k
= 0; k
< q
; k
++)
730 for (j
= 0; j
< list
->repeated
.count
; j
++, i
++)
731 copy_element (&list
->initial
.element
[i
],
732 &list
->repeated
.element
[j
]);
733 for (j
= 0; j
< s
; j
++, i
++)
734 copy_element (&list
->initial
.element
[i
], &list
->repeated
.element
[j
]);
737 copy_element (&list
->initial
.element
[i
],
738 &list
->repeated
.element
[j
]);
739 list
->initial
.element
[i
].repcount
= t
;
742 ASSERT (i
== newcount
);
743 list
->initial
.count
= newcount
;
744 /* The new length of the initial segment is
745 = list->initial.length
746 + q * list->repeated.length
747 + list->repeated[0..s-1].repcount + t
748 = list->initial.length + q * n + r
751 list
->initial
.length
= m
;
754 /* And rotate list->repeated. */
757 unsigned int i
, j
, oldcount
, newcount
;
758 struct format_arg
*newelement
;
760 oldcount
= list
->repeated
.count
;
761 newcount
= list
->repeated
.count
+ (t
> 0 ? 1 : 0);
763 (struct format_arg
*)
764 xmalloc (newcount
* sizeof (struct format_arg
));
766 for (j
= s
; j
< oldcount
; j
++, i
++)
767 newelement
[i
] = list
->repeated
.element
[j
];
768 for (j
= 0; j
< s
; j
++, i
++)
769 newelement
[i
] = list
->repeated
.element
[j
];
772 copy_element (&newelement
[oldcount
], &newelement
[0]);
773 newelement
[0].repcount
-= t
;
774 newelement
[oldcount
].repcount
= t
;
776 free (list
->repeated
.element
);
777 list
->repeated
.element
= newelement
;
783 /* Ensure index n in the initial segment falls on a split between elements,
784 i.e. if 0 < n < list->initial.length, then n-1 and n are covered by two
785 different adjacent elements. */
786 /* Memory effects: list is destructively modified. */
788 initial_splitelement (struct format_arg_list
*list
, unsigned int n
)
792 unsigned int oldrepcount
;
793 unsigned int newcount
;
798 if (n
> list
->initial
.length
)
800 ASSERT (list
->repeated
.count
> 0);
801 rotate_loop (list
, n
);
802 ASSERT (n
<= list
->initial
.length
);
805 /* Determine how many entries of list->initial need to be skipped. */
807 s
< list
->initial
.count
&& t
>= list
->initial
.element
[s
].repcount
;
808 t
-= list
->initial
.element
[s
].repcount
, s
++)
814 ASSERT (s
< list
->initial
.count
);
816 /* Split the entry into two entries. */
817 oldrepcount
= list
->initial
.element
[s
].repcount
;
818 newcount
= list
->initial
.count
+ 1;
819 ensure_initial_alloc (list
, newcount
);
820 for (i
= list
->initial
.count
- 1; i
> s
; i
--)
821 list
->initial
.element
[i
+1] = list
->initial
.element
[i
];
822 copy_element (&list
->initial
.element
[s
+1], &list
->initial
.element
[s
]);
823 list
->initial
.element
[s
].repcount
= t
;
824 list
->initial
.element
[s
+1].repcount
= oldrepcount
- t
;
825 list
->initial
.count
= newcount
;
833 /* Ensure index n in the initial segment is not shared. Return its index. */
834 /* Memory effects: list is destructively modified. */
836 initial_unshare (struct format_arg_list
*list
, unsigned int n
)
838 /* This does the same side effects as
839 initial_splitelement (list, n);
840 initial_splitelement (list, n + 1);
847 if (n
>= list
->initial
.length
)
849 ASSERT (list
->repeated
.count
> 0);
850 rotate_loop (list
, n
+ 1);
851 ASSERT (n
< list
->initial
.length
);
854 /* Determine how many entries of list->initial need to be skipped. */
856 s
< list
->initial
.count
&& t
>= list
->initial
.element
[s
].repcount
;
857 t
-= list
->initial
.element
[s
].repcount
, s
++)
860 /* s must be < list->initial.count. */
861 ASSERT (s
< list
->initial
.count
);
863 if (list
->initial
.element
[s
].repcount
> 1)
865 /* Split the entry into at most three entries: for indices < n,
866 for index n, and for indices > n. */
867 unsigned int oldrepcount
= list
->initial
.element
[s
].repcount
;
868 unsigned int newcount
=
869 list
->initial
.count
+ (t
== 0 || t
== oldrepcount
- 1 ? 1 : 2);
870 ensure_initial_alloc (list
, newcount
);
871 if (t
== 0 || t
== oldrepcount
- 1)
875 for (i
= list
->initial
.count
- 1; i
> s
; i
--)
876 list
->initial
.element
[i
+1] = list
->initial
.element
[i
];
877 copy_element (&list
->initial
.element
[s
+1], &list
->initial
.element
[s
]);
880 list
->initial
.element
[s
].repcount
= 1;
881 list
->initial
.element
[s
+1].repcount
= oldrepcount
- 1;
885 list
->initial
.element
[s
].repcount
= oldrepcount
- 1;
886 list
->initial
.element
[s
+1].repcount
= 1;
893 for (i
= list
->initial
.count
- 1; i
> s
; i
--)
894 list
->initial
.element
[i
+2] = list
->initial
.element
[i
];
895 copy_element (&list
->initial
.element
[s
+2], &list
->initial
.element
[s
]);
896 copy_element (&list
->initial
.element
[s
+1], &list
->initial
.element
[s
]);
897 list
->initial
.element
[s
].repcount
= t
;
898 list
->initial
.element
[s
+1].repcount
= 1;
899 list
->initial
.element
[s
+2].repcount
= oldrepcount
- 1 - t
;
901 list
->initial
.count
= newcount
;
906 /* Now the entry for index n has repcount 1. */
907 ASSERT (list
->initial
.element
[s
].repcount
== 1);
915 /* Add n unconstrained elements at the front of the list. */
916 /* Memory effects: list is destructively modified. */
918 shift_list (struct format_arg_list
*list
, unsigned int n
)
926 grow_initial_alloc (list
);
927 for (i
= list
->initial
.count
; i
> 0; i
--)
928 list
->initial
.element
[i
] = list
->initial
.element
[i
-1];
929 list
->initial
.element
[0].repcount
= n
;
930 list
->initial
.element
[0].presence
= FCT_REQUIRED
;
931 list
->initial
.element
[0].type
= FAT_OBJECT
;
932 list
->initial
.count
++;
933 list
->initial
.length
+= n
;
935 normalize_outermost_list (list
);
942 /* ================= Intersection of two format_arg_lists ================= */
944 /* Create the intersection (i.e. combined constraints) of two argument
945 constraints. Return false if the intersection is empty, i.e. if the
946 two constraints give a contradiction. */
947 /* Memory effects: Freshly allocated element's sublist. */
949 make_intersected_element (struct format_arg
*re
,
950 const struct format_arg
* e1
,
951 const struct format_arg
* e2
)
953 /* Intersect the cdr types. */
954 if (e1
->presence
== FCT_REQUIRED
|| e2
->presence
== FCT_REQUIRED
)
955 re
->presence
= FCT_REQUIRED
;
957 re
->presence
= FCT_OPTIONAL
;
959 /* Intersect the arg types. */
960 if (e1
->type
== FAT_OBJECT
)
963 if (re
->type
== FAT_LIST
)
964 re
->list
= copy_list (e2
->list
);
966 else if (e2
->type
== FAT_OBJECT
)
969 if (re
->type
== FAT_LIST
)
970 re
->list
= copy_list (e1
->list
);
972 else if (e1
->type
== FAT_LIST
973 && (e2
->type
== FAT_CHARACTER_INTEGER_NULL
974 || e2
->type
== FAT_CHARACTER_NULL
975 || e2
->type
== FAT_INTEGER_NULL
))
978 re
->list
= make_intersection_with_empty_list (e1
->list
);
979 if (re
->list
== NULL
)
982 else if (e2
->type
== FAT_LIST
983 && (e1
->type
== FAT_CHARACTER_INTEGER_NULL
984 || e1
->type
== FAT_CHARACTER_NULL
985 || e1
->type
== FAT_INTEGER_NULL
))
988 re
->list
= make_intersection_with_empty_list (e2
->list
);
989 if (re
->list
== NULL
)
992 else if (e1
->type
== FAT_CHARACTER_INTEGER_NULL
993 && (e2
->type
== FAT_CHARACTER_NULL
|| e2
->type
== FAT_CHARACTER
994 || e2
->type
== FAT_INTEGER_NULL
|| e2
->type
== FAT_INTEGER
))
998 else if (e2
->type
== FAT_CHARACTER_INTEGER_NULL
999 && (e1
->type
== FAT_CHARACTER_NULL
|| e1
->type
== FAT_CHARACTER
1000 || e1
->type
== FAT_INTEGER_NULL
|| e1
->type
== FAT_INTEGER
))
1002 re
->type
= e1
->type
;
1004 else if (e1
->type
== FAT_CHARACTER_NULL
&& e2
->type
== FAT_CHARACTER
)
1006 re
->type
= e2
->type
;
1008 else if (e2
->type
== FAT_CHARACTER_NULL
&& e1
->type
== FAT_CHARACTER
)
1010 re
->type
= e1
->type
;
1012 else if (e1
->type
== FAT_INTEGER_NULL
&& e2
->type
== FAT_INTEGER
)
1014 re
->type
= e2
->type
;
1016 else if (e2
->type
== FAT_INTEGER_NULL
&& e1
->type
== FAT_INTEGER
)
1018 re
->type
= e1
->type
;
1020 else if (e1
->type
== FAT_REAL
&& e2
->type
== FAT_INTEGER
)
1022 re
->type
= e2
->type
;
1024 else if (e2
->type
== FAT_REAL
&& e1
->type
== FAT_INTEGER
)
1026 re
->type
= e1
->type
;
1028 else if (e1
->type
== e2
->type
)
1030 re
->type
= e1
->type
;
1031 if (re
->type
== FAT_LIST
)
1033 re
->list
= make_intersected_list (copy_list (e1
->list
),
1034 copy_list (e2
->list
));
1035 if (re
->list
== NULL
)
1040 /* Each of FAT_CHARACTER, FAT_INTEGER, FAT_LIST, FAT_FORMATSTRING,
1041 FAT_FUNCTION matches only itself. Contradiction. */
1047 /* Append list->repeated to list->initial, and clear list->repeated. */
1048 /* Memory effects: list is destructively modified. */
1050 append_repeated_to_initial (struct format_arg_list
*list
)
1052 if (list
->repeated
.count
> 0)
1054 /* Move list->repeated over to list->initial. */
1055 unsigned int i
, j
, newcount
;
1057 newcount
= list
->initial
.count
+ list
->repeated
.count
;
1058 ensure_initial_alloc (list
, newcount
);
1059 i
= list
->initial
.count
;
1060 for (j
= 0; j
< list
->repeated
.count
; j
++, i
++)
1061 list
->initial
.element
[i
] = list
->repeated
.element
[j
];
1062 list
->initial
.count
= newcount
;
1063 list
->initial
.length
= list
->initial
.length
+ list
->repeated
.length
;
1064 free (list
->repeated
.element
);
1065 list
->repeated
.element
= NULL
;
1066 list
->repeated
.allocated
= 0;
1067 list
->repeated
.count
= 0;
1068 list
->repeated
.length
= 0;
1072 /* Handle a contradiction during building of a format_arg_list.
1073 The list consists only of an initial segment. The repeated segment is
1074 empty. This function searches the last FCT_OPTIONAL and cuts off the
1075 list at this point, or - if none is found - returns NULL. */
1076 /* Memory effects: list is destructively modified. If NULL is returned,
1078 static struct format_arg_list
*
1079 backtrack_in_initial (struct format_arg_list
*list
)
1081 ASSERT (list
->repeated
.count
== 0);
1083 while (list
->initial
.count
> 0)
1085 unsigned int i
= list
->initial
.count
- 1;
1086 if (list
->initial
.element
[i
].presence
== FCT_REQUIRED
)
1088 /* Throw away this element. */
1089 list
->initial
.length
-= list
->initial
.element
[i
].repcount
;
1090 free_element (&list
->initial
.element
[i
]);
1091 list
->initial
.count
= i
;
1093 else /* list->initial.element[i].presence == FCT_OPTIONAL */
1095 /* The list must end here. */
1096 list
->initial
.length
--;
1097 if (list
->initial
.element
[i
].repcount
> 1)
1098 list
->initial
.element
[i
].repcount
--;
1101 free_element (&list
->initial
.element
[i
]);
1102 list
->initial
.count
= i
;
1113 /* Create the intersection (i.e. combined constraints) of two argument list
1114 constraints. Free both argument lists when done. Return NULL if the
1115 intersection is empty, i.e. if the two constraints give a contradiction. */
1116 /* Memory effects: list1 and list2 are freed. The result, if non-NULL, is
1117 freshly allocated. */
1118 static struct format_arg_list
*
1119 make_intersected_list (struct format_arg_list
*list1
,
1120 struct format_arg_list
*list2
)
1122 struct format_arg_list
*result
;
1124 VERIFY_LIST (list1
);
1125 VERIFY_LIST (list2
);
1127 if (list1
->repeated
.length
> 0 && list2
->repeated
.length
> 0)
1128 /* Step 1: Ensure list1->repeated.length == list2->repeated.length. */
1130 unsigned int n1
= list1
->repeated
.length
;
1131 unsigned int n2
= list2
->repeated
.length
;
1132 unsigned int g
= gcd (n1
, n2
);
1133 unsigned int m1
= n2
/ g
; /* = lcm(n1,n2) / n1 */
1134 unsigned int m2
= n1
/ g
; /* = lcm(n1,n2) / n2 */
1136 unfold_loop (list1
, m1
);
1137 unfold_loop (list2
, m2
);
1138 /* Now list1->repeated.length = list2->repeated.length = lcm(n1,n2). */
1141 if (list1
->repeated
.length
> 0 || list2
->repeated
.length
> 0)
1142 /* Step 2: Ensure the initial segment of the result can be computed
1143 from the initial segments of list1 and list2. If both have a
1144 repeated segment, this means to ensure
1145 list1->initial.length == list2->initial.length. */
1147 unsigned int m
= MAX (list1
->initial
.length
, list2
->initial
.length
);
1149 if (list1
->repeated
.length
> 0)
1150 rotate_loop (list1
, m
);
1151 if (list2
->repeated
.length
> 0)
1152 rotate_loop (list2
, m
);
1155 if (list1
->repeated
.length
> 0 && list2
->repeated
.length
> 0)
1157 ASSERT (list1
->initial
.length
== list2
->initial
.length
);
1158 ASSERT (list1
->repeated
.length
== list2
->repeated
.length
);
1161 /* Step 3: Allocate the result. */
1163 (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
1164 result
->initial
.count
= 0;
1165 result
->initial
.allocated
= 0;
1166 result
->initial
.element
= NULL
;
1167 result
->initial
.length
= 0;
1168 result
->repeated
.count
= 0;
1169 result
->repeated
.allocated
= 0;
1170 result
->repeated
.element
= NULL
;
1171 result
->repeated
.length
= 0;
1173 /* Step 4: Elementwise intersection of list1->initial, list2->initial. */
1175 struct format_arg
*e1
;
1176 struct format_arg
*e2
;
1180 e1
= list1
->initial
.element
; c1
= list1
->initial
.count
;
1181 e2
= list2
->initial
.element
; c2
= list2
->initial
.count
;
1182 while (c1
> 0 && c2
> 0)
1184 struct format_arg
*re
;
1186 /* Ensure room in result->initial. */
1187 grow_initial_alloc (result
);
1188 re
= &result
->initial
.element
[result
->initial
.count
];
1189 re
->repcount
= MIN (e1
->repcount
, e2
->repcount
);
1191 /* Intersect the argument types. */
1192 if (!make_intersected_element (re
, e1
, e2
))
1194 /* If re->presence == FCT_OPTIONAL, the result list ends here. */
1195 if (re
->presence
== FCT_REQUIRED
)
1196 /* Contradiction. Backtrack. */
1197 result
= backtrack_in_initial (result
);
1201 result
->initial
.count
++;
1202 result
->initial
.length
+= re
->repcount
;
1204 e1
->repcount
-= re
->repcount
;
1205 if (e1
->repcount
== 0)
1210 e2
->repcount
-= re
->repcount
;
1211 if (e2
->repcount
== 0)
1218 if (list1
->repeated
.count
== 0 && list2
->repeated
.count
== 0)
1220 /* Intersecting two finite lists. */
1223 /* list1 longer than list2. */
1224 if (e1
->presence
== FCT_REQUIRED
)
1225 /* Contradiction. Backtrack. */
1226 result
= backtrack_in_initial (result
);
1230 /* list2 longer than list1. */
1231 if (e2
->presence
== FCT_REQUIRED
)
1232 /* Contradiction. Backtrack. */
1233 result
= backtrack_in_initial (result
);
1237 else if (list1
->repeated
.count
== 0)
1239 /* Intersecting a finite and an infinite list. */
1241 if ((c2
> 0 ? e2
->presence
: list2
->repeated
.element
[0].presence
)
1243 /* Contradiction. Backtrack. */
1244 result
= backtrack_in_initial (result
);
1247 else if (list2
->repeated
.count
== 0)
1249 /* Intersecting an infinite and a finite list. */
1251 if ((c1
> 0 ? e1
->presence
: list1
->repeated
.element
[0].presence
)
1253 /* Contradiction. Backtrack. */
1254 result
= backtrack_in_initial (result
);
1257 /* Intersecting two infinite lists. */
1258 ASSERT (c1
== 0 && c2
== 0);
1261 /* Step 5: Elementwise intersection of list1->repeated, list2->repeated. */
1263 struct format_arg
*e1
;
1264 struct format_arg
*e2
;
1268 e1
= list1
->repeated
.element
; c1
= list1
->repeated
.count
;
1269 e2
= list2
->repeated
.element
; c2
= list2
->repeated
.count
;
1270 while (c1
> 0 && c2
> 0)
1272 struct format_arg
*re
;
1274 /* Ensure room in result->repeated. */
1275 grow_repeated_alloc (result
);
1276 re
= &result
->repeated
.element
[result
->repeated
.count
];
1277 re
->repcount
= MIN (e1
->repcount
, e2
->repcount
);
1279 /* Intersect the argument types. */
1280 if (!make_intersected_element (re
, e1
, e2
))
1282 append_repeated_to_initial (result
);
1284 /* If re->presence == FCT_OPTIONAL, the result list ends here. */
1285 if (re
->presence
== FCT_REQUIRED
)
1286 /* Contradiction. Backtrack. */
1287 result
= backtrack_in_initial (result
);
1292 result
->repeated
.count
++;
1293 result
->repeated
.length
+= re
->repcount
;
1295 e1
->repcount
-= re
->repcount
;
1296 if (e1
->repcount
== 0)
1301 e2
->repcount
-= re
->repcount
;
1302 if (e2
->repcount
== 0)
1308 ASSERT (c1
== 0 && c2
== 0);
1316 /* Undo the loop unfolding and unrolling done above. */
1317 normalize_outermost_list (result
);
1318 VERIFY_LIST (result
);
1324 /* Create the intersection of an argument list and the empty list.
1325 Return NULL if the intersection is empty. */
1326 /* Memory effects: The result, if non-NULL, is freshly allocated. */
1327 static struct format_arg_list
*
1328 make_intersection_with_empty_list (struct format_arg_list
*list
)
1330 #if 0 /* equivalent but slower */
1331 return make_intersected_list (copy_list (list
), make_empty_list ());
1333 if (list
->initial
.count
> 0
1334 ? list
->initial
.element
[0].presence
== FCT_REQUIRED
1335 : list
->repeated
.count
> 0
1336 && list
->repeated
.element
[0].presence
== FCT_REQUIRED
)
1339 return make_empty_list ();
1345 /* Create the intersection of two argument list constraints. NULL stands
1346 for an impossible situation, i.e. a contradiction. */
1347 /* Memory effects: list1 and list2 are freed if non-NULL. The result,
1348 if non-NULL, is freshly allocated. */
1349 static struct format_arg_list
*
1350 intersection (struct format_arg_list
*list1
, struct format_arg_list
*list2
)
1355 return make_intersected_list (list1
, list2
);
1376 /* ===================== Union of two format_arg_lists ===================== */
1378 /* Create the union (i.e. alternative constraints) of two argument
1381 make_union_element (struct format_arg
*re
,
1382 const struct format_arg
* e1
,
1383 const struct format_arg
* e2
)
1385 /* Union of the cdr types. */
1386 if (e1
->presence
== FCT_REQUIRED
&& e2
->presence
== FCT_REQUIRED
)
1387 re
->presence
= FCT_REQUIRED
;
1388 else /* Either one of them is FCT_OPTIONAL. */
1389 re
->presence
= FCT_OPTIONAL
;
1391 /* Union of the arg types. */
1392 if (e1
->type
== e2
->type
)
1394 re
->type
= e1
->type
;
1395 if (re
->type
== FAT_LIST
)
1396 re
->list
= make_union_list (copy_list (e1
->list
),
1397 copy_list (e2
->list
));
1399 else if (e1
->type
== FAT_CHARACTER_INTEGER_NULL
1400 && (e2
->type
== FAT_CHARACTER_NULL
|| e2
->type
== FAT_CHARACTER
1401 || e2
->type
== FAT_INTEGER_NULL
|| e2
->type
== FAT_INTEGER
))
1403 re
->type
= e1
->type
;
1405 else if (e2
->type
== FAT_CHARACTER_INTEGER_NULL
1406 && (e1
->type
== FAT_CHARACTER_NULL
|| e1
->type
== FAT_CHARACTER
1407 || e1
->type
== FAT_INTEGER_NULL
|| e1
->type
== FAT_INTEGER
))
1409 re
->type
= e2
->type
;
1411 else if (e1
->type
== FAT_CHARACTER_NULL
&& e2
->type
== FAT_CHARACTER
)
1413 re
->type
= e1
->type
;
1415 else if (e2
->type
== FAT_CHARACTER_NULL
&& e1
->type
== FAT_CHARACTER
)
1417 re
->type
= e2
->type
;
1419 else if (e1
->type
== FAT_INTEGER_NULL
&& e2
->type
== FAT_INTEGER
)
1421 re
->type
= e1
->type
;
1423 else if (e2
->type
== FAT_INTEGER_NULL
&& e1
->type
== FAT_INTEGER
)
1425 re
->type
= e2
->type
;
1427 else if (e1
->type
== FAT_REAL
&& e2
->type
== FAT_INTEGER
)
1429 re
->type
= e1
->type
;
1431 else if (e2
->type
== FAT_REAL
&& e1
->type
== FAT_INTEGER
)
1433 re
->type
= e2
->type
;
1435 else if (e1
->type
== FAT_LIST
&& is_empty_list (e1
->list
))
1437 if (e2
->type
== FAT_CHARACTER_INTEGER_NULL
1438 || e2
->type
== FAT_CHARACTER_NULL
1439 || e2
->type
== FAT_INTEGER_NULL
)
1440 re
->type
= e2
->type
;
1441 else if (e2
->type
== FAT_CHARACTER
)
1442 re
->type
= FAT_CHARACTER_NULL
;
1443 else if (e2
->type
== FAT_INTEGER
)
1444 re
->type
= FAT_INTEGER_NULL
;
1446 re
->type
= FAT_OBJECT
;
1448 else if (e2
->type
== FAT_LIST
&& is_empty_list (e2
->list
))
1450 if (e1
->type
== FAT_CHARACTER_INTEGER_NULL
1451 || e1
->type
== FAT_CHARACTER_NULL
1452 || e1
->type
== FAT_INTEGER_NULL
)
1453 re
->type
= e1
->type
;
1454 else if (e1
->type
== FAT_CHARACTER
)
1455 re
->type
= FAT_CHARACTER_NULL
;
1456 else if (e1
->type
== FAT_INTEGER
)
1457 re
->type
= FAT_INTEGER_NULL
;
1459 re
->type
= FAT_OBJECT
;
1461 else if ((e1
->type
== FAT_CHARACTER
|| e1
->type
== FAT_CHARACTER_NULL
)
1462 && (e2
->type
== FAT_INTEGER
|| e2
->type
== FAT_INTEGER_NULL
))
1464 re
->type
= FAT_CHARACTER_INTEGER_NULL
;
1466 else if ((e2
->type
== FAT_CHARACTER
|| e2
->type
== FAT_CHARACTER_NULL
)
1467 && (e1
->type
== FAT_INTEGER
|| e1
->type
== FAT_INTEGER_NULL
))
1469 re
->type
= FAT_CHARACTER_INTEGER_NULL
;
1473 /* Other union types are too hard to describe precisely. */
1474 re
->type
= FAT_OBJECT
;
1478 /* Create the union (i.e. alternative constraints) of two argument list
1479 constraints. Free both argument lists when done. */
1480 /* Memory effects: list1 and list2 are freed. The result is freshly
1482 static struct format_arg_list
*
1483 make_union_list (struct format_arg_list
*list1
, struct format_arg_list
*list2
)
1485 struct format_arg_list
*result
;
1487 VERIFY_LIST (list1
);
1488 VERIFY_LIST (list2
);
1490 if (list1
->repeated
.length
> 0 && list2
->repeated
.length
> 0)
1492 /* Step 1: Ensure list1->repeated.length == list2->repeated.length. */
1494 unsigned int n1
= list1
->repeated
.length
;
1495 unsigned int n2
= list2
->repeated
.length
;
1496 unsigned int g
= gcd (n1
, n2
);
1497 unsigned int m1
= n2
/ g
; /* = lcm(n1,n2) / n1 */
1498 unsigned int m2
= n1
/ g
; /* = lcm(n1,n2) / n2 */
1500 unfold_loop (list1
, m1
);
1501 unfold_loop (list2
, m2
);
1502 /* Now list1->repeated.length = list2->repeated.length = lcm(n1,n2). */
1505 /* Step 2: Ensure that list1->initial.length == list2->initial.length. */
1507 unsigned int m
= MAX (list1
->initial
.length
, list2
->initial
.length
);
1509 rotate_loop (list1
, m
);
1510 rotate_loop (list2
, m
);
1513 ASSERT (list1
->initial
.length
== list2
->initial
.length
);
1514 ASSERT (list1
->repeated
.length
== list2
->repeated
.length
);
1516 else if (list1
->repeated
.length
> 0)
1518 /* Ensure the initial segment of the result can be computed from the
1519 initial segment of list1. */
1520 if (list2
->initial
.length
>= list1
->initial
.length
)
1522 rotate_loop (list1
, list2
->initial
.length
);
1523 if (list1
->repeated
.element
[0].presence
== FCT_REQUIRED
)
1524 rotate_loop (list1
, list1
->initial
.length
+ 1);
1527 else if (list2
->repeated
.length
> 0)
1529 /* Ensure the initial segment of the result can be computed from the
1530 initial segment of list2. */
1531 if (list1
->initial
.length
>= list2
->initial
.length
)
1533 rotate_loop (list2
, list1
->initial
.length
);
1534 if (list2
->repeated
.element
[0].presence
== FCT_REQUIRED
)
1535 rotate_loop (list2
, list2
->initial
.length
+ 1);
1539 /* Step 3: Allocate the result. */
1541 (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
1542 result
->initial
.count
= 0;
1543 result
->initial
.allocated
= 0;
1544 result
->initial
.element
= NULL
;
1545 result
->initial
.length
= 0;
1546 result
->repeated
.count
= 0;
1547 result
->repeated
.allocated
= 0;
1548 result
->repeated
.element
= NULL
;
1549 result
->repeated
.length
= 0;
1551 /* Step 4: Elementwise union of list1->initial, list2->initial. */
1553 struct format_arg
*e1
;
1554 struct format_arg
*e2
;
1558 e1
= list1
->initial
.element
; c1
= list1
->initial
.count
;
1559 e2
= list2
->initial
.element
; c2
= list2
->initial
.count
;
1560 while (c1
> 0 && c2
> 0)
1562 struct format_arg
*re
;
1564 /* Ensure room in result->initial. */
1565 grow_initial_alloc (result
);
1566 re
= &result
->initial
.element
[result
->initial
.count
];
1567 re
->repcount
= MIN (e1
->repcount
, e2
->repcount
);
1569 /* Union of the argument types. */
1570 make_union_element (re
, e1
, e2
);
1572 result
->initial
.count
++;
1573 result
->initial
.length
+= re
->repcount
;
1575 e1
->repcount
-= re
->repcount
;
1576 if (e1
->repcount
== 0)
1581 e2
->repcount
-= re
->repcount
;
1582 if (e2
->repcount
== 0)
1591 /* list2 already terminated, but still more elements in list1->initial.
1592 Copy them all, but turn the first presence to FCT_OPTIONAL. */
1593 ASSERT (list2
->repeated
.count
== 0);
1595 if (e1
->presence
== FCT_REQUIRED
)
1597 struct format_arg
*re
;
1599 /* Ensure room in result->initial. */
1600 grow_initial_alloc (result
);
1601 re
= &result
->initial
.element
[result
->initial
.count
];
1602 copy_element (re
, e1
);
1603 re
->presence
= FCT_OPTIONAL
;
1605 result
->initial
.count
++;
1606 result
->initial
.length
+= 1;
1608 if (e1
->repcount
== 0)
1615 /* Ensure room in result->initial. */
1616 ensure_initial_alloc (result
, result
->initial
.count
+ c1
);
1619 struct format_arg
*re
;
1621 re
= &result
->initial
.element
[result
->initial
.count
];
1622 copy_element (re
, e1
);
1623 result
->initial
.count
++;
1624 result
->initial
.length
+= re
->repcount
;
1631 /* list1 already terminated, but still more elements in list2->initial.
1632 Copy them all, but turn the first presence to FCT_OPTIONAL. */
1633 ASSERT (list1
->repeated
.count
== 0);
1635 if (e2
->presence
== FCT_REQUIRED
)
1637 struct format_arg
*re
;
1639 /* Ensure room in result->initial. */
1640 grow_initial_alloc (result
);
1641 re
= &result
->initial
.element
[result
->initial
.count
];
1642 copy_element (re
, e2
);
1643 re
->presence
= FCT_OPTIONAL
;
1645 result
->initial
.count
++;
1646 result
->initial
.length
+= 1;
1648 if (e2
->repcount
== 0)
1655 /* Ensure room in result->initial. */
1656 ensure_initial_alloc (result
, result
->initial
.count
+ c2
);
1659 struct format_arg
*re
;
1661 re
= &result
->initial
.element
[result
->initial
.count
];
1662 copy_element (re
, e2
);
1663 result
->initial
.count
++;
1664 result
->initial
.length
+= re
->repcount
;
1669 ASSERT (c1
== 0 && c2
== 0);
1672 if (list1
->repeated
.length
> 0 && list2
->repeated
.length
> 0)
1673 /* Step 5: Elementwise union of list1->repeated, list2->repeated. */
1675 struct format_arg
*e1
;
1676 struct format_arg
*e2
;
1680 e1
= list1
->repeated
.element
; c1
= list1
->repeated
.count
;
1681 e2
= list2
->repeated
.element
; c2
= list2
->repeated
.count
;
1682 while (c1
> 0 && c2
> 0)
1684 struct format_arg
*re
;
1686 /* Ensure room in result->repeated. */
1687 grow_repeated_alloc (result
);
1688 re
= &result
->repeated
.element
[result
->repeated
.count
];
1689 re
->repcount
= MIN (e1
->repcount
, e2
->repcount
);
1691 /* Union of the argument types. */
1692 make_union_element (re
, e1
, e2
);
1694 result
->repeated
.count
++;
1695 result
->repeated
.length
+= re
->repcount
;
1697 e1
->repcount
-= re
->repcount
;
1698 if (e1
->repcount
== 0)
1703 e2
->repcount
-= re
->repcount
;
1704 if (e2
->repcount
== 0)
1710 ASSERT (c1
== 0 && c2
== 0);
1712 else if (list1
->repeated
.length
> 0)
1714 /* Turning FCT_REQUIRED into FCT_OPTIONAL was already handled in the
1715 initial segment. Just copy the repeated segment of list1. */
1718 result
->repeated
.count
= list1
->repeated
.count
;
1719 result
->repeated
.allocated
= result
->repeated
.count
;
1720 result
->repeated
.element
=
1721 (struct format_arg
*)
1722 xmalloc (result
->repeated
.allocated
* sizeof (struct format_arg
));
1723 for (i
= 0; i
< list1
->repeated
.count
; i
++)
1724 copy_element (&result
->repeated
.element
[i
],
1725 &list1
->repeated
.element
[i
]);
1726 result
->repeated
.length
= list1
->repeated
.length
;
1728 else if (list2
->repeated
.length
> 0)
1730 /* Turning FCT_REQUIRED into FCT_OPTIONAL was already handled in the
1731 initial segment. Just copy the repeated segment of list2. */
1734 result
->repeated
.count
= list2
->repeated
.count
;
1735 result
->repeated
.allocated
= result
->repeated
.count
;
1736 result
->repeated
.element
=
1737 (struct format_arg
*)
1738 xmalloc (result
->repeated
.allocated
* sizeof (struct format_arg
));
1739 for (i
= 0; i
< list2
->repeated
.count
; i
++)
1740 copy_element (&result
->repeated
.element
[i
],
1741 &list2
->repeated
.element
[i
]);
1742 result
->repeated
.length
= list2
->repeated
.length
;
1747 /* Undo the loop unfolding and unrolling done above. */
1748 normalize_outermost_list (result
);
1749 VERIFY_LIST (result
);
1754 /* Create the union of an argument list and the empty list. */
1755 /* Memory effects: list is freed. The result is freshly allocated. */
1756 static struct format_arg_list
*
1757 make_union_with_empty_list (struct format_arg_list
*list
)
1759 #if 0 /* equivalent but slower */
1760 return make_union_list (list
, make_empty_list ());
1764 if (list
->initial
.count
> 0
1765 ? list
->initial
.element
[0].presence
== FCT_REQUIRED
1766 : list
->repeated
.count
> 0
1767 && list
->repeated
.element
[0].presence
== FCT_REQUIRED
)
1769 initial_splitelement (list
, 1);
1770 ASSERT (list
->initial
.count
> 0);
1771 ASSERT (list
->initial
.element
[0].repcount
== 1);
1772 ASSERT (list
->initial
.element
[0].presence
== FCT_REQUIRED
);
1773 list
->initial
.element
[0].presence
= FCT_OPTIONAL
;
1775 /* We might need to merge list->initial.element[0] and
1776 list->initial.element[1]. */
1777 normalize_outermost_list (list
);
1787 /* Create the union of two argument list constraints. NULL stands for an
1788 impossible situation, i.e. a contradiction. */
1789 /* Memory effects: list1 and list2 are freed if non-NULL. The result,
1790 if non-NULL, is freshly allocated. */
1791 static struct format_arg_list
*
1792 union (struct format_arg_list
*list1
, struct format_arg_list
*list2
)
1797 return make_union_list (list1
, list2
);
1811 /* =========== Adding specific constraints to a format_arg_list =========== */
1814 /* Test whether arguments 0..n are required arguments in a list. */
1816 is_required (const struct format_arg_list
*list
, unsigned int n
)
1821 /* We'll check whether the first n+1 presence flags are FCT_REQUIRED. */
1824 /* Walk the list->initial segment. */
1826 s
< list
->initial
.count
&& t
>= list
->initial
.element
[s
].repcount
;
1827 t
-= list
->initial
.element
[s
].repcount
, s
++)
1828 if (list
->initial
.element
[s
].presence
!= FCT_REQUIRED
)
1834 if (s
< list
->initial
.count
)
1836 if (list
->initial
.element
[s
].presence
!= FCT_REQUIRED
)
1842 /* Walk the list->repeated segment. */
1843 if (list
->repeated
.count
== 0)
1847 s
< list
->repeated
.count
&& t
>= list
->repeated
.element
[s
].repcount
;
1848 t
-= list
->repeated
.element
[s
].repcount
, s
++)
1849 if (list
->repeated
.element
[s
].presence
!= FCT_REQUIRED
)
1855 if (s
< list
->repeated
.count
)
1857 if (list
->repeated
.element
[s
].presence
!= FCT_REQUIRED
)
1863 /* The list->repeated segment consists only of FCT_REQUIRED. So,
1864 regardless how many more passes through list->repeated would be
1865 needed until t becomes 0, the result is true. */
1870 /* Add a constraint to an argument list, namely that the arguments 0...n are
1871 present. NULL stands for an impossible situation, i.e. a contradiction. */
1872 /* Memory effects: list is freed. The result is freshly allocated. */
1873 static struct format_arg_list
*
1874 add_required_constraint (struct format_arg_list
*list
, unsigned int n
)
1876 unsigned int i
, rest
;
1883 if (list
->repeated
.count
== 0 && list
->initial
.length
<= n
)
1885 /* list is already constrained to have at most length n.
1891 initial_splitelement (list
, n
+ 1);
1893 for (i
= 0, rest
= n
+ 1; rest
> 0; )
1895 list
->initial
.element
[i
].presence
= FCT_REQUIRED
;
1896 rest
-= list
->initial
.element
[i
].repcount
;
1906 /* Add a constraint to an argument list, namely that the argument n is
1907 never present. NULL stands for an impossible situation, i.e. a
1909 /* Memory effects: list is freed. The result is freshly allocated. */
1910 static struct format_arg_list
*
1911 add_end_constraint (struct format_arg_list
*list
, unsigned int n
)
1914 enum format_cdr_type n_presence
;
1921 if (list
->repeated
.count
== 0 && list
->initial
.length
<= n
)
1922 /* list is already constrained to have at most length n. */
1925 s
= initial_splitelement (list
, n
);
1927 (s
< list
->initial
.count
1928 ? /* n < list->initial.length */ list
->initial
.element
[s
].presence
1929 : /* n >= list->initial.length */ list
->repeated
.element
[0].presence
);
1931 for (i
= s
; i
< list
->initial
.count
; i
++)
1933 list
->initial
.length
-= list
->initial
.element
[i
].repcount
;
1934 free_element (&list
->initial
.element
[i
]);
1936 list
->initial
.count
= s
;
1938 for (i
= 0; i
< list
->repeated
.count
; i
++)
1939 free_element (&list
->repeated
.element
[i
]);
1940 if (list
->repeated
.element
!= NULL
)
1941 free (list
->repeated
.element
);
1942 list
->repeated
.element
= NULL
;
1943 list
->repeated
.allocated
= 0;
1944 list
->repeated
.count
= 0;
1945 list
->repeated
.length
= 0;
1947 if (n_presence
== FCT_REQUIRED
)
1948 return backtrack_in_initial (list
);
1954 /* Add a constraint to an argument list, namely that the argument n is
1955 of a given type. NULL stands for an impossible situation, i.e. a
1956 contradiction. Assumes a preceding add_required_constraint (list, n). */
1957 /* Memory effects: list is freed. The result is freshly allocated. */
1958 static struct format_arg_list
*
1959 add_type_constraint (struct format_arg_list
*list
, unsigned int n
,
1960 enum format_arg_type type
)
1963 struct format_arg newconstraint
;
1964 struct format_arg tmpelement
;
1969 /* Through the previous add_required_constraint, we can assume
1970 list->initial.length >= n+1. */
1972 s
= initial_unshare (list
, n
);
1974 newconstraint
.presence
= FCT_OPTIONAL
;
1975 newconstraint
.type
= type
;
1976 if (!make_intersected_element (&tmpelement
,
1977 &list
->initial
.element
[s
], &newconstraint
))
1978 return add_end_constraint (list
, n
);
1979 free_element (&list
->initial
.element
[s
]);
1980 list
->initial
.element
[s
].type
= tmpelement
.type
;
1981 list
->initial
.element
[s
].list
= tmpelement
.list
;
1989 /* Add a constraint to an argument list, namely that the argument n is
1990 of a given list type. NULL stands for an impossible situation, i.e. a
1991 contradiction. Assumes a preceding add_required_constraint (list, n). */
1992 /* Memory effects: list is freed. The result is freshly allocated. */
1993 static struct format_arg_list
*
1994 add_listtype_constraint (struct format_arg_list
*list
, unsigned int n
,
1995 enum format_arg_type type
,
1996 struct format_arg_list
*sublist
)
1999 struct format_arg newconstraint
;
2000 struct format_arg tmpelement
;
2005 /* Through the previous add_required_constraint, we can assume
2006 list->initial.length >= n+1. */
2008 s
= initial_unshare (list
, n
);
2010 newconstraint
.presence
= FCT_OPTIONAL
;
2011 newconstraint
.type
= type
;
2012 newconstraint
.list
= sublist
;
2013 if (!make_intersected_element (&tmpelement
,
2014 &list
->initial
.element
[s
], &newconstraint
))
2015 return add_end_constraint (list
, n
);
2016 free_element (&list
->initial
.element
[s
]);
2017 list
->initial
.element
[s
].type
= tmpelement
.type
;
2018 list
->initial
.element
[s
].list
= tmpelement
.list
;
2026 /* ============= Subroutines used by the format string parser ============= */
2029 add_req_type_constraint (struct format_arg_list
**listp
,
2030 unsigned int position
, enum format_arg_type type
)
2032 *listp
= add_required_constraint (*listp
, position
);
2033 *listp
= add_type_constraint (*listp
, position
, type
);
2038 add_req_listtype_constraint (struct format_arg_list
**listp
,
2039 unsigned int position
, enum format_arg_type type
,
2040 struct format_arg_list
*sublist
)
2042 *listp
= add_required_constraint (*listp
, position
);
2043 *listp
= add_listtype_constraint (*listp
, position
, type
, sublist
);
2047 /* Create an endless repeated list whose elements are lists constrained
2049 /* Memory effects: sublist is freed. The result is freshly allocated. */
2050 static struct format_arg_list
*
2051 make_repeated_list_of_lists (struct format_arg_list
*sublist
)
2053 if (sublist
== NULL
)
2054 /* The list cannot have a single element. */
2055 return make_empty_list ();
2058 struct format_arg_list
*listlist
;
2061 (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
2063 listlist
->initial
.count
= 0;
2064 listlist
->initial
.allocated
= 0;
2065 listlist
->initial
.element
= NULL
;
2066 listlist
->initial
.length
= 0;
2067 listlist
->repeated
.count
= 1;
2068 listlist
->repeated
.allocated
= 1;
2069 listlist
->repeated
.element
=
2070 (struct format_arg
*) xmalloc (1 * sizeof (struct format_arg
));
2071 listlist
->repeated
.element
[0].repcount
= 1;
2072 listlist
->repeated
.element
[0].presence
= FCT_OPTIONAL
;
2073 listlist
->repeated
.element
[0].type
= FAT_LIST
;
2074 listlist
->repeated
.element
[0].list
= sublist
;
2075 listlist
->repeated
.length
= 1;
2077 VERIFY_LIST (listlist
);
2084 /* Create an endless repeated list which represents the union of a finite
2085 number of copies of L, each time shifted by period:
2089 L and (*^period L) and (*^{2 period} L)
2090 L and (*^period L) and (*^{2 period} L) and (*^{3 period} L)
2093 /* Memory effects: sublist is freed. The result is freshly allocated. */
2094 static struct format_arg_list
*
2095 make_repeated_list (struct format_arg_list
*sublist
, unsigned int period
)
2098 struct segment
*srcseg
;
2099 struct format_arg_list
*list
;
2100 unsigned int p
, n
, i
, si
, ti
, j
, sj
, tj
, splitindex
, newcount
;
2103 VERIFY_LIST (sublist
);
2105 ASSERT (period
> 0);
2107 if (sublist
->repeated
.count
== 0)
2109 /* L is a finite list. */
2111 if (sublist
->initial
.length
< period
)
2112 /* L and (*^period L) is a contradition, so we need to consider
2113 only 1 and 0 iterations. */
2114 return make_union_with_empty_list (sublist
);
2116 srcseg
= &sublist
->initial
;
2121 /* L is an infinite list. */
2122 /* p := lcm (period, period of L) */
2123 unsigned int Lp
= sublist
->repeated
.length
;
2124 unsigned int m
= period
/ gcd (period
, Lp
); /* = lcm(period,Lp) / Lp */
2126 unfold_loop (sublist
, m
);
2129 /* Concatenate the initial and the repeated segments into a single
2131 tmp
.count
= sublist
->initial
.count
+ sublist
->repeated
.count
;
2132 tmp
.allocated
= tmp
.count
;
2134 (struct format_arg
*)
2135 xmalloc (tmp
.allocated
* sizeof (struct format_arg
));
2136 for (i
= 0; i
< sublist
->initial
.count
; i
++)
2137 tmp
.element
[i
] = sublist
->initial
.element
[i
];
2138 for (j
= 0; j
< sublist
->repeated
.count
; i
++, j
++)
2139 tmp
.element
[i
] = sublist
->initial
.element
[j
];
2140 tmp
.length
= sublist
->initial
.length
+ sublist
->repeated
.length
;
2147 /* Example: n = 7, p = 2
2148 Let L = (A B C D E F G).
2151 L & L<<p = A B C&A D&B E&C F&D G&E
2152 L & L<<p & L<<2p = A B C&A D&B E&C&A F&D&B G&E&C
2153 ... = A B C&A D&B E&C&A F&D&B G&E&C&A
2155 Thus the result has an initial segment of length n - p and a period
2156 of p, and can be computed by floor(n/p) intersection operations.
2157 Or by a single incremental intersection operation, going from left
2160 list
= (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
2161 list
->initial
.count
= 0;
2162 list
->initial
.allocated
= 0;
2163 list
->initial
.element
= NULL
;
2164 list
->initial
.length
= 0;
2165 list
->repeated
.count
= 0;
2166 list
->repeated
.allocated
= 0;
2167 list
->repeated
.element
= NULL
;
2168 list
->repeated
.length
= 0;
2171 for (i = 0; i < p; i++)
2172 list->initial.element[i] = srcseg->element[i];
2173 list->initial.element[0].presence = FCT_OPTIONAL; // union with empty list
2174 for (i = p, j = 0; i < n; i++, j++)
2175 list->initial.element[i] = srcseg->element[i] & list->initial.element[j];
2180 i
= 0, ti
= 0, si
= 0;
2183 unsigned int k
= MIN (srcseg
->element
[si
].repcount
- ti
, p
- i
);
2185 /* Ensure room in list->initial. */
2186 grow_initial_alloc (list
);
2187 copy_element (&list
->initial
.element
[list
->initial
.count
],
2188 &srcseg
->element
[si
]);
2189 list
->initial
.element
[list
->initial
.count
].repcount
= k
;
2190 list
->initial
.count
++;
2191 list
->initial
.length
+= k
;
2195 if (ti
== srcseg
->element
[si
].repcount
)
2202 ASSERT (list
->initial
.count
> 0);
2203 if (list
->initial
.element
[0].presence
== FCT_REQUIRED
)
2205 initial_splitelement (list
, 1);
2206 ASSERT (list
->initial
.element
[0].presence
== FCT_REQUIRED
);
2207 ASSERT (list
->initial
.element
[0].repcount
== 1);
2208 list
->initial
.element
[0].presence
= FCT_OPTIONAL
;
2211 j
= 0, tj
= 0, sj
= 0;
2215 MIN (srcseg
->element
[si
].repcount
- ti
,
2216 list
->initial
.element
[sj
].repcount
- tj
);
2218 /* Ensure room in list->initial. */
2219 grow_initial_alloc (list
);
2220 if (!make_intersected_element (&list
->initial
.element
[list
->initial
.count
],
2221 &srcseg
->element
[si
],
2222 &list
->initial
.element
[sj
]))
2224 if (list
->initial
.element
[list
->initial
.count
].presence
== FCT_REQUIRED
)
2226 /* Contradiction. Backtrack. */
2227 list
= backtrack_in_initial (list
);
2228 ASSERT (list
!= NULL
); /* at least the empty list is valid */
2233 /* The list ends here. */
2238 list
->initial
.element
[list
->initial
.count
].repcount
= k
;
2239 list
->initial
.count
++;
2240 list
->initial
.length
+= k
;
2244 if (ti
== srcseg
->element
[si
].repcount
)
2252 if (tj
== list
->initial
.element
[sj
].repcount
)
2259 ASSERT (list
->initial
.length
== n
);
2261 /* Add optional exit points at 0, period, 2*period etc.
2262 FIXME: Not sure this is correct in all cases. */
2263 for (i
= 0; i
< list
->initial
.length
; i
+= period
)
2265 si
= initial_unshare (list
, i
);
2266 list
->initial
.element
[si
].presence
= FCT_OPTIONAL
;
2271 /* Now split off the repeated part. */
2272 splitindex
= initial_splitelement (list
, n
- p
);
2273 newcount
= list
->initial
.count
- splitindex
;
2274 if (newcount
> list
->repeated
.allocated
)
2276 list
->repeated
.allocated
= newcount
;
2277 list
->repeated
.element
=
2278 (struct format_arg
*) xmalloc (newcount
* sizeof (struct format_arg
));
2280 for (i
= splitindex
, j
= 0; i
< n
; i
++, j
++)
2281 list
->repeated
.element
[j
] = list
->initial
.element
[i
];
2282 list
->repeated
.count
= newcount
;
2283 list
->repeated
.length
= p
;
2284 list
->initial
.count
= splitindex
;
2285 list
->initial
.length
= n
- p
;
2294 /* ================= Handling of format string directives ================= */
2296 /* Possible signatures of format directives. */
2297 static const enum format_arg_type I
[1] = { FAT_INTEGER_NULL
};
2298 static const enum format_arg_type II
[2] = {
2299 FAT_INTEGER_NULL
, FAT_INTEGER_NULL
2301 static const enum format_arg_type ICCI
[4] = {
2302 FAT_INTEGER_NULL
, FAT_CHARACTER_NULL
, FAT_CHARACTER_NULL
, FAT_INTEGER_NULL
2304 static const enum format_arg_type IIIC
[4] = {
2305 FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_CHARACTER_NULL
2307 static const enum format_arg_type IICCI
[5] = {
2308 FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_CHARACTER_NULL
, FAT_CHARACTER_NULL
,
2311 static const enum format_arg_type IIICC
[5] = {
2312 FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_CHARACTER_NULL
,
2315 static const enum format_arg_type IIIICCC
[7] = {
2316 FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_INTEGER_NULL
,
2317 FAT_CHARACTER_NULL
, FAT_CHARACTER_NULL
, FAT_CHARACTER_NULL
2319 static const enum format_arg_type THREE
[3] = {
2320 FAT_CHARACTER_INTEGER_NULL
, FAT_CHARACTER_INTEGER_NULL
,
2321 FAT_CHARACTER_INTEGER_NULL
2325 /* Check the parameters. For V params, add the constraint to the argument
2326 list. Return false and fill in *invalid_reason if the format string is
2329 check_params (struct format_arg_list
**listp
,
2330 unsigned int paramcount
, struct param
*params
,
2331 unsigned int t_count
, const enum format_arg_type
*t_types
,
2332 unsigned int directives
, char **invalid_reason
)
2334 unsigned int orig_paramcount
= paramcount
;
2335 unsigned int orig_t_count
= t_count
;
2337 for (; paramcount
> 0 && t_count
> 0;
2338 params
++, paramcount
--, t_types
++, t_count
--)
2342 case FAT_CHARACTER_INTEGER_NULL
:
2344 case FAT_CHARACTER_NULL
:
2345 switch (params
->type
)
2347 case PT_NIL
: case PT_CHARACTER
: case PT_V
:
2349 case PT_INTEGER
: case PT_ARGCOUNT
:
2350 /* wrong param type */
2352 xasprintf (_("In the directive number %u, parameter %u is of type '%s' but a parameter of type '%s' is expected."), directives
, orig_paramcount
- paramcount
+ 1, "integer", "character");
2356 case FAT_INTEGER_NULL
:
2357 switch (params
->type
)
2359 case PT_NIL
: case PT_INTEGER
: case PT_ARGCOUNT
: case PT_V
:
2362 /* wrong param type */
2364 xasprintf (_("In the directive number %u, parameter %u is of type '%s' but a parameter of type '%s' is expected."), directives
, orig_paramcount
- paramcount
+ 1, "character", "integer");
2371 if (params
->type
== PT_V
)
2373 int position
= params
->value
;
2375 add_req_type_constraint (listp
, position
, *t_types
);
2379 for (; paramcount
> 0; params
++, paramcount
--)
2380 switch (params
->type
)
2384 case PT_CHARACTER
: case PT_INTEGER
: case PT_ARGCOUNT
:
2385 /* too many params for directive */
2387 xasprintf (ngettext ("In the directive number %u, too many parameters are given; expected at most %u parameter.",
2388 "In the directive number %u, too many parameters are given; expected at most %u parameters.",
2390 directives
, orig_t_count
);
2393 /* Force argument to be NIL. */
2395 int position
= params
->value
;
2398 struct format_arg_list
*empty_list
= make_empty_list ();
2399 add_req_listtype_constraint (listp
, position
,
2400 FAT_LIST
, empty_list
);
2401 free_list (empty_list
);
2411 /* Handle the parameters, without a priori type information.
2412 For V params, add the constraint to the argument list.
2413 Return false and fill in *invalid_reason if the format string is
2416 nocheck_params (struct format_arg_list
**listp
,
2417 unsigned int paramcount
, struct param
*params
,
2418 unsigned int directives
, char **invalid_reason
)
2421 (void) invalid_reason
;
2423 for (; paramcount
> 0; params
++, paramcount
--)
2424 if (params
->type
== PT_V
)
2426 int position
= params
->value
;
2427 add_req_type_constraint (listp
, position
, FAT_CHARACTER_INTEGER_NULL
);
2434 /* ======================= The format string parser ======================= */
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
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 If the format string is invalid, false is returned and *invalid_reason is
2452 set to an error message explaining why. */
2454 parse_upto (const char **formatp
,
2455 int *positionp
, struct format_arg_list
**listp
,
2456 struct format_arg_list
**escapep
, int *separatorp
,
2457 struct spec
*spec
, char terminator
, bool separator
,
2458 char **invalid_reason
)
2460 const char *format
= *formatp
;
2461 int position
= *positionp
;
2462 struct format_arg_list
*list
= *listp
;
2463 struct format_arg_list
*escape
= *escapep
;
2465 for (; *format
!= '\0'; )
2466 if (*format
++ == '~')
2468 bool colon_p
= false;
2469 bool atsign_p
= false;
2470 unsigned int paramcount
= 0;
2471 struct param
*params
= NULL
;
2473 /* Count number of directives. */
2476 /* Parse parameters. */
2479 enum param_type type
= PT_NIL
;
2482 if (c_isdigit (*format
))
2487 value
= 10 * value
+ (*format
- '0');
2490 while (c_isdigit (*format
));
2492 else if (*format
== '+' || *format
== '-')
2494 bool negative
= (*format
== '-');
2497 if (!c_isdigit (*format
))
2501 ? INVALID_UNTERMINATED_DIRECTIVE ()
2502 : xasprintf (_("In the directive number %u, '%c' is not followed by a digit."), spec
->directives
, format
[-1]));
2507 value
= 10 * value
+ (*format
- '0');
2510 while (c_isdigit (*format
));
2514 else if (*format
== '\'')
2516 type
= PT_CHARACTER
;
2518 if (*format
== '\0')
2520 *invalid_reason
= INVALID_UNTERMINATED_DIRECTIVE ();
2525 else if (*format
== 'V' || *format
== 'v')
2530 /* Consumes an argument. */
2534 else if (*format
== '#')
2542 xrealloc (params
, (paramcount
+ 1) * sizeof (struct param
));
2543 params
[paramcount
].type
= type
;
2544 params
[paramcount
].value
= value
;
2553 /* Parse modifiers. */
2561 else if (*format
== '@')
2570 /* Parse directive. */
2573 case 'A': case 'a': /* 22.3.4.1 FORMAT-ASCII */
2574 case 'S': case 's': /* 22.3.4.2 FORMAT-S-EXPRESSION */
2575 if (!check_params (&list
, paramcount
, params
, 4, IIIC
,
2576 spec
->directives
, invalid_reason
))
2579 add_req_type_constraint (&list
, position
++, FAT_OBJECT
);
2582 case 'W': case 'w': /* 22.3.4.3 FORMAT-WRITE */
2583 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2584 spec
->directives
, invalid_reason
))
2587 add_req_type_constraint (&list
, position
++, FAT_OBJECT
);
2590 case 'D': case 'd': /* 22.3.2.2 FORMAT-DECIMAL */
2591 case 'B': case 'b': /* 22.3.2.3 FORMAT-BINARY */
2592 case 'O': case 'o': /* 22.3.2.4 FORMAT-OCTAL */
2593 case 'X': case 'x': /* 22.3.2.5 FORMAT-HEXADECIMAL */
2594 if (!check_params (&list
, paramcount
, params
, 4, ICCI
,
2595 spec
->directives
, invalid_reason
))
2598 add_req_type_constraint (&list
, position
++, FAT_INTEGER
);
2601 case 'R': case 'r': /* 22.3.2.1 FORMAT-RADIX */
2602 if (!check_params (&list
, paramcount
, params
, 5, IICCI
,
2603 spec
->directives
, invalid_reason
))
2606 add_req_type_constraint (&list
, position
++, FAT_INTEGER
);
2609 case 'P': case 'p': /* 22.3.8.3 FORMAT-PLURAL */
2610 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2611 spec
->directives
, invalid_reason
))
2615 /* Go back by 1 argument. */
2620 add_req_type_constraint (&list
, position
++, FAT_OBJECT
);
2623 case 'C': case 'c': /* 22.3.1.1 FORMAT-CHARACTER */
2624 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2625 spec
->directives
, invalid_reason
))
2628 add_req_type_constraint (&list
, position
++, FAT_CHARACTER
);
2631 case 'F': case 'f': /* 22.3.3.1 FORMAT-FIXED-FLOAT */
2632 if (!check_params (&list
, paramcount
, params
, 5, IIICC
,
2633 spec
->directives
, invalid_reason
))
2636 add_req_type_constraint (&list
, position
++, FAT_REAL
);
2639 case 'E': case 'e': /* 22.3.3.2 FORMAT-EXPONENTIAL-FLOAT */
2640 case 'G': case 'g': /* 22.3.3.3 FORMAT-GENERAL-FLOAT */
2641 if (!check_params (&list
, paramcount
, params
, 7, IIIICCC
,
2642 spec
->directives
, invalid_reason
))
2645 add_req_type_constraint (&list
, position
++, FAT_REAL
);
2648 case '$': /* 22.3.3.4 FORMAT-DOLLARS-FLOAT */
2649 if (!check_params (&list
, paramcount
, params
, 4, IIIC
,
2650 spec
->directives
, invalid_reason
))
2653 add_req_type_constraint (&list
, position
++, FAT_REAL
);
2656 case '%': /* 22.3.1.2 FORMAT-TERPRI */
2657 case '&': /* 22.3.1.3 FORMAT-FRESH-LINE */
2658 case '|': /* 22.3.1.4 FORMAT-PAGE */
2659 case '~': /* 22.3.1.5 FORMAT-TILDE */
2660 case 'I': case 'i': /* 22.3.5.3 */
2661 if (!check_params (&list
, paramcount
, params
, 1, I
,
2662 spec
->directives
, invalid_reason
))
2666 case '\n': /* 22.3.9.3 #\Newline */
2667 case '_': /* 22.3.5.1 */
2668 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2669 spec
->directives
, invalid_reason
))
2673 case 'T': case 't': /* 22.3.6.1 FORMAT-TABULATE */
2674 if (!check_params (&list
, paramcount
, params
, 2, II
,
2675 spec
->directives
, invalid_reason
))
2679 case '*': /* 22.3.7.1 FORMAT-GOTO */
2680 if (!check_params (&list
, paramcount
, params
, 1, I
,
2681 spec
->directives
, invalid_reason
))
2684 int n
; /* value of first parameter */
2686 || (paramcount
>= 1 && params
[0].type
== PT_NIL
))
2687 n
= (atsign_p
? 0 : 1);
2688 else if (paramcount
>= 1 && params
[0].type
== PT_INTEGER
)
2689 n
= params
[0].value
;
2692 /* Unknown argument, leads to an unknown position. */
2698 /* invalid argument */
2700 xasprintf (_("In the directive number %u, the argument %d is negative."), spec
->directives
, n
);
2705 /* Absolute goto. */
2710 /* Backward goto. */
2733 case '?': /* 22.3.7.6 FORMAT-INDIRECTION */
2734 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2735 spec
->directives
, invalid_reason
))
2738 add_req_type_constraint (&list
, position
++, FAT_FORMATSTRING
);
2744 struct format_arg_list
*sublist
= make_unconstrained_list ();
2745 add_req_listtype_constraint (&list
, position
++,
2747 free_list (sublist
);
2751 case '/': /* 22.3.5.4 FORMAT-CALL-USER-FUNCTION */
2752 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2753 spec
->directives
, invalid_reason
))
2756 add_req_type_constraint (&list
, position
++, FAT_OBJECT
);
2757 while (*format
!= '\0' && *format
!= '/')
2759 if (*format
== '\0')
2762 xstrdup (_("The string ends in the middle of a ~/.../ directive."));
2768 case '(': /* 22.3.8.1 FORMAT-CASE-CONVERSION */
2769 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2770 spec
->directives
, invalid_reason
))
2773 *positionp
= position
;
2777 if (!parse_upto (formatp
, positionp
, listp
, escapep
,
2778 NULL
, spec
, ')', false,
2783 position
= *positionp
;
2788 case ')': /* 22.3.8.2 FORMAT-CASE-CONVERSION-END */
2789 if (terminator
!= ')')
2792 xasprintf (_("Found '~%c' without matching '~%c'."), ')', '(');
2795 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2796 spec
->directives
, invalid_reason
))
2799 *positionp
= position
;
2804 case '[': /* 22.3.7.2 FORMAT-CONDITIONAL */
2805 if (atsign_p
&& colon_p
)
2808 xasprintf (_("In the directive number %u, both the @ and the : modifiers are given."), spec
->directives
);
2813 struct format_arg_list
*nil_list
;
2814 struct format_arg_list
*union_list
;
2816 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2817 spec
->directives
, invalid_reason
))
2823 /* First alternative: argument is NIL. */
2824 nil_list
= (list
!= NULL
? copy_list (list
) : NULL
);
2827 struct format_arg_list
*empty_list
= make_empty_list ();
2828 add_req_listtype_constraint (&nil_list
, position
,
2829 FAT_LIST
, empty_list
);
2830 free_list (empty_list
);
2833 /* Second alternative: use sub-format. */
2835 int sub_position
= position
;
2836 struct format_arg_list
*sub_list
=
2837 (list
!= NULL
? copy_list (list
) : NULL
);
2838 if (!parse_upto (formatp
, &sub_position
, &sub_list
, escapep
,
2839 NULL
, spec
, ']', false,
2842 if (sub_list
!= NULL
)
2846 if (sub_position
== position
+ 1)
2847 /* new position is branch independent */
2848 position
= position
+ 1;
2850 /* new position is branch dependent */
2857 position
= position
+ 1;
2859 union_list
= union (nil_list
, sub_list
);
2872 struct format_arg_list
*union_list
;
2874 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2875 spec
->directives
, invalid_reason
))
2879 add_req_type_constraint (&list
, position
++, FAT_OBJECT
);
2883 union_position
= -2;
2886 /* First alternative. */
2888 int sub_position
= position
;
2889 struct format_arg_list
*sub_list
=
2890 (list
!= NULL
? copy_list (list
) : NULL
);
2891 int sub_separator
= 0;
2894 struct format_arg_list
*empty_list
= make_empty_list ();
2895 add_req_listtype_constraint (&sub_list
, position
- 1,
2896 FAT_LIST
, empty_list
);
2897 free_list (empty_list
);
2899 if (!parse_upto (formatp
, &sub_position
, &sub_list
, escapep
,
2900 &sub_separator
, spec
, ']', true,
2906 xasprintf (_("In the directive number %u, '~:[' is not followed by two clauses, separated by '~;'."), spec
->directives
);
2909 if (sub_list
!= NULL
)
2910 union_position
= sub_position
;
2911 union_list
= union (union_list
, sub_list
);
2914 /* Second alternative. */
2916 int sub_position
= position
;
2917 struct format_arg_list
*sub_list
=
2918 (list
!= NULL
? copy_list (list
) : NULL
);
2919 if (!parse_upto (formatp
, &sub_position
, &sub_list
, escapep
,
2920 NULL
, spec
, ']', false,
2923 if (sub_list
!= NULL
)
2925 if (union_position
== -2)
2926 union_position
= sub_position
;
2927 else if (sub_position
< 0
2928 || sub_position
!= union_position
)
2929 union_position
= -1;
2931 union_list
= union (union_list
, sub_list
);
2937 if (union_position
!= -2)
2938 position
= union_position
;
2947 struct format_arg_list
*union_list
;
2948 bool last_alternative
;
2950 if (!check_params (&list
, paramcount
, params
, 1, I
,
2951 spec
->directives
, invalid_reason
))
2954 /* If there was no first parameter, an argument is consumed. */
2956 if (!(paramcount
>= 1 && params
[0].type
!= PT_NIL
))
2959 arg_position
= position
;
2960 add_req_type_constraint (&list
, position
++, FAT_OBJECT
);
2966 union_position
= -2;
2968 last_alternative
= false;
2971 /* Next alternative. */
2972 int sub_position
= position
;
2973 struct format_arg_list
*sub_list
=
2974 (list
!= NULL
? copy_list (list
) : NULL
);
2975 int sub_separator
= 0;
2976 if (!parse_upto (formatp
, &sub_position
, &sub_list
, escapep
,
2977 &sub_separator
, spec
, ']', !last_alternative
,
2980 /* If this alternative is chosen, the argument arg_position
2981 is an integer, namely the index of this alternative. */
2982 if (!last_alternative
&& arg_position
>= 0)
2983 add_req_type_constraint (&sub_list
, arg_position
,
2985 if (sub_list
!= NULL
)
2987 if (union_position
== -2)
2988 union_position
= sub_position
;
2989 else if (sub_position
< 0
2990 || sub_position
!= union_position
)
2991 union_position
= -1;
2993 union_list
= union (union_list
, sub_list
);
2994 if (sub_separator
== 2)
2995 last_alternative
= true;
2999 if (!last_alternative
)
3001 /* An implicit default alternative. */
3002 if (union_position
== -2)
3003 union_position
= position
;
3004 else if (position
< 0 || position
!= union_position
)
3005 union_position
= -1;
3007 union_list
= union (union_list
, copy_list (list
));
3013 if (union_position
!= -2)
3014 position
= union_position
;
3021 case ']': /* 22.3.7.3 FORMAT-CONDITIONAL-END */
3022 if (terminator
!= ']')
3025 xasprintf (_("Found '~%c' without matching '~%c'."), ']', '[');
3028 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
3029 spec
->directives
, invalid_reason
))
3032 *positionp
= position
;
3037 case '{': /* 22.3.7.4 FORMAT-ITERATION */
3038 if (!check_params (&list
, paramcount
, params
, 1, I
,
3039 spec
->directives
, invalid_reason
))
3043 int sub_position
= 0;
3044 struct format_arg_list
*sub_list
= make_unconstrained_list ();
3045 struct format_arg_list
*sub_escape
= NULL
;
3046 struct spec sub_spec
;
3047 sub_spec
.directives
= 0;
3048 sub_spec
.list
= sub_list
;
3049 if (!parse_upto (formatp
, &sub_position
, &sub_list
, &sub_escape
,
3050 NULL
, &sub_spec
, '}', false,
3053 spec
->directives
+= sub_spec
.directives
;
3055 /* If the sub-formatstring is empty, except for the terminating
3056 ~} directive, a formatstring argument is consumed. */
3057 if (*format
== '~' && sub_spec
.directives
== 1)
3059 add_req_type_constraint (&list
, position
++, FAT_FORMATSTRING
);
3063 /* Each iteration uses a new sublist. */
3064 struct format_arg_list
*listlist
;
3066 /* ~{ catches ~^. */
3067 sub_list
= union (sub_list
, sub_escape
);
3069 listlist
= make_repeated_list_of_lists (sub_list
);
3071 sub_list
= listlist
;
3075 /* Each iteration's arguments are all concatenated in a
3077 struct format_arg_list
*looplist
;
3079 /* FIXME: This is far from correct. Test cases:
3085 abc~{~D~^~C~}~:*~{~S~^~D~}
3088 /* ~{ catches ~^. */
3089 sub_list
= union (sub_list
, sub_escape
);
3091 if (sub_list
== NULL
)
3092 looplist
= make_empty_list ();
3094 if (sub_position
< 0 || sub_position
== 0)
3095 /* Too hard to track the possible argument types
3096 when the iteration is performed 2 times or more.
3097 So be satisfied with the constraints of executing
3098 the iteration 1 or 0 times. */
3099 looplist
= make_union_with_empty_list (sub_list
);
3101 looplist
= make_repeated_list (sub_list
, sub_position
);
3103 sub_list
= looplist
;
3108 /* All remaining arguments are used. */
3109 if (list
!= NULL
&& position
>= 0)
3111 shift_list (sub_list
, position
);
3112 list
= make_intersected_list (list
, sub_list
);
3118 /* The argument is a list. */
3120 add_req_listtype_constraint (&list
, position
++,
3121 FAT_LIST
, sub_list
);
3127 case '}': /* 22.3.7.5 FORMAT-ITERATION-END */
3128 if (terminator
!= '}')
3131 xasprintf (_("Found '~%c' without matching '~%c'."), '}', '{');
3134 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
3135 spec
->directives
, invalid_reason
))
3138 *positionp
= position
;
3143 case '<': /* 22.3.6.2, 22.3.5.2 FORMAT-JUSTIFICATION */
3144 if (!check_params (&list
, paramcount
, params
, 4, IIIC
,
3145 spec
->directives
, invalid_reason
))
3148 struct format_arg_list
*sub_escape
= NULL
;
3151 *positionp
= position
;
3156 int sub_separator
= 0;
3157 if (!parse_upto (formatp
, positionp
, listp
, &sub_escape
,
3158 &sub_separator
, spec
, '>', true,
3166 position
= *positionp
;
3169 /* ~< catches ~^. */
3170 if (sub_escape
!= NULL
)
3172 list
= union (list
, sub_escape
);
3176 case '>': /* 22.3.6.3 FORMAT-JUSTIFICATION-END */
3177 if (terminator
!= '>')
3180 xasprintf (_("Found '~%c' without matching '~%c'."), '>', '<');
3183 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
3184 spec
->directives
, invalid_reason
))
3187 *positionp
= position
;
3192 case '^': /* 22.3.9.2 FORMAT-UP-AND-OUT */
3193 if (!check_params (&list
, paramcount
, params
, 3, THREE
,
3194 spec
->directives
, invalid_reason
))
3196 if (position
>= 0 && list
!= NULL
&& is_required (list
, position
))
3197 /* This ~^ can never be executed. Ignore it. */
3201 struct format_arg_list
*this_escape
= copy_list (list
);
3203 this_escape
= add_end_constraint (this_escape
, position
);
3204 escape
= union (escape
, this_escape
);
3207 list
= add_required_constraint (list
, position
);
3210 case ';': /* 22.3.9.1 FORMAT-SEPARATOR */
3214 xasprintf (_("In the directive number %u, '~;' is used in an invalid position."), spec
->directives
);
3217 if (terminator
== '>')
3219 if (!check_params (&list
, paramcount
, params
, 1, I
,
3220 spec
->directives
, invalid_reason
))
3225 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
3226 spec
->directives
, invalid_reason
))
3230 *positionp
= position
;
3233 *separatorp
= (colon_p
? 2 : 1);
3236 case '!': /* FORMAT-CALL, a CLISP extension */
3237 if (!nocheck_params (&list
, paramcount
, params
,
3238 spec
->directives
, invalid_reason
))
3242 add_req_type_constraint (&list
, position
++, FAT_FUNCTION
);
3243 add_req_type_constraint (&list
, position
++, FAT_OBJECT
);
3251 ? INVALID_UNTERMINATED_DIRECTIVE ()
3252 : INVALID_CONVERSION_SPECIFIER (spec
->directives
, *format
));
3260 *positionp
= position
;
3263 if (terminator
!= '\0')
3266 xasprintf (_("Found '~%c' without matching '~%c'."), terminator
- 1, terminator
);
3273 /* ============== Top level format string handling functions ============== */
3276 format_parse (const char *format
, bool translated
, char **invalid_reason
)
3279 struct spec
*result
;
3281 struct format_arg_list
*escape
;
3283 spec
.directives
= 0;
3284 spec
.list
= make_unconstrained_list ();
3287 if (!parse_upto (&format
, &position
, &spec
.list
, &escape
,
3288 NULL
, &spec
, '\0', false,
3290 /* Invalid format string. */
3293 /* Catch ~^ here. */
3294 spec
.list
= union (spec
.list
, escape
);
3296 if (spec
.list
== NULL
)
3298 /* Contradictory argument type information. */
3300 xstrdup (_("The string refers to some argument in incompatible ways."));
3304 /* Normalize the result. */
3305 normalize_list (spec
.list
);
3307 result
= (struct spec
*) xmalloc (sizeof (struct spec
));
3313 format_free (void *descr
)
3315 struct spec
*spec
= (struct spec
*) descr
;
3317 free_list (spec
->list
);
3321 format_get_number_of_directives (void *descr
)
3323 struct spec
*spec
= (struct spec
*) descr
;
3325 return spec
->directives
;
3329 format_check (void *msgid_descr
, void *msgstr_descr
, bool equality
,
3330 formatstring_error_logger_t error_logger
,
3331 const char *pretty_msgstr
)
3333 struct spec
*spec1
= (struct spec
*) msgid_descr
;
3334 struct spec
*spec2
= (struct spec
*) msgstr_descr
;
3339 if (!equal_list (spec1
->list
, spec2
->list
))
3342 error_logger (_("format specifications in 'msgid' and '%s' are not equivalent"),
3349 struct format_arg_list
*intersection
=
3350 make_intersected_list (copy_list (spec1
->list
),
3351 copy_list (spec2
->list
));
3353 if (!(intersection
!= NULL
3354 && (normalize_list (intersection
),
3355 equal_list (intersection
, spec2
->list
))))
3358 error_logger (_("format specifications in '%s' are not a subset of those in 'msgid'"),
3368 struct formatstring_parser formatstring_lisp
=
3372 format_get_number_of_directives
,
3377 /* ============================= Testing code ============================= */
3383 /* Test program: Print the argument list specification returned by
3384 format_parse for strings read from standard input. */
3387 #include "getline.h"
3389 static void print_list (struct format_arg_list
*list
);
3392 print_element (struct format_arg
*element
)
3394 switch (element
->presence
)
3405 switch (element
->type
)
3410 case FAT_CHARACTER_INTEGER_NULL
:
3413 case FAT_CHARACTER_NULL
:
3419 case FAT_INTEGER_NULL
:
3429 print_list (element
->list
);
3431 case FAT_FORMATSTRING
:
3443 print_list (struct format_arg_list
*list
)
3449 for (i
= 0; i
< list
->initial
.count
; i
++)
3450 for (j
= 0; j
< list
->initial
.element
[i
].repcount
; j
++)
3454 print_element (&list
->initial
.element
[i
]);
3457 if (list
->repeated
.count
> 0)
3460 for (i
= 0; i
< list
->repeated
.count
; i
++)
3461 for (j
= 0; j
< list
->repeated
.element
[i
].repcount
; j
++)
3464 print_element (&list
->repeated
.element
[i
]);
3472 format_print (void *descr
)
3474 struct spec
*spec
= (struct spec
*) descr
;
3482 print_list (spec
->list
);
3491 size_t line_size
= 0;
3493 char *invalid_reason
;
3496 line_len
= getline (&line
, &line_size
, stdin
);
3499 if (line_len
> 0 && line
[line_len
- 1] == '\n')
3500 line
[--line_len
] = '\0';
3502 invalid_reason
= NULL
;
3503 descr
= format_parse (line
, false, &invalid_reason
);
3505 format_print (descr
);
3508 printf ("%s\n", invalid_reason
);
3510 free (invalid_reason
);
3518 * For Emacs M-x compile
3520 * compile-command: "/bin/sh ../libtool --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../lib -I../intl -DHAVE_CONFIG_H -DTEST format-lisp.c ../lib/libgettextlib.la"