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
];
45 * Small routines to amalgamate a string from an input source.
47 const rdstring empty_rdstring
= {0, 0, NULL
};
48 const rdstringc empty_rdstringc
= {0, 0, NULL
};
50 void rdadd(rdstring
*rs
, wchar_t c
) {
51 if (rs
->pos
>= rs
->size
-1) {
52 rs
->size
= rs
->pos
+ 128;
53 rs
->text
= resize(rs
->text
, rs
->size
);
55 rs
->text
[rs
->pos
++] = c
;
56 rs
->text
[rs
->pos
] = 0;
58 void rdadds(rdstring
*rs
, wchar_t *p
) {
60 if (rs
->pos
>= rs
->size
- len
) {
61 rs
->size
= rs
->pos
+ len
+ 128;
62 rs
->text
= resize(rs
->text
, rs
->size
);
64 ustrcpy(rs
->text
+ rs
->pos
, p
);
67 wchar_t *rdtrim(rdstring
*rs
) {
68 rs
->text
= resize(rs
->text
, rs
->pos
+ 1);
72 void rdaddc(rdstringc
*rs
, char c
) {
73 if (rs
->pos
>= rs
->size
-1) {
74 rs
->size
= rs
->pos
+ 128;
75 rs
->text
= resize(rs
->text
, rs
->size
);
77 rs
->text
[rs
->pos
++] = c
;
78 rs
->text
[rs
->pos
] = 0;
80 void rdaddsc(rdstringc
*rs
, char *p
) {
82 if (rs
->pos
>= rs
->size
- len
) {
83 rs
->size
= rs
->pos
+ len
+ 128;
84 rs
->text
= resize(rs
->text
, rs
->size
);
86 strcpy(rs
->text
+ rs
->pos
, p
);
89 char *rdtrimc(rdstringc
*rs
) {
90 rs
->text
= resize(rs
->text
, rs
->pos
+ 1);
94 int compare_wordlists(word
*a
, word
*b
) {
97 if (a
->type
!= b
->type
)
98 return (a
->type
< b
->type ?
-1 : +1); /* FIXME? */
100 if ((t
!= word_Normal
&& t
!= word_Code
&&
101 t
!= word_WeakCode
&& t
!= word_Emph
) ||
104 if (a
->text
&& b
->text
) {
105 c
= ustricmp(a
->text
, b
->text
);
109 c
= compare_wordlists(a
->alt
, b
->alt
);
115 wchar_t *ap
= a
->text
, *bp
= b
->text
;
117 wchar_t ac
= utolower(*ap
), bc
= utolower(*bp
);
119 return (ac
< bc ?
-1 : +1);
120 if (!*++ap
&& a
->next
&& a
->next
->type
== t
&& !a
->next
->alt
)
121 a
= a
->next
, ap
= a
->text
;
122 if (!*++bp
&& b
->next
&& b
->next
->type
== t
&& !b
->next
->alt
)
123 b
= b
->next
, bp
= b
->text
;
126 return (*ap ?
+1 : -1);
133 return (a ?
+1 : -1);
138 void mark_attr_ends(paragraph
*sourceform
) {
141 for (p
= sourceform
; p
; p
= p
->next
) {
143 for (w
= p
->words
; w
; w
= w
->next
) {
144 if (isattr(w
->type
)) {
145 int before
= (wp
&& isattr(wp
->type
) &&
146 sameattr(wp
->type
, w
->type
));
147 int after
= (w
->next
&& isattr(w
->next
->type
) &&
148 sameattr(w
->next
->type
, w
->type
));
150 (after ? attr_Always
: attr_Last
) :
151 (after ? attr_First
: attr_Only
));
158 wrappedline
*wrap_para(word
*text
, int width
, int subsequentwidth
,
159 int (*widthfn
)(word
*)) {
160 wrappedline
*head
= NULL
, **ptr
= &head
;
161 int nwords
, wordsize
;
172 * Break the line up into wrappable components.
174 nwords
= wordsize
= 0;
177 if (nwords
>= wordsize
) {
178 wordsize
= nwords
+ 64;
179 wrapwords
= srealloc(wrapwords
, wordsize
* sizeof(*wrapwords
));
181 wrapwords
[nwords
].width
= 0;
182 wrapwords
[nwords
].begin
= text
;
184 wrapwords
[nwords
].width
+= widthfn(text
);
185 wrapwords
[nwords
].end
= text
->next
;
186 if (text
->next
&& (text
->next
->type
== word_WhiteSpace
||
187 text
->next
->type
== word_EmphSpace
||
192 if (text
&& text
->next
&& (text
->next
->type
== word_WhiteSpace
||
193 text
->next
->type
== word_EmphSpace
)) {
194 wrapwords
[nwords
].spacewidth
= widthfn(text
->next
);
197 wrapwords
[nwords
].spacewidth
= 0;
205 * Perform the dynamic wrapping algorithm: work backwards from
206 * nwords-1, determining the optimal wrapping for each terminal
207 * subsequence of the paragraph.
209 for (i
= nwords
; i
-- ;) {
213 int linelen
= 0, spacewidth
= 0;
215 int thiswidth
= (i
== 0 ? width
: subsequentwidth
);
219 while (i
+j
< nwords
) {
221 * See what happens if we put j+1 words on this line.
225 linelen
+= spacewidth
+ wrapwords
[i
+j
].width
;
226 spacewidth
= wrapwords
[i
+j
].spacewidth
;
228 if (linelen
> thiswidth
) {
230 * If we're over the width limit, abandon ship,
231 * _unless_ there is no best-effort yet (which will
232 * only happen if the first word is too long all by
240 * Special case: if we're at the very end of the
241 * paragraph, we don't score penalty points for the
242 * white space left on the line.
246 cost
= (thiswidth
-linelen
) * (thiswidth
-linelen
);
247 cost
+= wrapwords
[i
+j
].cost
;
250 * We compare bestcost >= cost, not bestcost > cost,
251 * because in cases where the costs are identical we
252 * want to try to look like the greedy algorithm,
253 * because readers are likely to have spent a lot of
254 * time looking at greedy-wrapped paragraphs and
255 * there's no point violating the Principle of Least
256 * Surprise if it doesn't actually gain anything.
258 if (best
< 0 || bestcost
>= cost
) {
264 * Now we know the optimal answer for this terminal
265 * subsequence, so put it in wrapwords.
267 wrapwords
[i
].cost
= bestcost
;
268 wrapwords
[i
].nwords
= best
;
272 * We've wrapped the paragraph. Now build the output
273 * `wrappedline' list.
277 wrappedline
*w
= mknew(wrappedline
);
282 n
= wrapwords
[i
].nwords
;
283 w
->begin
= wrapwords
[i
].begin
;
284 w
->end
= wrapwords
[i
+n
-1].end
;
287 * Count along the words to find nspaces and shortfall.
290 w
->shortfall
= width
;
291 for (j
= 0; j
< n
; j
++) {
292 w
->shortfall
-= wrapwords
[i
+j
].width
;
293 if (j
< n
-1 && wrapwords
[i
+j
].spacewidth
) {
295 w
->shortfall
-= wrapwords
[i
+j
].spacewidth
;
306 void wrap_free(wrappedline
*w
) {
308 wrappedline
*t
= w
->next
;