1 /* Scheme format strings.
2 Copyright (C) 2001-2005 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"
34 #include "error-progname.h"
37 #define _(str) gettext (str)
40 /* Assertion macros. Could be defined to empty for speed. */
41 #define ASSERT(expr) if (!(expr)) abort ();
42 #define VERIFY_LIST(list) verify_list (list)
45 /* Scheme format strings are described in the GNU guile documentation,
46 section "Formatted Output". They are implemented in
47 guile-1.6.4/ice-9/format.scm. */
49 /* Data structure describing format string derived constraints for an
50 argument list. It is a recursive list structure. Structure sharing
55 FCT_REQUIRED
, /* The format argument list cannot end before this argument. */
56 FCT_OPTIONAL
/* The format argument list may end before this argument. */
61 FAT_OBJECT
, /* Any object, type T. */
62 FAT_CHARACTER_INTEGER_NULL
, /* Type (OR CHARACTER INTEGER NULL). */
63 FAT_CHARACTER_NULL
, /* Type (OR CHARACTER NULL). */
64 FAT_CHARACTER
, /* Type CHARACTER. */
65 FAT_INTEGER_NULL
, /* Type (OR INTEGER NULL). */
66 FAT_INTEGER
, /* Meant for objects of type INTEGER. */
67 FAT_REAL
, /* Meant for objects of type REAL. */
68 FAT_COMPLEX
, /* Meant for objects of type COMPLEX. */
69 FAT_LIST
, /* Meant for proper lists. */
70 FAT_FORMATSTRING
/* Format strings. */
75 unsigned int repcount
; /* Number of consecutive arguments this constraint
76 applies to. Normally 1, but unconstrained
77 arguments are often repeated. */
78 enum format_cdr_type presence
; /* Can the argument list end right before
80 enum format_arg_type type
; /* Possible values for this argument. */
81 struct format_arg_list
*list
; /* For FAT_LIST: List elements. */
84 struct format_arg_list
86 /* The constraints for the potentially infinite argument list are assumed
87 to become ultimately periodic. (Too complicated argument lists without
89 (format t "~@{~:[-~;~S~]~}" nil t 1 t 3 nil t 4)
90 are described by a constraint that ends in a length 1 period of
91 unconstrained arguments.) Such a periodic sequence can be split into
92 an initial segment and an endlessly repeated loop segment.
93 A finite sequence is represented entirely in the initial segment; the
94 loop segment is empty. */
98 unsigned int count
; /* Number of format_arg records used. */
99 unsigned int allocated
;
100 struct format_arg
*element
; /* Argument constraints. */
101 unsigned int length
; /* Number of arguments represented by this segment.
102 This is the sum of all repcounts in the segment. */
104 initial
, /* Initial arguments segment. */
105 repeated
; /* Endlessly repeated segment. */
110 unsigned int directives
;
111 struct format_arg_list
*list
;
115 /* Parameter for a directive. */
118 PT_NIL
, /* param not present */
119 PT_CHARACTER
, /* character */
120 PT_INTEGER
, /* integer */
121 PT_ARGCOUNT
, /* number of remaining arguments */
122 PT_V
/* variable taken from argument list */
127 enum param_type type
;
128 int value
; /* for PT_INTEGER: the value, for PT_V: the position */
132 /* Forward declaration of local functions. */
133 #define union make_union
134 static void verify_list (const struct format_arg_list
*list
);
135 static void free_list (struct format_arg_list
*list
);
136 static struct format_arg_list
* copy_list (const struct format_arg_list
*list
);
137 static bool equal_list (const struct format_arg_list
*list1
,
138 const struct format_arg_list
*list2
);
139 static struct format_arg_list
* make_intersected_list
140 (struct format_arg_list
*list1
,
141 struct format_arg_list
*list2
);
142 static struct format_arg_list
* make_intersection_with_empty_list
143 (struct format_arg_list
*list
);
144 static struct format_arg_list
* make_union_list
145 (struct format_arg_list
*list1
,
146 struct format_arg_list
*list2
);
149 /* ======================= Verify a format_arg_list ======================= */
151 /* Verify some invariants. */
153 verify_element (const struct format_arg
* e
)
155 ASSERT (e
->repcount
> 0);
156 if (e
->type
== FAT_LIST
)
157 verify_list (e
->list
);
160 /* Verify some invariants. */
161 /* Memory effects: none. */
163 verify_list (const struct format_arg_list
*list
)
166 unsigned int total_repcount
;
168 ASSERT (list
->initial
.count
<= list
->initial
.allocated
);
170 for (i
= 0; i
< list
->initial
.count
; i
++)
172 verify_element (&list
->initial
.element
[i
]);
173 total_repcount
+= list
->initial
.element
[i
].repcount
;
175 ASSERT (total_repcount
== list
->initial
.length
);
177 ASSERT (list
->repeated
.count
<= list
->repeated
.allocated
);
179 for (i
= 0; i
< list
->repeated
.count
; i
++)
181 verify_element (&list
->repeated
.element
[i
]);
182 total_repcount
+= list
->repeated
.element
[i
].repcount
;
184 ASSERT (total_repcount
== list
->repeated
.length
);
187 #define VERIFY_LIST(list) verify_list (list)
190 /* ======================== Free a format_arg_list ======================== */
192 /* Free the data belonging to an argument list element. */
194 free_element (struct format_arg
*element
)
196 if (element
->type
== FAT_LIST
)
197 free_list (element
->list
);
200 /* Free an argument list. */
201 /* Memory effects: Frees list. */
203 free_list (struct format_arg_list
*list
)
207 for (i
= 0; i
< list
->initial
.count
; i
++)
208 free_element (&list
->initial
.element
[i
]);
209 if (list
->initial
.element
!= NULL
)
210 free (list
->initial
.element
);
212 for (i
= 0; i
< list
->repeated
.count
; i
++)
213 free_element (&list
->repeated
.element
[i
]);
214 if (list
->repeated
.element
!= NULL
)
215 free (list
->repeated
.element
);
219 /* ======================== Copy a format_arg_list ======================== */
221 /* Copy the data belonging to an argument list element. */
223 copy_element (struct format_arg
*newelement
,
224 const struct format_arg
*oldelement
)
226 newelement
->repcount
= oldelement
->repcount
;
227 newelement
->presence
= oldelement
->presence
;
228 newelement
->type
= oldelement
->type
;
229 if (oldelement
->type
== FAT_LIST
)
230 newelement
->list
= copy_list (oldelement
->list
);
233 /* Copy an argument list. */
234 /* Memory effects: Freshly allocated result. */
235 static struct format_arg_list
*
236 copy_list (const struct format_arg_list
*list
)
238 struct format_arg_list
*newlist
;
245 (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
247 newlist
->initial
.count
= newlist
->initial
.allocated
= list
->initial
.count
;
249 if (list
->initial
.count
== 0)
250 newlist
->initial
.element
= NULL
;
253 newlist
->initial
.element
=
254 (struct format_arg
*)
255 xmalloc (newlist
->initial
.allocated
* sizeof (struct format_arg
));
256 for (i
= 0; i
< list
->initial
.count
; i
++)
258 copy_element (&newlist
->initial
.element
[i
],
259 &list
->initial
.element
[i
]);
260 length
+= list
->initial
.element
[i
].repcount
;
263 ASSERT (length
== list
->initial
.length
);
264 newlist
->initial
.length
= length
;
266 newlist
->repeated
.count
= newlist
->repeated
.allocated
= list
->repeated
.count
;
268 if (list
->repeated
.count
== 0)
269 newlist
->repeated
.element
= NULL
;
272 newlist
->repeated
.element
=
273 (struct format_arg
*)
274 xmalloc (newlist
->repeated
.allocated
* sizeof (struct format_arg
));
275 for (i
= 0; i
< list
->repeated
.count
; i
++)
277 copy_element (&newlist
->repeated
.element
[i
],
278 &list
->repeated
.element
[i
]);
279 length
+= list
->repeated
.element
[i
].repcount
;
282 ASSERT (length
== list
->repeated
.length
);
283 newlist
->repeated
.length
= length
;
285 VERIFY_LIST (newlist
);
291 /* ===================== Compare two format_arg_lists ===================== */
293 /* Tests whether two normalized argument constraints are equivalent,
294 ignoring the repcount. */
296 equal_element (const struct format_arg
* e1
, const struct format_arg
* e2
)
298 return (e1
->presence
== e2
->presence
299 && e1
->type
== e2
->type
300 && (e1
->type
== FAT_LIST
? equal_list (e1
->list
, e2
->list
) : true));
303 /* Tests whether two normalized argument list constraints are equivalent. */
304 /* Memory effects: none. */
306 equal_list (const struct format_arg_list
*list1
,
307 const struct format_arg_list
*list2
)
314 n
= list1
->initial
.count
;
315 if (n
!= list2
->initial
.count
)
317 for (i
= 0; i
< n
; i
++)
319 const struct format_arg
* e1
= &list1
->initial
.element
[i
];
320 const struct format_arg
* e2
= &list2
->initial
.element
[i
];
322 if (!(e1
->repcount
== e2
->repcount
&& equal_element (e1
, e2
)))
326 n
= list1
->repeated
.count
;
327 if (n
!= list2
->repeated
.count
)
329 for (i
= 0; i
< n
; i
++)
331 const struct format_arg
* e1
= &list1
->repeated
.element
[i
];
332 const struct format_arg
* e2
= &list2
->repeated
.element
[i
];
334 if (!(e1
->repcount
== e2
->repcount
&& equal_element (e1
, e2
)))
342 /* ===================== Incremental memory allocation ===================== */
344 /* Ensure list->initial.allocated >= newcount. */
346 ensure_initial_alloc (struct format_arg_list
*list
, unsigned int newcount
)
348 if (newcount
> list
->initial
.allocated
)
350 list
->initial
.allocated
=
351 MAX (2 * list
->initial
.allocated
+ 1, newcount
);
352 list
->initial
.element
=
353 (struct format_arg
*)
354 xrealloc (list
->initial
.element
,
355 list
->initial
.allocated
* sizeof (struct format_arg
));
359 /* Ensure list->initial.allocated > list->initial.count. */
361 grow_initial_alloc (struct format_arg_list
*list
)
363 if (list
->initial
.count
>= list
->initial
.allocated
)
365 list
->initial
.allocated
=
366 MAX (2 * list
->initial
.allocated
+ 1, list
->initial
.count
+ 1);
367 list
->initial
.element
=
368 (struct format_arg
*)
369 xrealloc (list
->initial
.element
,
370 list
->initial
.allocated
* sizeof (struct format_arg
));
374 /* Ensure list->repeated.allocated >= newcount. */
376 ensure_repeated_alloc (struct format_arg_list
*list
, unsigned int newcount
)
378 if (newcount
> list
->repeated
.allocated
)
380 list
->repeated
.allocated
=
381 MAX (2 * list
->repeated
.allocated
+ 1, newcount
);
382 list
->repeated
.element
=
383 (struct format_arg
*)
384 xrealloc (list
->repeated
.element
,
385 list
->repeated
.allocated
* sizeof (struct format_arg
));
389 /* Ensure list->repeated.allocated > list->repeated.count. */
391 grow_repeated_alloc (struct format_arg_list
*list
)
393 if (list
->repeated
.count
>= list
->repeated
.allocated
)
395 list
->repeated
.allocated
=
396 MAX (2 * list
->repeated
.allocated
+ 1, list
->repeated
.count
+ 1);
397 list
->repeated
.element
=
398 (struct format_arg
*)
399 xrealloc (list
->repeated
.element
,
400 list
->repeated
.allocated
* sizeof (struct format_arg
));
405 /* ====================== Normalize a format_arg_list ====================== */
407 /* Normalize an argument list constraint, assuming all sublists are already
409 /* Memory effects: Destructively modifies list. */
411 normalize_outermost_list (struct format_arg_list
*list
)
413 unsigned int n
, i
, j
;
415 /* Step 1: Combine adjacent elements.
416 Copy from i to j, keeping 0 <= j <= i. */
418 n
= list
->initial
.count
;
419 for (i
= j
= 0; i
< n
; i
++)
421 && equal_element (&list
->initial
.element
[i
],
422 &list
->initial
.element
[j
-1]))
424 list
->initial
.element
[j
-1].repcount
+=
425 list
->initial
.element
[i
].repcount
;
426 free_element (&list
->initial
.element
[i
]);
431 list
->initial
.element
[j
] = list
->initial
.element
[i
];
434 list
->initial
.count
= j
;
436 n
= list
->repeated
.count
;
437 for (i
= j
= 0; i
< n
; i
++)
439 && equal_element (&list
->repeated
.element
[i
],
440 &list
->repeated
.element
[j
-1]))
442 list
->repeated
.element
[j
-1].repcount
+=
443 list
->repeated
.element
[i
].repcount
;
444 free_element (&list
->repeated
.element
[i
]);
449 list
->repeated
.element
[j
] = list
->repeated
.element
[i
];
452 list
->repeated
.count
= j
;
454 /* Nothing more to be done if the loop segment is empty. */
455 if (list
->repeated
.count
> 0)
457 unsigned int m
, repcount0_extra
;
459 /* Step 2: Reduce the loop period. */
460 n
= list
->repeated
.count
;
463 && equal_element (&list
->repeated
.element
[0],
464 &list
->repeated
.element
[n
-1]))
466 repcount0_extra
= list
->repeated
.element
[n
-1].repcount
;
469 /* Proceed as if the loop period were n, with
470 list->repeated.element[0].repcount incremented by repcount0_extra. */
471 for (m
= 2; m
<= n
/ 2; n
++)
474 /* m is a divisor of n. Try to reduce the loop period to n. */
477 for (i
= 0; i
< n
- m
; i
++)
478 if (!((list
->repeated
.element
[i
].repcount
479 + (i
== 0 ? repcount0_extra
: 0)
480 == list
->repeated
.element
[i
+m
].repcount
)
481 && equal_element (&list
->repeated
.element
[i
],
482 &list
->repeated
.element
[i
+m
])))
489 for (i
= m
; i
< n
; i
++)
490 free_element (&list
->repeated
.element
[i
]);
491 if (n
< list
->repeated
.count
)
492 list
->repeated
.element
[m
] = list
->repeated
.element
[n
];
493 list
->repeated
.count
= list
->repeated
.count
- n
+ m
;
494 list
->repeated
.length
/= n
/ m
;
499 /* Step 3: Roll as much as possible of the initial segment's tail
501 if (list
->repeated
.count
== 1)
503 if (list
->initial
.count
> 0
504 && equal_element (&list
->initial
.element
[list
->initial
.count
-1],
505 &list
->repeated
.element
[0]))
507 /* Roll the last element of the initial segment into the loop.
508 Its repcount is irrelevant. The second-to-last element is
509 certainly different and doesn't need to be considered. */
510 list
->initial
.length
-=
511 list
->initial
.element
[list
->initial
.count
-1].repcount
;
512 list
->initial
.count
--;
517 while (list
->initial
.count
> 0
518 && equal_element (&list
->initial
.element
[list
->initial
.count
-1],
519 &list
->repeated
.element
[list
->repeated
.count
-1]))
521 unsigned int moved_repcount
=
522 MIN (list
->initial
.element
[list
->initial
.count
-1].repcount
,
523 list
->repeated
.element
[list
->repeated
.count
-1].repcount
);
525 /* Add the element at the start of list->repeated. */
526 if (equal_element (&list
->repeated
.element
[0],
527 &list
->repeated
.element
[list
->repeated
.count
-1]))
528 list
->repeated
.element
[0].repcount
+= moved_repcount
;
531 unsigned int newcount
= list
->repeated
.count
+ 1;
532 ensure_repeated_alloc (list
, newcount
);
533 for (i
= newcount
- 1; i
> 0; i
--)
534 list
->repeated
.element
[i
] = list
->repeated
.element
[i
-1];
535 list
->repeated
.count
= newcount
;
536 copy_element (&list
->repeated
.element
[0],
537 &list
->repeated
.element
[list
->repeated
.count
-1]);
538 list
->repeated
.element
[0].repcount
= moved_repcount
;
541 /* Remove the element from the end of list->repeated. */
542 list
->repeated
.element
[list
->repeated
.count
-1].repcount
-=
544 if (list
->repeated
.element
[list
->repeated
.count
-1].repcount
== 0)
546 free_element (&list
->repeated
.element
[list
->repeated
.count
-1]);
547 list
->repeated
.count
--;
550 /* Remove the element from the end of list->initial. */
551 list
->initial
.element
[list
->initial
.count
-1].repcount
-=
553 if (list
->initial
.element
[list
->initial
.count
-1].repcount
== 0)
555 free_element (&list
->initial
.element
[list
->initial
.count
-1]);
556 list
->initial
.count
--;
558 list
->initial
.length
-= moved_repcount
;
564 /* Normalize an argument list constraint. */
565 /* Memory effects: Destructively modifies list. */
567 normalize_list (struct format_arg_list
*list
)
573 /* First normalize all elements, recursively. */
574 n
= list
->initial
.count
;
575 for (i
= 0; i
< n
; i
++)
576 if (list
->initial
.element
[i
].type
== FAT_LIST
)
577 normalize_list (list
->initial
.element
[i
].list
);
578 n
= list
->repeated
.count
;
579 for (i
= 0; i
< n
; i
++)
580 if (list
->repeated
.element
[i
].type
== FAT_LIST
)
581 normalize_list (list
->repeated
.element
[i
].list
);
583 /* Then normalize the top level list. */
584 normalize_outermost_list (list
);
590 /* ===================== Unconstrained and empty lists ===================== */
592 /* It's easier to allocate these on demand, than to be careful not to
593 accidentally modify statically allocated lists. */
596 /* Create an unconstrained argument list. */
597 /* Memory effects: Freshly allocated result. */
598 static struct format_arg_list
*
599 make_unconstrained_list ()
601 struct format_arg_list
*list
;
603 list
= (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
604 list
->initial
.count
= 0;
605 list
->initial
.allocated
= 0;
606 list
->initial
.element
= NULL
;
607 list
->initial
.length
= 0;
608 list
->repeated
.count
= 1;
609 list
->repeated
.allocated
= 1;
610 list
->repeated
.element
=
611 (struct format_arg
*) xmalloc (1 * sizeof (struct format_arg
));
612 list
->repeated
.element
[0].repcount
= 1;
613 list
->repeated
.element
[0].presence
= FCT_OPTIONAL
;
614 list
->repeated
.element
[0].type
= FAT_OBJECT
;
615 list
->repeated
.length
= 1;
623 /* Create an empty argument list. */
624 /* Memory effects: Freshly allocated result. */
625 static struct format_arg_list
*
628 struct format_arg_list
*list
;
630 list
= (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
631 list
->initial
.count
= 0;
632 list
->initial
.allocated
= 0;
633 list
->initial
.element
= NULL
;
634 list
->initial
.length
= 0;
635 list
->repeated
.count
= 0;
636 list
->repeated
.allocated
= 0;
637 list
->repeated
.element
= NULL
;
638 list
->repeated
.length
= 0;
646 /* Test for an empty list. */
647 /* Memory effects: none. */
649 is_empty_list (const struct format_arg_list
*list
)
651 return (list
->initial
.count
== 0 && list
->repeated
.count
== 0);
655 /* ======================== format_arg_list surgery ======================== */
657 /* Unfold list->repeated m times, where m >= 1.
658 Assumes list->repeated.count > 0. */
659 /* Memory effects: list is destructively modified. */
661 unfold_loop (struct format_arg_list
*list
, unsigned int m
)
663 unsigned int i
, j
, k
;
667 unsigned int newcount
= list
->repeated
.count
* m
;
668 ensure_repeated_alloc (list
, newcount
);
669 i
= list
->repeated
.count
;
670 for (k
= 1; k
< m
; k
++)
671 for (j
= 0; j
< list
->repeated
.count
; j
++, i
++)
672 copy_element (&list
->repeated
.element
[i
], &list
->repeated
.element
[j
]);
673 list
->repeated
.count
= newcount
;
674 list
->repeated
.length
= list
->repeated
.length
* m
;
678 /* Ensure list->initial.length := m, where m >= list->initial.length.
679 Assumes list->repeated.count > 0. */
680 /* Memory effects: list is destructively modified. */
682 rotate_loop (struct format_arg_list
*list
, unsigned int m
)
684 if (m
== list
->initial
.length
)
687 if (list
->repeated
.count
== 1)
689 /* Instead of multiple copies of list->repeated.element[0], a single
690 copy with higher repcount is appended to list->initial. */
691 unsigned int i
, newcount
;
693 newcount
= list
->initial
.count
+ 1;
694 ensure_initial_alloc (list
, newcount
);
695 i
= list
->initial
.count
;
696 copy_element (&list
->initial
.element
[i
], &list
->repeated
.element
[0]);
697 list
->initial
.element
[i
].repcount
= m
- list
->initial
.length
;
698 list
->initial
.count
= newcount
;
699 list
->initial
.length
= m
;
703 unsigned int n
= list
->repeated
.length
;
705 /* Write m = list->initial.length + q * n + r with 0 <= r < n. */
706 unsigned int q
= (m
- list
->initial
.length
) / n
;
707 unsigned int r
= (m
- list
->initial
.length
) % n
;
709 /* Determine how many entries of list->repeated are needed for
715 s
< list
->repeated
.count
&& t
>= list
->repeated
.element
[s
].repcount
;
716 t
-= list
->repeated
.element
[s
].repcount
, s
++)
719 /* s must be < list->repeated.count, otherwise r would have been >= n. */
720 ASSERT (s
< list
->repeated
.count
);
722 /* So we need to add to list->initial:
723 q full copies of list->repeated,
724 plus the s first elements of list->repeated,
725 plus, if t > 0, a splitoff of list->repeated.element[s]. */
727 unsigned int i
, j
, k
, newcount
;
729 i
= list
->initial
.count
;
730 newcount
= i
+ q
* list
->repeated
.count
+ s
+ (t
> 0 ? 1 : 0);
731 ensure_initial_alloc (list
, newcount
);
732 for (k
= 0; k
< q
; k
++)
733 for (j
= 0; j
< list
->repeated
.count
; j
++, i
++)
734 copy_element (&list
->initial
.element
[i
],
735 &list
->repeated
.element
[j
]);
736 for (j
= 0; j
< s
; j
++, i
++)
737 copy_element (&list
->initial
.element
[i
], &list
->repeated
.element
[j
]);
740 copy_element (&list
->initial
.element
[i
],
741 &list
->repeated
.element
[j
]);
742 list
->initial
.element
[i
].repcount
= t
;
745 ASSERT (i
== newcount
);
746 list
->initial
.count
= newcount
;
747 /* The new length of the initial segment is
748 = list->initial.length
749 + q * list->repeated.length
750 + list->repeated[0..s-1].repcount + t
751 = list->initial.length + q * n + r
754 list
->initial
.length
= m
;
757 /* And rotate list->repeated. */
760 unsigned int i
, j
, oldcount
, newcount
;
761 struct format_arg
*newelement
;
763 oldcount
= list
->repeated
.count
;
764 newcount
= list
->repeated
.count
+ (t
> 0 ? 1 : 0);
766 (struct format_arg
*)
767 xmalloc (newcount
* sizeof (struct format_arg
));
769 for (j
= s
; j
< oldcount
; j
++, i
++)
770 newelement
[i
] = list
->repeated
.element
[j
];
771 for (j
= 0; j
< s
; j
++, i
++)
772 newelement
[i
] = list
->repeated
.element
[j
];
775 copy_element (&newelement
[oldcount
], &newelement
[0]);
776 newelement
[0].repcount
-= t
;
777 newelement
[oldcount
].repcount
= t
;
779 free (list
->repeated
.element
);
780 list
->repeated
.element
= newelement
;
786 /* Ensure index n in the initial segment falls on a split between elements,
787 i.e. if 0 < n < list->initial.length, then n-1 and n are covered by two
788 different adjacent elements. */
789 /* Memory effects: list is destructively modified. */
791 initial_splitelement (struct format_arg_list
*list
, unsigned int n
)
795 unsigned int oldrepcount
;
796 unsigned int newcount
;
801 if (n
> list
->initial
.length
)
803 ASSERT (list
->repeated
.count
> 0);
804 rotate_loop (list
, n
);
805 ASSERT (n
<= list
->initial
.length
);
808 /* Determine how many entries of list->initial need to be skipped. */
810 s
< list
->initial
.count
&& t
>= list
->initial
.element
[s
].repcount
;
811 t
-= list
->initial
.element
[s
].repcount
, s
++)
817 ASSERT (s
< list
->initial
.count
);
819 /* Split the entry into two entries. */
820 oldrepcount
= list
->initial
.element
[s
].repcount
;
821 newcount
= list
->initial
.count
+ 1;
822 ensure_initial_alloc (list
, newcount
);
823 for (i
= list
->initial
.count
- 1; i
> s
; i
--)
824 list
->initial
.element
[i
+1] = list
->initial
.element
[i
];
825 copy_element (&list
->initial
.element
[s
+1], &list
->initial
.element
[s
]);
826 list
->initial
.element
[s
].repcount
= t
;
827 list
->initial
.element
[s
+1].repcount
= oldrepcount
- t
;
828 list
->initial
.count
= newcount
;
836 /* Ensure index n in the initial segment is not shared. Return its index. */
837 /* Memory effects: list is destructively modified. */
839 initial_unshare (struct format_arg_list
*list
, unsigned int n
)
841 /* This does the same side effects as
842 initial_splitelement (list, n);
843 initial_splitelement (list, n + 1);
850 if (n
>= list
->initial
.length
)
852 ASSERT (list
->repeated
.count
> 0);
853 rotate_loop (list
, n
+ 1);
854 ASSERT (n
< list
->initial
.length
);
857 /* Determine how many entries of list->initial need to be skipped. */
859 s
< list
->initial
.count
&& t
>= list
->initial
.element
[s
].repcount
;
860 t
-= list
->initial
.element
[s
].repcount
, s
++)
863 /* s must be < list->initial.count. */
864 ASSERT (s
< list
->initial
.count
);
866 if (list
->initial
.element
[s
].repcount
> 1)
868 /* Split the entry into at most three entries: for indices < n,
869 for index n, and for indices > n. */
870 unsigned int oldrepcount
= list
->initial
.element
[s
].repcount
;
871 unsigned int newcount
=
872 list
->initial
.count
+ (t
== 0 || t
== oldrepcount
- 1 ? 1 : 2);
873 ensure_initial_alloc (list
, newcount
);
874 if (t
== 0 || t
== oldrepcount
- 1)
878 for (i
= list
->initial
.count
- 1; i
> s
; i
--)
879 list
->initial
.element
[i
+1] = list
->initial
.element
[i
];
880 copy_element (&list
->initial
.element
[s
+1], &list
->initial
.element
[s
]);
883 list
->initial
.element
[s
].repcount
= 1;
884 list
->initial
.element
[s
+1].repcount
= oldrepcount
- 1;
888 list
->initial
.element
[s
].repcount
= oldrepcount
- 1;
889 list
->initial
.element
[s
+1].repcount
= 1;
896 for (i
= list
->initial
.count
- 1; i
> s
; i
--)
897 list
->initial
.element
[i
+2] = list
->initial
.element
[i
];
898 copy_element (&list
->initial
.element
[s
+2], &list
->initial
.element
[s
]);
899 copy_element (&list
->initial
.element
[s
+1], &list
->initial
.element
[s
]);
900 list
->initial
.element
[s
].repcount
= t
;
901 list
->initial
.element
[s
+1].repcount
= 1;
902 list
->initial
.element
[s
+2].repcount
= oldrepcount
- 1 - t
;
904 list
->initial
.count
= newcount
;
909 /* Now the entry for index n has repcount 1. */
910 ASSERT (list
->initial
.element
[s
].repcount
== 1);
918 /* Add n unconstrained elements at the front of the list. */
919 /* Memory effects: list is destructively modified. */
921 shift_list (struct format_arg_list
*list
, unsigned int n
)
929 grow_initial_alloc (list
);
930 for (i
= list
->initial
.count
; i
> 0; i
--)
931 list
->initial
.element
[i
] = list
->initial
.element
[i
-1];
932 list
->initial
.element
[0].repcount
= n
;
933 list
->initial
.element
[0].presence
= FCT_REQUIRED
;
934 list
->initial
.element
[0].type
= FAT_OBJECT
;
935 list
->initial
.count
++;
936 list
->initial
.length
+= n
;
938 normalize_outermost_list (list
);
945 /* ================= Intersection of two format_arg_lists ================= */
947 /* Create the intersection (i.e. combined constraints) of two argument
948 constraints. Return false if the intersection is empty, i.e. if the
949 two constraints give a contradiction. */
950 /* Memory effects: Freshly allocated element's sublist. */
952 make_intersected_element (struct format_arg
*re
,
953 const struct format_arg
* e1
,
954 const struct format_arg
* e2
)
956 /* Intersect the cdr types. */
957 if (e1
->presence
== FCT_REQUIRED
|| e2
->presence
== FCT_REQUIRED
)
958 re
->presence
= FCT_REQUIRED
;
960 re
->presence
= FCT_OPTIONAL
;
962 /* Intersect the arg types. */
963 if (e1
->type
== FAT_OBJECT
)
966 if (re
->type
== FAT_LIST
)
967 re
->list
= copy_list (e2
->list
);
969 else if (e2
->type
== FAT_OBJECT
)
972 if (re
->type
== FAT_LIST
)
973 re
->list
= copy_list (e1
->list
);
975 else if (e1
->type
== FAT_LIST
976 && (e2
->type
== FAT_CHARACTER_INTEGER_NULL
977 || e2
->type
== FAT_CHARACTER_NULL
978 || e2
->type
== FAT_INTEGER_NULL
))
981 re
->list
= make_intersection_with_empty_list (e1
->list
);
982 if (re
->list
== NULL
)
985 else if (e2
->type
== FAT_LIST
986 && (e1
->type
== FAT_CHARACTER_INTEGER_NULL
987 || e1
->type
== FAT_CHARACTER_NULL
988 || e1
->type
== FAT_INTEGER_NULL
))
991 re
->list
= make_intersection_with_empty_list (e2
->list
);
992 if (re
->list
== NULL
)
995 else if (e1
->type
== FAT_CHARACTER_INTEGER_NULL
996 && (e2
->type
== FAT_CHARACTER_NULL
|| e2
->type
== FAT_CHARACTER
997 || e2
->type
== FAT_INTEGER_NULL
|| e2
->type
== FAT_INTEGER
))
1001 else if (e2
->type
== FAT_CHARACTER_INTEGER_NULL
1002 && (e1
->type
== FAT_CHARACTER_NULL
|| e1
->type
== FAT_CHARACTER
1003 || e1
->type
== FAT_INTEGER_NULL
|| e1
->type
== FAT_INTEGER
))
1005 re
->type
= e1
->type
;
1007 else if (e1
->type
== FAT_CHARACTER_NULL
&& e2
->type
== FAT_CHARACTER
)
1009 re
->type
= e2
->type
;
1011 else if (e2
->type
== FAT_CHARACTER_NULL
&& e1
->type
== FAT_CHARACTER
)
1013 re
->type
= e1
->type
;
1015 else if (e1
->type
== FAT_INTEGER_NULL
&& e2
->type
== FAT_INTEGER
)
1017 re
->type
= e2
->type
;
1019 else if (e2
->type
== FAT_INTEGER_NULL
&& e1
->type
== FAT_INTEGER
)
1021 re
->type
= e1
->type
;
1023 else if (e1
->type
== FAT_REAL
&& e2
->type
== FAT_INTEGER
)
1025 re
->type
= e2
->type
;
1027 else if (e2
->type
== FAT_REAL
&& e1
->type
== FAT_INTEGER
)
1029 re
->type
= e1
->type
;
1031 else if (e1
->type
== FAT_COMPLEX
1032 && (e2
->type
== FAT_REAL
|| e2
->type
== FAT_INTEGER
))
1034 re
->type
= e2
->type
;
1036 else if (e2
->type
== FAT_COMPLEX
1037 && (e1
->type
== FAT_REAL
|| e1
->type
== FAT_INTEGER
))
1039 re
->type
= e1
->type
;
1041 else if (e1
->type
== e2
->type
)
1043 re
->type
= e1
->type
;
1044 if (re
->type
== FAT_LIST
)
1046 re
->list
= make_intersected_list (copy_list (e1
->list
),
1047 copy_list (e2
->list
));
1048 if (re
->list
== NULL
)
1053 /* Each of FAT_CHARACTER, FAT_INTEGER, FAT_LIST, FAT_FORMATSTRING
1054 matches only itself. Contradiction. */
1060 /* Append list->repeated to list->initial, and clear list->repeated. */
1061 /* Memory effects: list is destructively modified. */
1063 append_repeated_to_initial (struct format_arg_list
*list
)
1065 if (list
->repeated
.count
> 0)
1067 /* Move list->repeated over to list->initial. */
1068 unsigned int i
, j
, newcount
;
1070 newcount
= list
->initial
.count
+ list
->repeated
.count
;
1071 ensure_initial_alloc (list
, newcount
);
1072 i
= list
->initial
.count
;
1073 for (j
= 0; j
< list
->repeated
.count
; j
++, i
++)
1074 list
->initial
.element
[i
] = list
->repeated
.element
[j
];
1075 list
->initial
.count
= newcount
;
1076 list
->initial
.length
= list
->initial
.length
+ list
->repeated
.length
;
1077 free (list
->repeated
.element
);
1078 list
->repeated
.element
= NULL
;
1079 list
->repeated
.allocated
= 0;
1080 list
->repeated
.count
= 0;
1081 list
->repeated
.length
= 0;
1085 /* Handle a contradiction during building of a format_arg_list.
1086 The list consists only of an initial segment. The repeated segment is
1087 empty. This function searches the last FCT_OPTIONAL and cuts off the
1088 list at this point, or - if none is found - returns NULL. */
1089 /* Memory effects: list is destructively modified. If NULL is returned,
1091 static struct format_arg_list
*
1092 backtrack_in_initial (struct format_arg_list
*list
)
1094 ASSERT (list
->repeated
.count
== 0);
1096 while (list
->initial
.count
> 0)
1098 unsigned int i
= list
->initial
.count
- 1;
1099 if (list
->initial
.element
[i
].presence
== FCT_REQUIRED
)
1101 /* Throw away this element. */
1102 list
->initial
.length
-= list
->initial
.element
[i
].repcount
;
1103 free_element (&list
->initial
.element
[i
]);
1104 list
->initial
.count
= i
;
1106 else /* list->initial.element[i].presence == FCT_OPTIONAL */
1108 /* The list must end here. */
1109 list
->initial
.length
--;
1110 if (list
->initial
.element
[i
].repcount
> 1)
1111 list
->initial
.element
[i
].repcount
--;
1114 free_element (&list
->initial
.element
[i
]);
1115 list
->initial
.count
= i
;
1126 /* Create the intersection (i.e. combined constraints) of two argument list
1127 constraints. Free both argument lists when done. Return NULL if the
1128 intersection is empty, i.e. if the two constraints give a contradiction. */
1129 /* Memory effects: list1 and list2 are freed. The result, if non-NULL, is
1130 freshly allocated. */
1131 static struct format_arg_list
*
1132 make_intersected_list (struct format_arg_list
*list1
,
1133 struct format_arg_list
*list2
)
1135 struct format_arg_list
*result
;
1137 VERIFY_LIST (list1
);
1138 VERIFY_LIST (list2
);
1140 if (list1
->repeated
.length
> 0 && list2
->repeated
.length
> 0)
1141 /* Step 1: Ensure list1->repeated.length == list2->repeated.length. */
1143 unsigned int n1
= list1
->repeated
.length
;
1144 unsigned int n2
= list2
->repeated
.length
;
1145 unsigned int g
= gcd (n1
, n2
);
1146 unsigned int m1
= n2
/ g
; /* = lcm(n1,n2) / n1 */
1147 unsigned int m2
= n1
/ g
; /* = lcm(n1,n2) / n2 */
1149 unfold_loop (list1
, m1
);
1150 unfold_loop (list2
, m2
);
1151 /* Now list1->repeated.length = list2->repeated.length = lcm(n1,n2). */
1154 if (list1
->repeated
.length
> 0 || list2
->repeated
.length
> 0)
1155 /* Step 2: Ensure the initial segment of the result can be computed
1156 from the initial segments of list1 and list2. If both have a
1157 repeated segment, this means to ensure
1158 list1->initial.length == list2->initial.length. */
1160 unsigned int m
= MAX (list1
->initial
.length
, list2
->initial
.length
);
1162 if (list1
->repeated
.length
> 0)
1163 rotate_loop (list1
, m
);
1164 if (list2
->repeated
.length
> 0)
1165 rotate_loop (list2
, m
);
1168 if (list1
->repeated
.length
> 0 && list2
->repeated
.length
> 0)
1170 ASSERT (list1
->initial
.length
== list2
->initial
.length
);
1171 ASSERT (list1
->repeated
.length
== list2
->repeated
.length
);
1174 /* Step 3: Allocate the result. */
1176 (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
1177 result
->initial
.count
= 0;
1178 result
->initial
.allocated
= 0;
1179 result
->initial
.element
= NULL
;
1180 result
->initial
.length
= 0;
1181 result
->repeated
.count
= 0;
1182 result
->repeated
.allocated
= 0;
1183 result
->repeated
.element
= NULL
;
1184 result
->repeated
.length
= 0;
1186 /* Step 4: Elementwise intersection of list1->initial, list2->initial. */
1188 struct format_arg
*e1
;
1189 struct format_arg
*e2
;
1193 e1
= list1
->initial
.element
; c1
= list1
->initial
.count
;
1194 e2
= list2
->initial
.element
; c2
= list2
->initial
.count
;
1195 while (c1
> 0 && c2
> 0)
1197 struct format_arg
*re
;
1199 /* Ensure room in result->initial. */
1200 grow_initial_alloc (result
);
1201 re
= &result
->initial
.element
[result
->initial
.count
];
1202 re
->repcount
= MIN (e1
->repcount
, e2
->repcount
);
1204 /* Intersect the argument types. */
1205 if (!make_intersected_element (re
, e1
, e2
))
1207 /* If re->presence == FCT_OPTIONAL, the result list ends here. */
1208 if (re
->presence
== FCT_REQUIRED
)
1209 /* Contradiction. Backtrack. */
1210 result
= backtrack_in_initial (result
);
1214 result
->initial
.count
++;
1215 result
->initial
.length
+= re
->repcount
;
1217 e1
->repcount
-= re
->repcount
;
1218 if (e1
->repcount
== 0)
1223 e2
->repcount
-= re
->repcount
;
1224 if (e2
->repcount
== 0)
1231 if (list1
->repeated
.count
== 0 && list2
->repeated
.count
== 0)
1233 /* Intersecting two finite lists. */
1236 /* list1 longer than list2. */
1237 if (e1
->presence
== FCT_REQUIRED
)
1238 /* Contradiction. Backtrack. */
1239 result
= backtrack_in_initial (result
);
1243 /* list2 longer than list1. */
1244 if (e2
->presence
== FCT_REQUIRED
)
1245 /* Contradiction. Backtrack. */
1246 result
= backtrack_in_initial (result
);
1250 else if (list1
->repeated
.count
== 0)
1252 /* Intersecting a finite and an infinite list. */
1254 if ((c2
> 0 ? e2
->presence
: list2
->repeated
.element
[0].presence
)
1256 /* Contradiction. Backtrack. */
1257 result
= backtrack_in_initial (result
);
1260 else if (list2
->repeated
.count
== 0)
1262 /* Intersecting an infinite and a finite list. */
1264 if ((c1
> 0 ? e1
->presence
: list1
->repeated
.element
[0].presence
)
1266 /* Contradiction. Backtrack. */
1267 result
= backtrack_in_initial (result
);
1270 /* Intersecting two infinite lists. */
1271 ASSERT (c1
== 0 && c2
== 0);
1274 /* Step 5: Elementwise intersection of list1->repeated, list2->repeated. */
1276 struct format_arg
*e1
;
1277 struct format_arg
*e2
;
1281 e1
= list1
->repeated
.element
; c1
= list1
->repeated
.count
;
1282 e2
= list2
->repeated
.element
; c2
= list2
->repeated
.count
;
1283 while (c1
> 0 && c2
> 0)
1285 struct format_arg
*re
;
1287 /* Ensure room in result->repeated. */
1288 grow_repeated_alloc (result
);
1289 re
= &result
->repeated
.element
[result
->repeated
.count
];
1290 re
->repcount
= MIN (e1
->repcount
, e2
->repcount
);
1292 /* Intersect the argument types. */
1293 if (!make_intersected_element (re
, e1
, e2
))
1295 append_repeated_to_initial (result
);
1297 /* If re->presence == FCT_OPTIONAL, the result list ends here. */
1298 if (re
->presence
== FCT_REQUIRED
)
1299 /* Contradiction. Backtrack. */
1300 result
= backtrack_in_initial (result
);
1305 result
->repeated
.count
++;
1306 result
->repeated
.length
+= re
->repcount
;
1308 e1
->repcount
-= re
->repcount
;
1309 if (e1
->repcount
== 0)
1314 e2
->repcount
-= re
->repcount
;
1315 if (e2
->repcount
== 0)
1321 ASSERT (c1
== 0 && c2
== 0);
1329 /* Undo the loop unfolding and unrolling done above. */
1330 normalize_outermost_list (result
);
1331 VERIFY_LIST (result
);
1337 /* Create the intersection of an argument list and the empty list.
1338 Return NULL if the intersection is empty. */
1339 /* Memory effects: The result, if non-NULL, is freshly allocated. */
1340 static struct format_arg_list
*
1341 make_intersection_with_empty_list (struct format_arg_list
*list
)
1343 #if 0 /* equivalent but slower */
1344 return make_intersected_list (copy_list (list
), make_empty_list ());
1346 if (list
->initial
.count
> 0
1347 ? list
->initial
.element
[0].presence
== FCT_REQUIRED
1348 : list
->repeated
.count
> 0
1349 && list
->repeated
.element
[0].presence
== FCT_REQUIRED
)
1352 return make_empty_list ();
1358 /* Create the intersection of two argument list constraints. NULL stands
1359 for an impossible situation, i.e. a contradiction. */
1360 /* Memory effects: list1 and list2 are freed if non-NULL. The result,
1361 if non-NULL, is freshly allocated. */
1362 static struct format_arg_list
*
1363 intersection (struct format_arg_list
*list1
, struct format_arg_list
*list2
)
1368 return make_intersected_list (list1
, list2
);
1389 /* ===================== Union of two format_arg_lists ===================== */
1391 /* Create the union (i.e. alternative constraints) of two argument
1394 make_union_element (struct format_arg
*re
,
1395 const struct format_arg
* e1
,
1396 const struct format_arg
* e2
)
1398 /* Union of the cdr types. */
1399 if (e1
->presence
== FCT_REQUIRED
&& e2
->presence
== FCT_REQUIRED
)
1400 re
->presence
= FCT_REQUIRED
;
1401 else /* Either one of them is FCT_OPTIONAL. */
1402 re
->presence
= FCT_OPTIONAL
;
1404 /* Union of the arg types. */
1405 if (e1
->type
== e2
->type
)
1407 re
->type
= e1
->type
;
1408 if (re
->type
== FAT_LIST
)
1409 re
->list
= make_union_list (copy_list (e1
->list
),
1410 copy_list (e2
->list
));
1412 else if (e1
->type
== FAT_CHARACTER_INTEGER_NULL
1413 && (e2
->type
== FAT_CHARACTER_NULL
|| e2
->type
== FAT_CHARACTER
1414 || e2
->type
== FAT_INTEGER_NULL
|| e2
->type
== FAT_INTEGER
))
1416 re
->type
= e1
->type
;
1418 else if (e2
->type
== FAT_CHARACTER_INTEGER_NULL
1419 && (e1
->type
== FAT_CHARACTER_NULL
|| e1
->type
== FAT_CHARACTER
1420 || e1
->type
== FAT_INTEGER_NULL
|| e1
->type
== FAT_INTEGER
))
1422 re
->type
= e2
->type
;
1424 else if (e1
->type
== FAT_CHARACTER_NULL
&& e2
->type
== FAT_CHARACTER
)
1426 re
->type
= e1
->type
;
1428 else if (e2
->type
== FAT_CHARACTER_NULL
&& e1
->type
== FAT_CHARACTER
)
1430 re
->type
= e2
->type
;
1432 else if (e1
->type
== FAT_INTEGER_NULL
&& e2
->type
== FAT_INTEGER
)
1434 re
->type
= e1
->type
;
1436 else if (e2
->type
== FAT_INTEGER_NULL
&& e1
->type
== FAT_INTEGER
)
1438 re
->type
= e2
->type
;
1440 else if (e1
->type
== FAT_REAL
&& e2
->type
== FAT_INTEGER
)
1442 re
->type
= e1
->type
;
1444 else if (e2
->type
== FAT_REAL
&& e1
->type
== FAT_INTEGER
)
1446 re
->type
= e2
->type
;
1448 else if (e1
->type
== FAT_COMPLEX
1449 && (e2
->type
== FAT_REAL
|| e2
->type
== FAT_INTEGER
))
1451 re
->type
= e1
->type
;
1453 else if (e2
->type
== FAT_COMPLEX
1454 && (e1
->type
== FAT_REAL
|| e1
->type
== FAT_INTEGER
))
1456 re
->type
= e2
->type
;
1458 else if (e1
->type
== FAT_LIST
&& is_empty_list (e1
->list
))
1460 if (e2
->type
== FAT_CHARACTER_INTEGER_NULL
1461 || e2
->type
== FAT_CHARACTER_NULL
1462 || e2
->type
== FAT_INTEGER_NULL
)
1463 re
->type
= e2
->type
;
1464 else if (e2
->type
== FAT_CHARACTER
)
1465 re
->type
= FAT_CHARACTER_NULL
;
1466 else if (e2
->type
== FAT_INTEGER
)
1467 re
->type
= FAT_INTEGER_NULL
;
1469 re
->type
= FAT_OBJECT
;
1471 else if (e2
->type
== FAT_LIST
&& is_empty_list (e2
->list
))
1473 if (e1
->type
== FAT_CHARACTER_INTEGER_NULL
1474 || e1
->type
== FAT_CHARACTER_NULL
1475 || e1
->type
== FAT_INTEGER_NULL
)
1476 re
->type
= e1
->type
;
1477 else if (e1
->type
== FAT_CHARACTER
)
1478 re
->type
= FAT_CHARACTER_NULL
;
1479 else if (e1
->type
== FAT_INTEGER
)
1480 re
->type
= FAT_INTEGER_NULL
;
1482 re
->type
= FAT_OBJECT
;
1484 else if ((e1
->type
== FAT_CHARACTER
|| e1
->type
== FAT_CHARACTER_NULL
)
1485 && (e2
->type
== FAT_INTEGER
|| e2
->type
== FAT_INTEGER_NULL
))
1487 re
->type
= FAT_CHARACTER_INTEGER_NULL
;
1489 else if ((e2
->type
== FAT_CHARACTER
|| e2
->type
== FAT_CHARACTER_NULL
)
1490 && (e1
->type
== FAT_INTEGER
|| e1
->type
== FAT_INTEGER_NULL
))
1492 re
->type
= FAT_CHARACTER_INTEGER_NULL
;
1496 /* Other union types are too hard to describe precisely. */
1497 re
->type
= FAT_OBJECT
;
1501 /* Create the union (i.e. alternative constraints) of two argument list
1502 constraints. Free both argument lists when done. */
1503 /* Memory effects: list1 and list2 are freed. The result is freshly
1505 static struct format_arg_list
*
1506 make_union_list (struct format_arg_list
*list1
, struct format_arg_list
*list2
)
1508 struct format_arg_list
*result
;
1510 VERIFY_LIST (list1
);
1511 VERIFY_LIST (list2
);
1513 if (list1
->repeated
.length
> 0 && list2
->repeated
.length
> 0)
1515 /* Step 1: Ensure list1->repeated.length == list2->repeated.length. */
1517 unsigned int n1
= list1
->repeated
.length
;
1518 unsigned int n2
= list2
->repeated
.length
;
1519 unsigned int g
= gcd (n1
, n2
);
1520 unsigned int m1
= n2
/ g
; /* = lcm(n1,n2) / n1 */
1521 unsigned int m2
= n1
/ g
; /* = lcm(n1,n2) / n2 */
1523 unfold_loop (list1
, m1
);
1524 unfold_loop (list2
, m2
);
1525 /* Now list1->repeated.length = list2->repeated.length = lcm(n1,n2). */
1528 /* Step 2: Ensure that list1->initial.length == list2->initial.length. */
1530 unsigned int m
= MAX (list1
->initial
.length
, list2
->initial
.length
);
1532 rotate_loop (list1
, m
);
1533 rotate_loop (list2
, m
);
1536 ASSERT (list1
->initial
.length
== list2
->initial
.length
);
1537 ASSERT (list1
->repeated
.length
== list2
->repeated
.length
);
1539 else if (list1
->repeated
.length
> 0)
1541 /* Ensure the initial segment of the result can be computed from the
1542 initial segment of list1. */
1543 if (list2
->initial
.length
>= list1
->initial
.length
)
1545 rotate_loop (list1
, list2
->initial
.length
);
1546 if (list1
->repeated
.element
[0].presence
== FCT_REQUIRED
)
1547 rotate_loop (list1
, list1
->initial
.length
+ 1);
1550 else if (list2
->repeated
.length
> 0)
1552 /* Ensure the initial segment of the result can be computed from the
1553 initial segment of list2. */
1554 if (list1
->initial
.length
>= list2
->initial
.length
)
1556 rotate_loop (list2
, list1
->initial
.length
);
1557 if (list2
->repeated
.element
[0].presence
== FCT_REQUIRED
)
1558 rotate_loop (list2
, list2
->initial
.length
+ 1);
1562 /* Step 3: Allocate the result. */
1564 (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
1565 result
->initial
.count
= 0;
1566 result
->initial
.allocated
= 0;
1567 result
->initial
.element
= NULL
;
1568 result
->initial
.length
= 0;
1569 result
->repeated
.count
= 0;
1570 result
->repeated
.allocated
= 0;
1571 result
->repeated
.element
= NULL
;
1572 result
->repeated
.length
= 0;
1574 /* Step 4: Elementwise union of list1->initial, list2->initial. */
1576 struct format_arg
*e1
;
1577 struct format_arg
*e2
;
1581 e1
= list1
->initial
.element
; c1
= list1
->initial
.count
;
1582 e2
= list2
->initial
.element
; c2
= list2
->initial
.count
;
1583 while (c1
> 0 && c2
> 0)
1585 struct format_arg
*re
;
1587 /* Ensure room in result->initial. */
1588 grow_initial_alloc (result
);
1589 re
= &result
->initial
.element
[result
->initial
.count
];
1590 re
->repcount
= MIN (e1
->repcount
, e2
->repcount
);
1592 /* Union of the argument types. */
1593 make_union_element (re
, e1
, e2
);
1595 result
->initial
.count
++;
1596 result
->initial
.length
+= re
->repcount
;
1598 e1
->repcount
-= re
->repcount
;
1599 if (e1
->repcount
== 0)
1604 e2
->repcount
-= re
->repcount
;
1605 if (e2
->repcount
== 0)
1614 /* list2 already terminated, but still more elements in list1->initial.
1615 Copy them all, but turn the first presence to FCT_OPTIONAL. */
1616 ASSERT (list2
->repeated
.count
== 0);
1618 if (e1
->presence
== FCT_REQUIRED
)
1620 struct format_arg
*re
;
1622 /* Ensure room in result->initial. */
1623 grow_initial_alloc (result
);
1624 re
= &result
->initial
.element
[result
->initial
.count
];
1625 copy_element (re
, e1
);
1626 re
->presence
= FCT_OPTIONAL
;
1628 result
->initial
.count
++;
1629 result
->initial
.length
+= 1;
1631 if (e1
->repcount
== 0)
1638 /* Ensure room in result->initial. */
1639 ensure_initial_alloc (result
, result
->initial
.count
+ c1
);
1642 struct format_arg
*re
;
1644 re
= &result
->initial
.element
[result
->initial
.count
];
1645 copy_element (re
, e1
);
1646 result
->initial
.count
++;
1647 result
->initial
.length
+= re
->repcount
;
1654 /* list1 already terminated, but still more elements in list2->initial.
1655 Copy them all, but turn the first presence to FCT_OPTIONAL. */
1656 ASSERT (list1
->repeated
.count
== 0);
1658 if (e2
->presence
== FCT_REQUIRED
)
1660 struct format_arg
*re
;
1662 /* Ensure room in result->initial. */
1663 grow_initial_alloc (result
);
1664 re
= &result
->initial
.element
[result
->initial
.count
];
1665 copy_element (re
, e2
);
1666 re
->presence
= FCT_OPTIONAL
;
1668 result
->initial
.count
++;
1669 result
->initial
.length
+= 1;
1671 if (e2
->repcount
== 0)
1678 /* Ensure room in result->initial. */
1679 ensure_initial_alloc (result
, result
->initial
.count
+ c2
);
1682 struct format_arg
*re
;
1684 re
= &result
->initial
.element
[result
->initial
.count
];
1685 copy_element (re
, e2
);
1686 result
->initial
.count
++;
1687 result
->initial
.length
+= re
->repcount
;
1692 ASSERT (c1
== 0 && c2
== 0);
1695 if (list1
->repeated
.length
> 0 && list2
->repeated
.length
> 0)
1696 /* Step 5: Elementwise union of list1->repeated, list2->repeated. */
1698 struct format_arg
*e1
;
1699 struct format_arg
*e2
;
1703 e1
= list1
->repeated
.element
; c1
= list1
->repeated
.count
;
1704 e2
= list2
->repeated
.element
; c2
= list2
->repeated
.count
;
1705 while (c1
> 0 && c2
> 0)
1707 struct format_arg
*re
;
1709 /* Ensure room in result->repeated. */
1710 grow_repeated_alloc (result
);
1711 re
= &result
->repeated
.element
[result
->repeated
.count
];
1712 re
->repcount
= MIN (e1
->repcount
, e2
->repcount
);
1714 /* Union of the argument types. */
1715 make_union_element (re
, e1
, e2
);
1717 result
->repeated
.count
++;
1718 result
->repeated
.length
+= re
->repcount
;
1720 e1
->repcount
-= re
->repcount
;
1721 if (e1
->repcount
== 0)
1726 e2
->repcount
-= re
->repcount
;
1727 if (e2
->repcount
== 0)
1733 ASSERT (c1
== 0 && c2
== 0);
1735 else if (list1
->repeated
.length
> 0)
1737 /* Turning FCT_REQUIRED into FCT_OPTIONAL was already handled in the
1738 initial segment. Just copy the repeated segment of list1. */
1741 result
->repeated
.count
= list1
->repeated
.count
;
1742 result
->repeated
.allocated
= result
->repeated
.count
;
1743 result
->repeated
.element
=
1744 (struct format_arg
*)
1745 xmalloc (result
->repeated
.allocated
* sizeof (struct format_arg
));
1746 for (i
= 0; i
< list1
->repeated
.count
; i
++)
1747 copy_element (&result
->repeated
.element
[i
],
1748 &list1
->repeated
.element
[i
]);
1749 result
->repeated
.length
= list1
->repeated
.length
;
1751 else if (list2
->repeated
.length
> 0)
1753 /* Turning FCT_REQUIRED into FCT_OPTIONAL was already handled in the
1754 initial segment. Just copy the repeated segment of list2. */
1757 result
->repeated
.count
= list2
->repeated
.count
;
1758 result
->repeated
.allocated
= result
->repeated
.count
;
1759 result
->repeated
.element
=
1760 (struct format_arg
*)
1761 xmalloc (result
->repeated
.allocated
* sizeof (struct format_arg
));
1762 for (i
= 0; i
< list2
->repeated
.count
; i
++)
1763 copy_element (&result
->repeated
.element
[i
],
1764 &list2
->repeated
.element
[i
]);
1765 result
->repeated
.length
= list2
->repeated
.length
;
1770 /* Undo the loop unfolding and unrolling done above. */
1771 normalize_outermost_list (result
);
1772 VERIFY_LIST (result
);
1777 /* Create the union of an argument list and the empty list. */
1778 /* Memory effects: list is freed. The result is freshly allocated. */
1779 static struct format_arg_list
*
1780 make_union_with_empty_list (struct format_arg_list
*list
)
1782 #if 0 /* equivalent but slower */
1783 return make_union_list (list
, make_empty_list ());
1787 if (list
->initial
.count
> 0
1788 ? list
->initial
.element
[0].presence
== FCT_REQUIRED
1789 : list
->repeated
.count
> 0
1790 && list
->repeated
.element
[0].presence
== FCT_REQUIRED
)
1792 initial_splitelement (list
, 1);
1793 ASSERT (list
->initial
.count
> 0);
1794 ASSERT (list
->initial
.element
[0].repcount
== 1);
1795 ASSERT (list
->initial
.element
[0].presence
== FCT_REQUIRED
);
1796 list
->initial
.element
[0].presence
= FCT_OPTIONAL
;
1798 /* We might need to merge list->initial.element[0] and
1799 list->initial.element[1]. */
1800 normalize_outermost_list (list
);
1810 /* Create the union of two argument list constraints. NULL stands for an
1811 impossible situation, i.e. a contradiction. */
1812 /* Memory effects: list1 and list2 are freed if non-NULL. The result,
1813 if non-NULL, is freshly allocated. */
1814 static struct format_arg_list
*
1815 union (struct format_arg_list
*list1
, struct format_arg_list
*list2
)
1820 return make_union_list (list1
, list2
);
1834 /* =========== Adding specific constraints to a format_arg_list =========== */
1837 /* Test whether arguments 0..n are required arguments in a list. */
1839 is_required (const struct format_arg_list
*list
, unsigned int n
)
1844 /* We'll check whether the first n+1 presence flags are FCT_REQUIRED. */
1847 /* Walk the list->initial segment. */
1849 s
< list
->initial
.count
&& t
>= list
->initial
.element
[s
].repcount
;
1850 t
-= list
->initial
.element
[s
].repcount
, s
++)
1851 if (list
->initial
.element
[s
].presence
!= FCT_REQUIRED
)
1857 if (s
< list
->initial
.count
)
1859 if (list
->initial
.element
[s
].presence
!= FCT_REQUIRED
)
1865 /* Walk the list->repeated segment. */
1866 if (list
->repeated
.count
== 0)
1870 s
< list
->repeated
.count
&& t
>= list
->repeated
.element
[s
].repcount
;
1871 t
-= list
->repeated
.element
[s
].repcount
, s
++)
1872 if (list
->repeated
.element
[s
].presence
!= FCT_REQUIRED
)
1878 if (s
< list
->repeated
.count
)
1880 if (list
->repeated
.element
[s
].presence
!= FCT_REQUIRED
)
1886 /* The list->repeated segment consists only of FCT_REQUIRED. So,
1887 regardless how many more passes through list->repeated would be
1888 needed until t becomes 0, the result is true. */
1893 /* Add a constraint to an argument list, namely that the arguments 0...n are
1894 present. NULL stands for an impossible situation, i.e. a contradiction. */
1895 /* Memory effects: list is freed. The result is freshly allocated. */
1896 static struct format_arg_list
*
1897 add_required_constraint (struct format_arg_list
*list
, unsigned int n
)
1899 unsigned int i
, rest
;
1906 if (list
->repeated
.count
== 0 && list
->initial
.length
<= n
)
1908 /* list is already constrained to have at most length n.
1914 initial_splitelement (list
, n
+ 1);
1916 for (i
= 0, rest
= n
+ 1; rest
> 0; )
1918 list
->initial
.element
[i
].presence
= FCT_REQUIRED
;
1919 rest
-= list
->initial
.element
[i
].repcount
;
1929 /* Add a constraint to an argument list, namely that the argument n is
1930 never present. NULL stands for an impossible situation, i.e. a
1932 /* Memory effects: list is freed. The result is freshly allocated. */
1933 static struct format_arg_list
*
1934 add_end_constraint (struct format_arg_list
*list
, unsigned int n
)
1937 enum format_cdr_type n_presence
;
1944 if (list
->repeated
.count
== 0 && list
->initial
.length
<= n
)
1945 /* list is already constrained to have at most length n. */
1948 s
= initial_splitelement (list
, n
);
1950 (s
< list
->initial
.count
1951 ? /* n < list->initial.length */ list
->initial
.element
[s
].presence
1952 : /* n >= list->initial.length */ list
->repeated
.element
[0].presence
);
1954 for (i
= s
; i
< list
->initial
.count
; i
++)
1956 list
->initial
.length
-= list
->initial
.element
[i
].repcount
;
1957 free_element (&list
->initial
.element
[i
]);
1959 list
->initial
.count
= s
;
1961 for (i
= 0; i
< list
->repeated
.count
; i
++)
1962 free_element (&list
->repeated
.element
[i
]);
1963 if (list
->repeated
.element
!= NULL
)
1964 free (list
->repeated
.element
);
1965 list
->repeated
.element
= NULL
;
1966 list
->repeated
.allocated
= 0;
1967 list
->repeated
.count
= 0;
1968 list
->repeated
.length
= 0;
1970 if (n_presence
== FCT_REQUIRED
)
1971 return backtrack_in_initial (list
);
1977 /* Add a constraint to an argument list, namely that the argument n is
1978 of a given type. NULL stands for an impossible situation, i.e. a
1979 contradiction. Assumes a preceding add_required_constraint (list, n). */
1980 /* Memory effects: list is freed. The result is freshly allocated. */
1981 static struct format_arg_list
*
1982 add_type_constraint (struct format_arg_list
*list
, unsigned int n
,
1983 enum format_arg_type type
)
1986 struct format_arg newconstraint
;
1987 struct format_arg tmpelement
;
1992 /* Through the previous add_required_constraint, we can assume
1993 list->initial.length >= n+1. */
1995 s
= initial_unshare (list
, n
);
1997 newconstraint
.presence
= FCT_OPTIONAL
;
1998 newconstraint
.type
= type
;
1999 if (!make_intersected_element (&tmpelement
,
2000 &list
->initial
.element
[s
], &newconstraint
))
2001 return add_end_constraint (list
, n
);
2002 free_element (&list
->initial
.element
[s
]);
2003 list
->initial
.element
[s
].type
= tmpelement
.type
;
2004 list
->initial
.element
[s
].list
= tmpelement
.list
;
2012 /* Add a constraint to an argument list, namely that the argument n is
2013 of a given list type. NULL stands for an impossible situation, i.e. a
2014 contradiction. Assumes a preceding add_required_constraint (list, n). */
2015 /* Memory effects: list is freed. The result is freshly allocated. */
2016 static struct format_arg_list
*
2017 add_listtype_constraint (struct format_arg_list
*list
, unsigned int n
,
2018 enum format_arg_type type
,
2019 struct format_arg_list
*sublist
)
2022 struct format_arg newconstraint
;
2023 struct format_arg tmpelement
;
2028 /* Through the previous add_required_constraint, we can assume
2029 list->initial.length >= n+1. */
2031 s
= initial_unshare (list
, n
);
2033 newconstraint
.presence
= FCT_OPTIONAL
;
2034 newconstraint
.type
= type
;
2035 newconstraint
.list
= sublist
;
2036 if (!make_intersected_element (&tmpelement
,
2037 &list
->initial
.element
[s
], &newconstraint
))
2038 return add_end_constraint (list
, n
);
2039 free_element (&list
->initial
.element
[s
]);
2040 list
->initial
.element
[s
].type
= tmpelement
.type
;
2041 list
->initial
.element
[s
].list
= tmpelement
.list
;
2049 /* ============= Subroutines used by the format string parser ============= */
2052 add_req_type_constraint (struct format_arg_list
**listp
,
2053 unsigned int position
, enum format_arg_type type
)
2055 *listp
= add_required_constraint (*listp
, position
);
2056 *listp
= add_type_constraint (*listp
, position
, type
);
2061 add_req_listtype_constraint (struct format_arg_list
**listp
,
2062 unsigned int position
, enum format_arg_type type
,
2063 struct format_arg_list
*sublist
)
2065 *listp
= add_required_constraint (*listp
, position
);
2066 *listp
= add_listtype_constraint (*listp
, position
, type
, sublist
);
2070 /* Create an endless repeated list whose elements are lists constrained
2072 /* Memory effects: sublist is freed. The result is freshly allocated. */
2073 static struct format_arg_list
*
2074 make_repeated_list_of_lists (struct format_arg_list
*sublist
)
2076 if (sublist
== NULL
)
2077 /* The list cannot have a single element. */
2078 return make_empty_list ();
2081 struct format_arg_list
*listlist
;
2084 (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
2086 listlist
->initial
.count
= 0;
2087 listlist
->initial
.allocated
= 0;
2088 listlist
->initial
.element
= NULL
;
2089 listlist
->initial
.length
= 0;
2090 listlist
->repeated
.count
= 1;
2091 listlist
->repeated
.allocated
= 1;
2092 listlist
->repeated
.element
=
2093 (struct format_arg
*) xmalloc (1 * sizeof (struct format_arg
));
2094 listlist
->repeated
.element
[0].repcount
= 1;
2095 listlist
->repeated
.element
[0].presence
= FCT_OPTIONAL
;
2096 listlist
->repeated
.element
[0].type
= FAT_LIST
;
2097 listlist
->repeated
.element
[0].list
= sublist
;
2098 listlist
->repeated
.length
= 1;
2100 VERIFY_LIST (listlist
);
2107 /* Create an endless repeated list which represents the union of a finite
2108 number of copies of L, each time shifted by period:
2112 L and (*^period L) and (*^{2 period} L)
2113 L and (*^period L) and (*^{2 period} L) and (*^{3 period} L)
2116 /* Memory effects: sublist is freed. The result is freshly allocated. */
2117 static struct format_arg_list
*
2118 make_repeated_list (struct format_arg_list
*sublist
, unsigned int period
)
2121 struct segment
*srcseg
;
2122 struct format_arg_list
*list
;
2123 unsigned int p
, n
, i
, si
, ti
, j
, sj
, tj
, splitindex
, newcount
;
2126 VERIFY_LIST (sublist
);
2128 ASSERT (period
> 0);
2130 if (sublist
->repeated
.count
== 0)
2132 /* L is a finite list. */
2134 if (sublist
->initial
.length
< period
)
2135 /* L and (*^period L) is a contradition, so we need to consider
2136 only 1 and 0 iterations. */
2137 return make_union_with_empty_list (sublist
);
2139 srcseg
= &sublist
->initial
;
2144 /* L is an infinite list. */
2145 /* p := lcm (period, period of L) */
2146 unsigned int Lp
= sublist
->repeated
.length
;
2147 unsigned int m
= period
/ gcd (period
, Lp
); /* = lcm(period,Lp) / Lp */
2149 unfold_loop (sublist
, m
);
2152 /* Concatenate the initial and the repeated segments into a single
2154 tmp
.count
= sublist
->initial
.count
+ sublist
->repeated
.count
;
2155 tmp
.allocated
= tmp
.count
;
2157 (struct format_arg
*)
2158 xmalloc (tmp
.allocated
* sizeof (struct format_arg
));
2159 for (i
= 0; i
< sublist
->initial
.count
; i
++)
2160 tmp
.element
[i
] = sublist
->initial
.element
[i
];
2161 for (j
= 0; j
< sublist
->repeated
.count
; i
++, j
++)
2162 tmp
.element
[i
] = sublist
->initial
.element
[j
];
2163 tmp
.length
= sublist
->initial
.length
+ sublist
->repeated
.length
;
2170 /* Example: n = 7, p = 2
2171 Let L = (A B C D E F G).
2174 L & L<<p = A B C&A D&B E&C F&D G&E
2175 L & L<<p & L<<2p = A B C&A D&B E&C&A F&D&B G&E&C
2176 ... = A B C&A D&B E&C&A F&D&B G&E&C&A
2178 Thus the result has an initial segment of length n - p and a period
2179 of p, and can be computed by floor(n/p) intersection operations.
2180 Or by a single incremental intersection operation, going from left
2183 list
= (struct format_arg_list
*) xmalloc (sizeof (struct format_arg_list
));
2184 list
->initial
.count
= 0;
2185 list
->initial
.allocated
= 0;
2186 list
->initial
.element
= NULL
;
2187 list
->initial
.length
= 0;
2188 list
->repeated
.count
= 0;
2189 list
->repeated
.allocated
= 0;
2190 list
->repeated
.element
= NULL
;
2191 list
->repeated
.length
= 0;
2194 for (i = 0; i < p; i++)
2195 list->initial.element[i] = srcseg->element[i];
2196 list->initial.element[0].presence = FCT_OPTIONAL; // union with empty list
2197 for (i = p, j = 0; i < n; i++, j++)
2198 list->initial.element[i] = srcseg->element[i] & list->initial.element[j];
2203 i
= 0, ti
= 0, si
= 0;
2206 unsigned int k
= MIN (srcseg
->element
[si
].repcount
- ti
, p
- i
);
2208 /* Ensure room in list->initial. */
2209 grow_initial_alloc (list
);
2210 copy_element (&list
->initial
.element
[list
->initial
.count
],
2211 &srcseg
->element
[si
]);
2212 list
->initial
.element
[list
->initial
.count
].repcount
= k
;
2213 list
->initial
.count
++;
2214 list
->initial
.length
+= k
;
2218 if (ti
== srcseg
->element
[si
].repcount
)
2225 ASSERT (list
->initial
.count
> 0);
2226 if (list
->initial
.element
[0].presence
== FCT_REQUIRED
)
2228 initial_splitelement (list
, 1);
2229 ASSERT (list
->initial
.element
[0].presence
== FCT_REQUIRED
);
2230 ASSERT (list
->initial
.element
[0].repcount
== 1);
2231 list
->initial
.element
[0].presence
= FCT_OPTIONAL
;
2234 j
= 0, tj
= 0, sj
= 0;
2238 MIN (srcseg
->element
[si
].repcount
- ti
,
2239 list
->initial
.element
[sj
].repcount
- tj
);
2241 /* Ensure room in list->initial. */
2242 grow_initial_alloc (list
);
2243 if (!make_intersected_element (&list
->initial
.element
[list
->initial
.count
],
2244 &srcseg
->element
[si
],
2245 &list
->initial
.element
[sj
]))
2247 if (list
->initial
.element
[list
->initial
.count
].presence
== FCT_REQUIRED
)
2249 /* Contradiction. Backtrack. */
2250 list
= backtrack_in_initial (list
);
2251 ASSERT (list
!= NULL
); /* at least the empty list is valid */
2256 /* The list ends here. */
2261 list
->initial
.element
[list
->initial
.count
].repcount
= k
;
2262 list
->initial
.count
++;
2263 list
->initial
.length
+= k
;
2267 if (ti
== srcseg
->element
[si
].repcount
)
2275 if (tj
== list
->initial
.element
[sj
].repcount
)
2282 ASSERT (list
->initial
.length
== n
);
2284 /* Add optional exit points at 0, period, 2*period etc.
2285 FIXME: Not sure this is correct in all cases. */
2286 for (i
= 0; i
< list
->initial
.length
; i
+= period
)
2288 si
= initial_unshare (list
, i
);
2289 list
->initial
.element
[si
].presence
= FCT_OPTIONAL
;
2294 /* Now split off the repeated part. */
2295 splitindex
= initial_splitelement (list
, n
- p
);
2296 newcount
= list
->initial
.count
- splitindex
;
2297 if (newcount
> list
->repeated
.allocated
)
2299 list
->repeated
.allocated
= newcount
;
2300 list
->repeated
.element
=
2301 (struct format_arg
*) xmalloc (newcount
* sizeof (struct format_arg
));
2303 for (i
= splitindex
, j
= 0; i
< n
; i
++, j
++)
2304 list
->repeated
.element
[j
] = list
->initial
.element
[i
];
2305 list
->repeated
.count
= newcount
;
2306 list
->repeated
.length
= p
;
2307 list
->initial
.count
= splitindex
;
2308 list
->initial
.length
= n
- p
;
2317 /* ================= Handling of format string directives ================= */
2319 /* Possible signatures of format directives. */
2320 static const enum format_arg_type I
[1] = { FAT_INTEGER_NULL
};
2321 static const enum format_arg_type II
[2] = {
2322 FAT_INTEGER_NULL
, FAT_INTEGER_NULL
2324 static const enum format_arg_type IIC
[3] = {
2325 FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_CHARACTER_NULL
2327 static const enum format_arg_type ICCI
[4] = {
2328 FAT_INTEGER_NULL
, FAT_CHARACTER_NULL
, FAT_CHARACTER_NULL
, FAT_INTEGER_NULL
2330 static const enum format_arg_type IIIC
[4] = {
2331 FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_CHARACTER_NULL
2333 static const enum format_arg_type IICCI
[5] = {
2334 FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_CHARACTER_NULL
, FAT_CHARACTER_NULL
,
2337 static const enum format_arg_type IIICC
[5] = {
2338 FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_CHARACTER_NULL
,
2341 static const enum format_arg_type IIIICCC
[7] = {
2342 FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_INTEGER_NULL
, FAT_INTEGER_NULL
,
2343 FAT_CHARACTER_NULL
, FAT_CHARACTER_NULL
, FAT_CHARACTER_NULL
2345 static const enum format_arg_type THREE
[3] = {
2346 FAT_CHARACTER_INTEGER_NULL
, FAT_CHARACTER_INTEGER_NULL
,
2347 FAT_CHARACTER_INTEGER_NULL
2351 /* Check the parameters. For V params, add the constraint to the argument
2352 list. Return false and fill in *invalid_reason if the format string is
2355 check_params (struct format_arg_list
**listp
,
2356 unsigned int paramcount
, struct param
*params
,
2357 unsigned int t_count
, const enum format_arg_type
*t_types
,
2358 unsigned int directives
, char **invalid_reason
)
2360 unsigned int orig_paramcount
= paramcount
;
2361 unsigned int orig_t_count
= t_count
;
2363 for (; paramcount
> 0 && t_count
> 0;
2364 params
++, paramcount
--, t_types
++, t_count
--)
2368 case FAT_CHARACTER_INTEGER_NULL
:
2370 case FAT_CHARACTER_NULL
:
2371 switch (params
->type
)
2373 case PT_NIL
: case PT_CHARACTER
: case PT_V
:
2375 case PT_INTEGER
: case PT_ARGCOUNT
:
2376 /* wrong param type */
2378 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");
2382 case FAT_INTEGER_NULL
:
2383 switch (params
->type
)
2385 case PT_NIL
: case PT_INTEGER
: case PT_ARGCOUNT
: case PT_V
:
2388 /* wrong param type */
2390 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");
2397 if (params
->type
== PT_V
)
2399 int position
= params
->value
;
2401 add_req_type_constraint (listp
, position
, *t_types
);
2405 for (; paramcount
> 0; params
++, paramcount
--)
2406 switch (params
->type
)
2410 case PT_CHARACTER
: case PT_INTEGER
: case PT_ARGCOUNT
:
2411 /* too many params for directive */
2413 xasprintf (ngettext ("In the directive number %u, too many parameters are given; expected at most %u parameter.",
2414 "In the directive number %u, too many parameters are given; expected at most %u parameters.",
2416 directives
, orig_t_count
);
2419 /* Force argument to be NIL. */
2421 int position
= params
->value
;
2424 struct format_arg_list
*empty_list
= make_empty_list ();
2425 add_req_listtype_constraint (listp
, position
,
2426 FAT_LIST
, empty_list
);
2427 free_list (empty_list
);
2437 /* ======================= The format string parser ======================= */
2439 /* Parse a piece of format string, until the matching terminating format
2440 directive is encountered.
2441 format is the remainder of the format string.
2442 position is the position in this argument list, if known, or -1 if unknown.
2443 list represents the argument list constraints at the current parse point.
2444 NULL stands for a contradiction.
2445 escape represents the union of the argument list constraints at all the
2446 currently pending FORMAT-UP-AND-OUT points. NULL stands for a contradiction
2448 All four are updated upon valid return.
2449 *separatorp is set to true if the parse terminated due to a ~; separator,
2450 more precisely to 2 if with colon, or to 1 if without colon.
2451 spec is the global struct spec.
2452 terminator is the directive that terminates this parse.
2453 separator specifies if ~; separators are allowed.
2454 If the format string is invalid, false is returned and *invalid_reason is
2455 set to an error message explaining why. */
2457 parse_upto (const char **formatp
,
2458 int *positionp
, struct format_arg_list
**listp
,
2459 struct format_arg_list
**escapep
, int *separatorp
,
2460 struct spec
*spec
, char terminator
, bool separator
,
2461 char **invalid_reason
)
2463 const char *format
= *formatp
;
2464 int position
= *positionp
;
2465 struct format_arg_list
*list
= *listp
;
2466 struct format_arg_list
*escape
= *escapep
;
2468 for (; *format
!= '\0'; )
2469 if (*format
++ == '~')
2471 bool colon_p
= false;
2472 bool atsign_p
= false;
2473 unsigned int paramcount
= 0;
2474 struct param
*params
= NULL
;
2476 /* Count number of directives. */
2479 /* Parse parameters. */
2482 enum param_type type
= PT_NIL
;
2485 if (c_isdigit (*format
))
2490 value
= 10 * value
+ (*format
- '0');
2493 while (c_isdigit (*format
));
2495 else if (*format
== '+' || *format
== '-')
2497 bool negative
= (*format
== '-');
2500 if (!c_isdigit (*format
))
2504 ? INVALID_UNTERMINATED_DIRECTIVE ()
2505 : xasprintf (_("In the directive number %u, '%c' is not followed by a digit."), spec
->directives
, format
[-1]));
2510 value
= 10 * value
+ (*format
- '0');
2513 while (c_isdigit (*format
));
2517 else if (*format
== '\'')
2519 type
= PT_CHARACTER
;
2521 if (*format
== '\0')
2523 *invalid_reason
= INVALID_UNTERMINATED_DIRECTIVE ();
2528 else if (*format
== 'V' || *format
== 'v')
2533 /* Consumes an argument. */
2537 else if (*format
== '#')
2545 xrealloc (params
, (paramcount
+ 1) * sizeof (struct param
));
2546 params
[paramcount
].type
= type
;
2547 params
[paramcount
].value
= value
;
2556 /* Parse modifiers. */
2564 else if (*format
== '@')
2573 /* Parse directive. */
2576 case 'A': case 'a': /* 22.3.4.1 FORMAT-ASCII */
2577 case 'S': case 's': /* 22.3.4.2 FORMAT-S-EXPRESSION */
2578 if (!check_params (&list
, paramcount
, params
, 4, IIIC
,
2579 spec
->directives
, invalid_reason
))
2582 add_req_type_constraint (&list
, position
++, FAT_OBJECT
);
2585 case 'C': case 'c': /* FORMAT-CHARACTER */
2586 if (!check_params (&list
, paramcount
, params
, 1, I
,
2587 spec
->directives
, invalid_reason
))
2590 || (paramcount
== 1 && params
[0].type
== PT_NIL
))
2592 add_req_type_constraint (&list
, position
++, FAT_CHARACTER
);
2595 case 'D': case 'd': /* 22.3.2.2 FORMAT-DECIMAL */
2596 case 'B': case 'b': /* 22.3.2.3 FORMAT-BINARY */
2597 case 'O': case 'o': /* 22.3.2.4 FORMAT-OCTAL */
2598 case 'X': case 'x': /* 22.3.2.5 FORMAT-HEXADECIMAL */
2599 if (!check_params (&list
, paramcount
, params
, 4, ICCI
,
2600 spec
->directives
, invalid_reason
))
2603 add_req_type_constraint (&list
, position
++, FAT_INTEGER
);
2606 case 'R': case 'r': /* 22.3.2.1 FORMAT-RADIX */
2607 if (!check_params (&list
, paramcount
, params
, 5, IICCI
,
2608 spec
->directives
, invalid_reason
))
2611 add_req_type_constraint (&list
, position
++, FAT_INTEGER
);
2614 case 'P': case 'p': /* 22.3.8.3 FORMAT-PLURAL */
2615 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2616 spec
->directives
, invalid_reason
))
2620 /* Go back by 1 argument. */
2625 add_req_type_constraint (&list
, position
++, FAT_OBJECT
);
2628 case 'F': case 'f': /* 22.3.3.1 FORMAT-FIXED-FLOAT */
2629 if (!check_params (&list
, paramcount
, params
, 5, IIICC
,
2630 spec
->directives
, invalid_reason
))
2633 add_req_type_constraint (&list
, position
++, FAT_REAL
);
2636 case 'E': case 'e': /* 22.3.3.2 FORMAT-EXPONENTIAL-FLOAT */
2637 case 'G': case 'g': /* 22.3.3.3 FORMAT-GENERAL-FLOAT */
2638 if (!check_params (&list
, paramcount
, params
, 7, IIIICCC
,
2639 spec
->directives
, invalid_reason
))
2642 add_req_type_constraint (&list
, position
++, FAT_REAL
);
2645 case '$': /* 22.3.3.4 FORMAT-DOLLARS-FLOAT */
2646 if (!check_params (&list
, paramcount
, params
, 4, IIIC
,
2647 spec
->directives
, invalid_reason
))
2650 add_req_type_constraint (&list
, position
++, FAT_REAL
);
2653 case 'I': case 'i': /* FORMAT-FIXED-FLOAT-COMPLEX */
2654 if (!check_params (&list
, paramcount
, params
, 5, IIICC
,
2655 spec
->directives
, invalid_reason
))
2658 add_req_type_constraint (&list
, position
++, FAT_COMPLEX
);
2661 case 'Y': case 'y': /* FORMAT-PRETTY */
2662 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2663 spec
->directives
, invalid_reason
))
2666 add_req_type_constraint (&list
, position
++, FAT_OBJECT
);
2669 case '%': /* 22.3.1.2 FORMAT-TERPRI */
2670 case '&': /* 22.3.1.3 FORMAT-FRESH-LINE */
2671 case '_': /* FORMAT-SPACE */
2672 case '/': /* FORMAT-TAB */
2673 case '|': /* 22.3.1.4 FORMAT-PAGE */
2674 case '~': /* 22.3.1.5 FORMAT-TILDE */
2675 if (!check_params (&list
, paramcount
, params
, 1, I
,
2676 spec
->directives
, invalid_reason
))
2680 case '!': /* FORMAT-FORCE-OUTPUT */
2681 case '\n': /* 22.3.9.3 #\Newline */
2682 case 'Q': case 'q': /* FORMAT-IMPLEMENTATION */
2683 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2684 spec
->directives
, invalid_reason
))
2688 case 'T': case 't': /* FORMAT-TABULATE */
2689 if (!check_params (&list
, paramcount
, params
, 3, IIC
,
2690 spec
->directives
, invalid_reason
))
2694 case '*': /* 22.3.7.1 FORMAT-GOTO */
2695 if (!check_params (&list
, paramcount
, params
, 1, I
,
2696 spec
->directives
, invalid_reason
))
2699 int n
; /* value of first parameter */
2701 || (paramcount
>= 1 && params
[0].type
== PT_NIL
))
2702 n
= (atsign_p
? 0 : 1);
2703 else if (paramcount
>= 1 && params
[0].type
== PT_INTEGER
)
2704 n
= params
[0].value
;
2707 /* Unknown argument, leads to an unknown position. */
2713 /* invalid argument */
2715 xasprintf (_("In the directive number %u, the argument %d is negative."), spec
->directives
, n
);
2720 /* Absolute goto. */
2725 /* Backward goto. */
2748 case '?': case 'K': case 'k': /* 22.3.7.6 FORMAT-INDIRECTION */
2749 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2750 spec
->directives
, invalid_reason
))
2753 add_req_type_constraint (&list
, position
++, FAT_FORMATSTRING
);
2759 struct format_arg_list
*sublist
= make_unconstrained_list ();
2760 add_req_listtype_constraint (&list
, position
++,
2762 free_list (sublist
);
2766 case '(': /* 22.3.8.1 FORMAT-CASE-CONVERSION */
2767 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2768 spec
->directives
, invalid_reason
))
2771 *positionp
= position
;
2775 if (!parse_upto (formatp
, positionp
, listp
, escapep
,
2776 NULL
, spec
, ')', false,
2781 position
= *positionp
;
2786 case ')': /* 22.3.8.2 FORMAT-CASE-CONVERSION-END */
2787 if (terminator
!= ')')
2790 xasprintf (_("Found '~%c' without matching '~%c'."), ')', '(');
2793 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2794 spec
->directives
, invalid_reason
))
2797 *positionp
= position
;
2802 case '[': /* 22.3.7.2 FORMAT-CONDITIONAL */
2803 if (atsign_p
&& colon_p
)
2806 xasprintf (_("In the directive number %u, both the @ and the : modifiers are given."), spec
->directives
);
2811 struct format_arg_list
*nil_list
;
2812 struct format_arg_list
*union_list
;
2814 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2815 spec
->directives
, invalid_reason
))
2821 /* First alternative: argument is NIL. */
2822 nil_list
= (list
!= NULL
? copy_list (list
) : NULL
);
2825 struct format_arg_list
*empty_list
= make_empty_list ();
2826 add_req_listtype_constraint (&nil_list
, position
,
2827 FAT_LIST
, empty_list
);
2828 free_list (empty_list
);
2831 /* Second alternative: use sub-format. */
2833 int sub_position
= position
;
2834 struct format_arg_list
*sub_list
=
2835 (list
!= NULL
? copy_list (list
) : NULL
);
2836 if (!parse_upto (formatp
, &sub_position
, &sub_list
, escapep
,
2837 NULL
, spec
, ']', false,
2840 if (sub_list
!= NULL
)
2844 if (sub_position
== position
+ 1)
2845 /* new position is branch independent */
2846 position
= position
+ 1;
2848 /* new position is branch dependent */
2855 position
= position
+ 1;
2857 union_list
= union (nil_list
, sub_list
);
2870 struct format_arg_list
*union_list
;
2872 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
2873 spec
->directives
, invalid_reason
))
2877 add_req_type_constraint (&list
, position
++, FAT_OBJECT
);
2881 union_position
= -2;
2884 /* First alternative. */
2886 int sub_position
= position
;
2887 struct format_arg_list
*sub_list
=
2888 (list
!= NULL
? copy_list (list
) : NULL
);
2889 int sub_separator
= 0;
2892 struct format_arg_list
*empty_list
= make_empty_list ();
2893 add_req_listtype_constraint (&sub_list
, position
- 1,
2894 FAT_LIST
, empty_list
);
2895 free_list (empty_list
);
2897 if (!parse_upto (formatp
, &sub_position
, &sub_list
, escapep
,
2898 &sub_separator
, spec
, ']', true,
2904 xasprintf (_("In the directive number %u, '~:[' is not followed by two clauses, separated by '~;'."), spec
->directives
);
2907 if (sub_list
!= NULL
)
2908 union_position
= sub_position
;
2909 union_list
= union (union_list
, sub_list
);
2912 /* Second alternative. */
2914 int sub_position
= position
;
2915 struct format_arg_list
*sub_list
=
2916 (list
!= NULL
? copy_list (list
) : NULL
);
2917 if (!parse_upto (formatp
, &sub_position
, &sub_list
, escapep
,
2918 NULL
, spec
, ']', false,
2921 if (sub_list
!= NULL
)
2923 if (union_position
== -2)
2924 union_position
= sub_position
;
2925 else if (sub_position
< 0
2926 || sub_position
!= union_position
)
2927 union_position
= -1;
2929 union_list
= union (union_list
, sub_list
);
2935 if (union_position
!= -2)
2936 position
= union_position
;
2945 struct format_arg_list
*union_list
;
2946 bool last_alternative
;
2948 if (!check_params (&list
, paramcount
, params
, 1, I
,
2949 spec
->directives
, invalid_reason
))
2952 /* If there was no first parameter, an argument is consumed. */
2954 if (!(paramcount
>= 1 && params
[0].type
!= PT_NIL
))
2957 arg_position
= position
;
2958 add_req_type_constraint (&list
, position
++, FAT_OBJECT
);
2964 union_position
= -2;
2966 last_alternative
= false;
2969 /* Next alternative. */
2970 int sub_position
= position
;
2971 struct format_arg_list
*sub_list
=
2972 (list
!= NULL
? copy_list (list
) : NULL
);
2973 int sub_separator
= 0;
2974 if (!parse_upto (formatp
, &sub_position
, &sub_list
, escapep
,
2975 &sub_separator
, spec
, ']', !last_alternative
,
2978 /* If this alternative is chosen, the argument arg_position
2979 is an integer, namely the index of this alternative. */
2980 if (!last_alternative
&& arg_position
>= 0)
2981 add_req_type_constraint (&sub_list
, arg_position
,
2983 if (sub_list
!= NULL
)
2985 if (union_position
== -2)
2986 union_position
= sub_position
;
2987 else if (sub_position
< 0
2988 || sub_position
!= union_position
)
2989 union_position
= -1;
2991 union_list
= union (union_list
, sub_list
);
2992 if (sub_separator
== 2)
2993 last_alternative
= true;
2997 if (!last_alternative
)
2999 /* An implicit default alternative. */
3000 if (union_position
== -2)
3001 union_position
= position
;
3002 else if (position
< 0 || position
!= union_position
)
3003 union_position
= -1;
3005 union_list
= union (union_list
, copy_list (list
));
3011 if (union_position
!= -2)
3012 position
= union_position
;
3019 case ']': /* 22.3.7.3 FORMAT-CONDITIONAL-END */
3020 if (terminator
!= ']')
3023 xasprintf (_("Found '~%c' without matching '~%c'."), ']', '[');
3026 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
3027 spec
->directives
, invalid_reason
))
3030 *positionp
= position
;
3035 case '{': /* 22.3.7.4 FORMAT-ITERATION */
3036 if (!check_params (&list
, paramcount
, params
, 1, I
,
3037 spec
->directives
, invalid_reason
))
3041 int sub_position
= 0;
3042 struct format_arg_list
*sub_list
= make_unconstrained_list ();
3043 struct format_arg_list
*sub_escape
= NULL
;
3044 struct spec sub_spec
;
3045 sub_spec
.directives
= 0;
3046 sub_spec
.list
= sub_list
;
3047 if (!parse_upto (formatp
, &sub_position
, &sub_list
, &sub_escape
,
3048 NULL
, &sub_spec
, '}', false,
3051 spec
->directives
+= sub_spec
.directives
;
3053 /* If the sub-formatstring is empty, except for the terminating
3054 ~} directive, a formatstring argument is consumed. */
3055 if (*format
== '~' && sub_spec
.directives
== 1)
3057 add_req_type_constraint (&list
, position
++, FAT_FORMATSTRING
);
3061 /* Each iteration uses a new sublist. */
3062 struct format_arg_list
*listlist
;
3064 /* ~{ catches ~^. */
3065 sub_list
= union (sub_list
, sub_escape
);
3067 listlist
= make_repeated_list_of_lists (sub_list
);
3069 sub_list
= listlist
;
3073 /* Each iteration's arguments are all concatenated in a
3075 struct format_arg_list
*looplist
;
3077 /* FIXME: This is far from correct. Test cases:
3083 abc~{~D~^~C~}~:*~{~S~^~D~}
3086 /* ~{ catches ~^. */
3087 sub_list
= union (sub_list
, sub_escape
);
3089 if (sub_list
== NULL
)
3090 looplist
= make_empty_list ();
3092 if (sub_position
< 0 || sub_position
== 0)
3093 /* Too hard to track the possible argument types
3094 when the iteration is performed 2 times or more.
3095 So be satisfied with the constraints of executing
3096 the iteration 1 or 0 times. */
3097 looplist
= make_union_with_empty_list (sub_list
);
3099 looplist
= make_repeated_list (sub_list
, sub_position
);
3101 sub_list
= looplist
;
3106 /* All remaining arguments are used. */
3107 if (list
!= NULL
&& position
>= 0)
3109 shift_list (sub_list
, position
);
3110 list
= make_intersected_list (list
, sub_list
);
3116 /* The argument is a list. */
3118 add_req_listtype_constraint (&list
, position
++,
3119 FAT_LIST
, sub_list
);
3125 case '}': /* 22.3.7.5 FORMAT-ITERATION-END */
3126 if (terminator
!= '}')
3129 xasprintf (_("Found '~%c' without matching '~%c'."), '}', '{');
3132 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
3133 spec
->directives
, invalid_reason
))
3136 *positionp
= position
;
3141 case '^': /* 22.3.9.2 FORMAT-UP-AND-OUT */
3142 if (!check_params (&list
, paramcount
, params
, 3, THREE
,
3143 spec
->directives
, invalid_reason
))
3145 if (position
>= 0 && list
!= NULL
&& is_required (list
, position
))
3146 /* This ~^ can never be executed. Ignore it. */
3150 struct format_arg_list
*this_escape
= copy_list (list
);
3152 this_escape
= add_end_constraint (this_escape
, position
);
3153 escape
= union (escape
, this_escape
);
3156 list
= add_required_constraint (list
, position
);
3159 case ';': /* 22.3.9.1 FORMAT-SEPARATOR */
3163 xasprintf (_("In the directive number %u, '~;' is used in an invalid position."), spec
->directives
);
3166 if (terminator
== '>')
3168 if (!check_params (&list
, paramcount
, params
, 1, I
,
3169 spec
->directives
, invalid_reason
))
3174 if (!check_params (&list
, paramcount
, params
, 0, NULL
,
3175 spec
->directives
, invalid_reason
))
3179 *positionp
= position
;
3182 *separatorp
= (colon_p
? 2 : 1);
3189 ? INVALID_UNTERMINATED_DIRECTIVE ()
3190 : INVALID_CONVERSION_SPECIFIER (spec
->directives
, *format
));
3198 *positionp
= position
;
3201 if (terminator
!= '\0')
3204 xasprintf (_("Found '~%c' without matching '~%c'."), terminator
- 1, terminator
);
3211 /* ============== Top level format string handling functions ============== */
3214 format_parse (const char *format
, bool translated
, char **invalid_reason
)
3217 struct spec
*result
;
3219 struct format_arg_list
*escape
;
3221 spec
.directives
= 0;
3222 spec
.list
= make_unconstrained_list ();
3225 if (!parse_upto (&format
, &position
, &spec
.list
, &escape
,
3226 NULL
, &spec
, '\0', false,
3228 /* Invalid format string. */
3231 /* Catch ~^ here. */
3232 spec
.list
= union (spec
.list
, escape
);
3234 if (spec
.list
== NULL
)
3236 /* Contradictory argument type information. */
3238 xstrdup (_("The string refers to some argument in incompatible ways."));
3242 /* Normalize the result. */
3243 normalize_list (spec
.list
);
3245 result
= (struct spec
*) xmalloc (sizeof (struct spec
));
3251 format_free (void *descr
)
3253 struct spec
*spec
= (struct spec
*) descr
;
3255 free_list (spec
->list
);
3259 format_get_number_of_directives (void *descr
)
3261 struct spec
*spec
= (struct spec
*) descr
;
3263 return spec
->directives
;
3267 format_check (void *msgid_descr
, void *msgstr_descr
, bool equality
,
3268 formatstring_error_logger_t error_logger
,
3269 const char *pretty_msgstr
)
3271 struct spec
*spec1
= (struct spec
*) msgid_descr
;
3272 struct spec
*spec2
= (struct spec
*) msgstr_descr
;
3277 if (!equal_list (spec1
->list
, spec2
->list
))
3280 error_logger (_("format specifications in 'msgid' and '%s' are not equivalent"),
3287 struct format_arg_list
*intersection
=
3288 make_intersected_list (copy_list (spec1
->list
),
3289 copy_list (spec2
->list
));
3291 if (!(intersection
!= NULL
3292 && (normalize_list (intersection
),
3293 equal_list (intersection
, spec2
->list
))))
3296 error_logger (_("format specifications in '%s' are not a subset of those in 'msgid'"),
3306 struct formatstring_parser formatstring_scheme
=
3310 format_get_number_of_directives
,
3315 /* ============================= Testing code ============================= */
3321 /* Test program: Print the argument list specification returned by
3322 format_parse for strings read from standard input. */
3325 #include "getline.h"
3327 static void print_list (struct format_arg_list
*list
);
3330 print_element (struct format_arg
*element
)
3332 switch (element
->presence
)
3343 switch (element
->type
)
3348 case FAT_CHARACTER_INTEGER_NULL
:
3351 case FAT_CHARACTER_NULL
:
3357 case FAT_INTEGER_NULL
:
3370 print_list (element
->list
);
3372 case FAT_FORMATSTRING
:
3381 print_list (struct format_arg_list
*list
)
3387 for (i
= 0; i
< list
->initial
.count
; i
++)
3388 for (j
= 0; j
< list
->initial
.element
[i
].repcount
; j
++)
3392 print_element (&list
->initial
.element
[i
]);
3395 if (list
->repeated
.count
> 0)
3398 for (i
= 0; i
< list
->repeated
.count
; i
++)
3399 for (j
= 0; j
< list
->repeated
.element
[i
].repcount
; j
++)
3402 print_element (&list
->repeated
.element
[i
]);
3410 format_print (void *descr
)
3412 struct spec
*spec
= (struct spec
*) descr
;
3420 print_list (spec
->list
);
3429 size_t line_size
= 0;
3431 char *invalid_reason
;
3434 line_len
= getline (&line
, &line_size
, stdin
);
3437 if (line_len
> 0 && line
[line_len
- 1] == '\n')
3438 line
[--line_len
] = '\0';
3440 invalid_reason
= NULL
;
3441 descr
= format_parse (line
, false, &invalid_reason
);
3443 format_print (descr
);
3446 printf ("%s\n", invalid_reason
);
3448 free (invalid_reason
);
3456 * For Emacs M-x compile
3458 * 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-scheme.c ../lib/libgettextlib.la"