2 * misc.c: miscellaneous useful items
16 s
= mknew(struct stackTag
);
24 void stk_free(stack s
) {
29 void stk_push(stack s
, void *item
) {
30 if (s
->size
<= s
->sp
) {
32 s
->data
= resize(s
->data
, s
->size
);
34 s
->data
[s
->sp
++] = item
;
37 void *stk_pop(stack s
) {
39 return s
->data
[--s
->sp
];
44 void *stk_top(stack s
) {
46 return s
->data
[s
->sp
-1];
52 * Small routines to amalgamate a string from an input source.
54 const rdstring empty_rdstring
= {0, 0, NULL
};
55 const rdstringc empty_rdstringc
= {0, 0, NULL
};
57 void rdadd(rdstring
*rs
, wchar_t c
) {
58 if (rs
->pos
>= rs
->size
-1) {
59 rs
->size
= rs
->pos
+ 128;
60 rs
->text
= resize(rs
->text
, rs
->size
);
62 rs
->text
[rs
->pos
++] = c
;
63 rs
->text
[rs
->pos
] = 0;
65 void rdadds(rdstring
*rs
, wchar_t *p
) {
67 if (rs
->pos
>= rs
->size
- len
) {
68 rs
->size
= rs
->pos
+ len
+ 128;
69 rs
->text
= resize(rs
->text
, rs
->size
);
71 ustrcpy(rs
->text
+ rs
->pos
, p
);
74 wchar_t *rdtrim(rdstring
*rs
) {
75 rs
->text
= resize(rs
->text
, rs
->pos
+ 1);
79 void rdaddc(rdstringc
*rs
, char c
) {
80 if (rs
->pos
>= rs
->size
-1) {
81 rs
->size
= rs
->pos
+ 128;
82 rs
->text
= resize(rs
->text
, rs
->size
);
84 rs
->text
[rs
->pos
++] = c
;
85 rs
->text
[rs
->pos
] = 0;
87 void rdaddsc(rdstringc
*rs
, char *p
) {
89 if (rs
->pos
>= rs
->size
- len
) {
90 rs
->size
= rs
->pos
+ len
+ 128;
91 rs
->text
= resize(rs
->text
, rs
->size
);
93 strcpy(rs
->text
+ rs
->pos
, p
);
96 char *rdtrimc(rdstringc
*rs
) {
97 rs
->text
= resize(rs
->text
, rs
->pos
+ 1);
101 static int compare_wordlists_literally(word
*a
, word
*b
) {
104 if (a
->type
!= b
->type
)
105 return (a
->type
< b
->type ?
-1 : +1); /* FIXME? */
107 if ((t
!= word_Normal
&& t
!= word_Code
&&
108 t
!= word_WeakCode
&& t
!= word_Emph
) ||
111 if (a
->text
&& b
->text
) {
112 c
= ustricmp(a
->text
, b
->text
);
116 c
= compare_wordlists_literally(a
->alt
, b
->alt
);
122 wchar_t *ap
= a
->text
, *bp
= b
->text
;
124 wchar_t ac
= utolower(*ap
), bc
= utolower(*bp
);
126 return (ac
< bc ?
-1 : +1);
127 if (!*++ap
&& a
->next
&& a
->next
->type
== t
&& !a
->next
->alt
)
128 a
= a
->next
, ap
= a
->text
;
129 if (!*++bp
&& b
->next
&& b
->next
->type
== t
&& !b
->next
->alt
)
130 b
= b
->next
, bp
= b
->text
;
133 return (*ap ?
+1 : -1);
140 return (a ?
+1 : -1);
145 int compare_wordlists(word
*a
, word
*b
) {
147 * First we compare only the alphabetic content of the word
148 * lists, with case not a factor. If that comes out equal,
149 * _then_ we compare the word lists literally.
159 pos
[0].i
= pos
[1].i
= 0;
163 * Find the next alphabetic character in each word list.
167 for (k
= 0; k
< 2; k
++) {
169 * Advance until we hit either an alphabetic character
170 * or the end of the word list.
174 /* End of word list. */
177 } else if (!pos
[k
].w
->text
|| !pos
[k
].w
->text
[pos
[k
].i
]) {
178 /* No characters remaining in this word; move on. */
179 pos
[k
].w
= pos
[k
].w
->next
;
181 } else if (!uisalpha(pos
[k
].w
->text
[pos
[k
].i
])) {
182 /* This character isn't alphabetic; move on. */
185 /* We have an alphabetic! Lowercase it and continue. */
186 pos
[k
].c
= utolower(pos
[k
].w
->text
[pos
[k
].i
]);
192 if (pos
[0].c
< pos
[1].c
)
194 else if (pos
[0].c
> pos
[1].c
)
198 break; /* they're equal */
205 * If we reach here, the strings were alphabetically equal, so
206 * compare in more detail.
208 return compare_wordlists_literally(a
, b
);
211 void mark_attr_ends(paragraph
*sourceform
) {
214 for (p
= sourceform
; p
; p
= p
->next
) {
216 for (w
= p
->words
; w
; w
= w
->next
) {
217 if (isattr(w
->type
)) {
218 int before
= (wp
&& isattr(wp
->type
) &&
219 sameattr(wp
->type
, w
->type
));
220 int after
= (w
->next
&& isattr(w
->next
->type
) &&
221 sameattr(w
->next
->type
, w
->type
));
223 (after ? attr_Always
: attr_Last
) :
224 (after ? attr_First
: attr_Only
));
231 wrappedline
*wrap_para(word
*text
, int width
, int subsequentwidth
,
232 int (*widthfn
)(word
*)) {
233 wrappedline
*head
= NULL
, **ptr
= &head
;
234 int nwords
, wordsize
;
245 * Break the line up into wrappable components.
247 nwords
= wordsize
= 0;
250 if (nwords
>= wordsize
) {
251 wordsize
= nwords
+ 64;
252 wrapwords
= srealloc(wrapwords
, wordsize
* sizeof(*wrapwords
));
254 wrapwords
[nwords
].width
= 0;
255 wrapwords
[nwords
].begin
= text
;
257 wrapwords
[nwords
].width
+= widthfn(text
);
258 wrapwords
[nwords
].end
= text
->next
;
259 if (text
->next
&& (text
->next
->type
== word_WhiteSpace
||
260 text
->next
->type
== word_EmphSpace
||
265 if (text
&& text
->next
&& (text
->next
->type
== word_WhiteSpace
||
266 text
->next
->type
== word_EmphSpace
)) {
267 wrapwords
[nwords
].spacewidth
= widthfn(text
->next
);
270 wrapwords
[nwords
].spacewidth
= 0;
278 * Perform the dynamic wrapping algorithm: work backwards from
279 * nwords-1, determining the optimal wrapping for each terminal
280 * subsequence of the paragraph.
282 for (i
= nwords
; i
-- ;) {
286 int linelen
= 0, spacewidth
= 0;
288 int thiswidth
= (i
== 0 ? width
: subsequentwidth
);
292 while (i
+j
< nwords
) {
294 * See what happens if we put j+1 words on this line.
298 linelen
+= spacewidth
+ wrapwords
[i
+j
].width
;
299 spacewidth
= wrapwords
[i
+j
].spacewidth
;
301 if (linelen
> thiswidth
) {
303 * If we're over the width limit, abandon ship,
304 * _unless_ there is no best-effort yet (which will
305 * only happen if the first word is too long all by
313 * Special case: if we're at the very end of the
314 * paragraph, we don't score penalty points for the
315 * white space left on the line.
319 cost
= (thiswidth
-linelen
) * (thiswidth
-linelen
);
320 cost
+= wrapwords
[i
+j
].cost
;
323 * We compare bestcost >= cost, not bestcost > cost,
324 * because in cases where the costs are identical we
325 * want to try to look like the greedy algorithm,
326 * because readers are likely to have spent a lot of
327 * time looking at greedy-wrapped paragraphs and
328 * there's no point violating the Principle of Least
329 * Surprise if it doesn't actually gain anything.
331 if (best
< 0 || bestcost
>= cost
) {
337 * Now we know the optimal answer for this terminal
338 * subsequence, so put it in wrapwords.
340 wrapwords
[i
].cost
= bestcost
;
341 wrapwords
[i
].nwords
= best
;
345 * We've wrapped the paragraph. Now build the output
346 * `wrappedline' list.
350 wrappedline
*w
= mknew(wrappedline
);
355 n
= wrapwords
[i
].nwords
;
356 w
->begin
= wrapwords
[i
].begin
;
357 w
->end
= wrapwords
[i
+n
-1].end
;
360 * Count along the words to find nspaces and shortfall.
363 w
->shortfall
= width
;
364 for (j
= 0; j
< n
; j
++) {
365 w
->shortfall
-= wrapwords
[i
+j
].width
;
366 if (j
< n
-1 && wrapwords
[i
+j
].spacewidth
) {
368 w
->shortfall
-= wrapwords
[i
+j
].spacewidth
;
379 void wrap_free(wrappedline
*w
) {
381 wrappedline
*t
= w
->next
;