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 int compare_wordlists(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(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 void mark_attr_ends(paragraph
*sourceform
) {
148 for (p
= sourceform
; p
; p
= p
->next
) {
150 for (w
= p
->words
; w
; w
= w
->next
) {
151 if (isattr(w
->type
)) {
152 int before
= (wp
&& isattr(wp
->type
) &&
153 sameattr(wp
->type
, w
->type
));
154 int after
= (w
->next
&& isattr(w
->next
->type
) &&
155 sameattr(w
->next
->type
, w
->type
));
157 (after ? attr_Always
: attr_Last
) :
158 (after ? attr_First
: attr_Only
));
165 wrappedline
*wrap_para(word
*text
, int width
, int subsequentwidth
,
166 int (*widthfn
)(word
*)) {
167 wrappedline
*head
= NULL
, **ptr
= &head
;
168 int nwords
, wordsize
;
179 * Break the line up into wrappable components.
181 nwords
= wordsize
= 0;
184 if (nwords
>= wordsize
) {
185 wordsize
= nwords
+ 64;
186 wrapwords
= srealloc(wrapwords
, wordsize
* sizeof(*wrapwords
));
188 wrapwords
[nwords
].width
= 0;
189 wrapwords
[nwords
].begin
= text
;
191 wrapwords
[nwords
].width
+= widthfn(text
);
192 wrapwords
[nwords
].end
= text
->next
;
193 if (text
->next
&& (text
->next
->type
== word_WhiteSpace
||
194 text
->next
->type
== word_EmphSpace
||
199 if (text
&& text
->next
&& (text
->next
->type
== word_WhiteSpace
||
200 text
->next
->type
== word_EmphSpace
)) {
201 wrapwords
[nwords
].spacewidth
= widthfn(text
->next
);
204 wrapwords
[nwords
].spacewidth
= 0;
212 * Perform the dynamic wrapping algorithm: work backwards from
213 * nwords-1, determining the optimal wrapping for each terminal
214 * subsequence of the paragraph.
216 for (i
= nwords
; i
-- ;) {
220 int linelen
= 0, spacewidth
= 0;
222 int thiswidth
= (i
== 0 ? width
: subsequentwidth
);
226 while (i
+j
< nwords
) {
228 * See what happens if we put j+1 words on this line.
232 linelen
+= spacewidth
+ wrapwords
[i
+j
].width
;
233 spacewidth
= wrapwords
[i
+j
].spacewidth
;
235 if (linelen
> thiswidth
) {
237 * If we're over the width limit, abandon ship,
238 * _unless_ there is no best-effort yet (which will
239 * only happen if the first word is too long all by
247 * Special case: if we're at the very end of the
248 * paragraph, we don't score penalty points for the
249 * white space left on the line.
253 cost
= (thiswidth
-linelen
) * (thiswidth
-linelen
);
254 cost
+= wrapwords
[i
+j
].cost
;
257 * We compare bestcost >= cost, not bestcost > cost,
258 * because in cases where the costs are identical we
259 * want to try to look like the greedy algorithm,
260 * because readers are likely to have spent a lot of
261 * time looking at greedy-wrapped paragraphs and
262 * there's no point violating the Principle of Least
263 * Surprise if it doesn't actually gain anything.
265 if (best
< 0 || bestcost
>= cost
) {
271 * Now we know the optimal answer for this terminal
272 * subsequence, so put it in wrapwords.
274 wrapwords
[i
].cost
= bestcost
;
275 wrapwords
[i
].nwords
= best
;
279 * We've wrapped the paragraph. Now build the output
280 * `wrappedline' list.
284 wrappedline
*w
= mknew(wrappedline
);
289 n
= wrapwords
[i
].nwords
;
290 w
->begin
= wrapwords
[i
].begin
;
291 w
->end
= wrapwords
[i
+n
-1].end
;
294 * Count along the words to find nspaces and shortfall.
297 w
->shortfall
= width
;
298 for (j
= 0; j
< n
; j
++) {
299 w
->shortfall
-= wrapwords
[i
+j
].width
;
300 if (j
< n
-1 && wrapwords
[i
+j
].spacewidth
) {
302 w
->shortfall
-= wrapwords
[i
+j
].spacewidth
;
313 void wrap_free(wrappedline
*w
) {
315 wrappedline
*t
= w
->next
;