tr: add .tc and .lc
[neatroff.git] / wb.c
blobcc2870619eea83867088000fa29c064cb71f3929
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include "roff.h"
6 #define R_F(wb) ((wb)->r_f >= 0 ? (wb)->r_f : n_f) /* current font */
7 #define R_S(wb) ((wb)->r_s >= 0 ? (wb)->r_s : n_s) /* current size */
8 #define R_M(wb) ((wb)->r_m >= 0 ? (wb)->r_m : n_m) /* current color */
10 void wb_init(struct wb *wb)
12 memset(wb, 0, sizeof(*wb));
13 sbuf_init(&wb->sbuf);
14 wb->f = -1;
15 wb->s = -1;
16 wb->m = -1;
17 wb->r_f = -1;
18 wb->r_s = -1;
19 wb->r_m = -1;
22 void wb_done(struct wb *wb)
24 sbuf_done(&wb->sbuf);
27 /* update wb->st and wb->sb */
28 static void wb_stsb(struct wb *wb)
30 wb->st = MIN(wb->st, wb->v - SC_HT);
31 wb->sb = MAX(wb->sb, wb->v);
34 /* append font and size to the buffer if needed */
35 static void wb_font(struct wb *wb)
37 if (wb->f != R_F(wb)) {
38 sbuf_printf(&wb->sbuf, "%cf(%02d", c_ec, R_F(wb));
39 wb->f = R_F(wb);
41 if (wb->s != R_S(wb)) {
42 sbuf_printf(&wb->sbuf, "%cs(%02d", c_ec, R_S(wb));
43 wb->s = R_S(wb);
45 if (!n_cp && wb->m != R_M(wb)) {
46 sbuf_printf(&wb->sbuf, "%cm[%s]", c_ec, clr_str(R_M(wb)));
47 wb->m = R_M(wb);
49 wb_stsb(wb);
52 void wb_hmov(struct wb *wb, int n)
54 wb->h += n;
55 sbuf_printf(&wb->sbuf, "%ch'%du'", c_ec, n);
58 void wb_vmov(struct wb *wb, int n)
60 wb->v += n;
61 sbuf_printf(&wb->sbuf, "%cv'%du'", c_ec, n);
64 void wb_els(struct wb *wb, int els)
66 if (els > wb->els_pos)
67 wb->els_pos = els;
68 if (els < wb->els_neg)
69 wb->els_neg = els;
70 sbuf_printf(&wb->sbuf, "%cx'%du'", c_ec, els);
73 void wb_etc(struct wb *wb, char *x)
75 wb_font(wb);
76 sbuf_printf(&wb->sbuf, "%cX\x02%s\x02", c_ec, x);
79 /* make sure nothing is appended to wb after the last wb_put() */
80 static void wb_prevcheck(struct wb *wb)
82 if (wb->prev_ll != sbuf_len(&wb->sbuf))
83 wb->prev_n = 0;
86 /* mark wb->prev_c[] as valid */
87 static void wb_prevok(struct wb *wb)
89 wb->prev_ll = sbuf_len(&wb->sbuf);
92 /* append c to wb->prev_c[] */
93 static void wb_prevput(struct wb *wb, char *c, int ll)
95 if (wb->prev_n == LEN(wb->prev_c))
96 wb->prev_n--;
97 memmove(wb->prev_l + 1, wb->prev_l, wb->prev_n * sizeof(wb->prev_l[0]));
98 memmove(wb->prev_h + 1, wb->prev_h, wb->prev_n * sizeof(wb->prev_h[0]));
99 memmove(wb->prev_c + 1, wb->prev_c, wb->prev_n * sizeof(wb->prev_c[0]));
100 wb->prev_l[0] = ll;
101 wb->prev_h[0] = wb->h;
102 strcpy(wb->prev_c[0], c);
103 wb->prev_n++;
104 wb_prevok(wb);
107 /* strip the last i characters from wb */
108 static void wb_prevpop(struct wb *wb, int i)
110 int n = wb->prev_n - i;
111 sbuf_cut(&wb->sbuf, wb->prev_l[i - 1]);
112 wb->h = wb->prev_h[i - 1];
113 memmove(wb->prev_l, wb->prev_l + i, n * sizeof(wb->prev_l[0]));
114 memmove(wb->prev_h, wb->prev_h + i, n * sizeof(wb->prev_h[0]));
115 memmove(wb->prev_c, wb->prev_c + i, n * sizeof(wb->prev_c[0]));
116 wb->prev_n = n;
117 wb->prev_ll = sbuf_len(&wb->sbuf);
120 /* return the i-th last character inserted via wb_put() */
121 static char *wb_prev(struct wb *wb, int i)
123 wb_prevcheck(wb);
124 return i < wb->prev_n ? wb->prev_c[i] : NULL;
127 void wb_put(struct wb *wb, char *c)
129 struct glyph *g;
130 int ll;
131 if (c[0] == '\n') {
132 wb->part = 0;
133 return;
135 if (c[0] == ' ') {
136 wb_hmov(wb, spacewid(R_F(wb), R_S(wb)));
137 return;
139 if (c[0] == '\t' || c[0] == '\x01' ||
140 (c[0] == c_ni && (c[1] == '\t' || c[1] == '\x01'))) {
141 sbuf_append(&wb->sbuf, c);
142 return;
144 g = dev_glyph(c, R_F(wb));
145 wb_font(wb);
146 wb_prevcheck(wb); /* make sure wb->prev_c[] is valid */
147 ll = sbuf_len(&wb->sbuf); /* sbuf length before inserting c */
148 if (!c[1] || c[0] == c_ec || c[0] == c_ni ||
149 utf8len((unsigned char) c[0]) == strlen(c)) {
150 if (c[0] == c_ni && c[1] == c_ec)
151 sbuf_printf(&wb->sbuf, "%c%c", c_ec, c_ec);
152 else
153 sbuf_append(&wb->sbuf, c);
154 } else {
155 if (c[1] && !c[2])
156 sbuf_printf(&wb->sbuf, "%c(%s", c_ec, c);
157 else
158 sbuf_printf(&wb->sbuf, "%cC'%s'", c_ec, c);
160 if (strcmp(c_hc, c)) {
161 wb_prevput(wb, c, ll);
162 wb->h += charwid(R_F(wb), R_S(wb), g ? g->wid : SC_DW);
163 wb->ct |= g ? g->type : 0;
164 wb_stsb(wb);
168 /* return zero if c formed a ligature with its previous character */
169 int wb_lig(struct wb *wb, char *c)
171 char lig[GNLEN] = "";
172 char *cs[LIGLEN + 2];
173 int i = -1;
174 int ligpos;
175 cs[0] = c;
176 while (wb_prev(wb, ++i))
177 cs[i + 1] = wb_prev(wb, i);
178 ligpos = font_lig(dev_font(R_F(wb)), cs, i + 1);
179 if (ligpos > 0) {
180 for (i = 0; i < ligpos - 1; i++)
181 strcat(lig, wb_prev(wb, ligpos - i - 2));
182 strcat(lig, c);
183 wb_prevpop(wb, ligpos - 1);
184 wb_put(wb, lig);
185 return 0;
187 return 1;
190 /* return 0 if pairwise kerning was done */
191 int wb_kern(struct wb *wb, char *c)
193 int val;
194 if (!wb_prev(wb, 0))
195 return 1;
196 val = font_kern(dev_font(R_F(wb)), wb_prev(wb, 0), c);
197 if (val)
198 wb_hmov(wb, charwid(R_F(wb), R_S(wb), val));
199 wb_prevok(wb); /* kerning should not prevent ligatures */
200 return !val;
203 int wb_part(struct wb *wb)
205 return wb->part;
208 void wb_setpart(struct wb *wb)
210 wb->part = 1;
213 void wb_drawl(struct wb *wb, int h, int v)
215 wb_font(wb);
216 sbuf_printf(&wb->sbuf, "%cD'l %du %du'", c_ec, h, v);
217 wb->h += h;
218 wb->v += v;
219 wb_stsb(wb);
222 void wb_drawc(struct wb *wb, int r)
224 wb_font(wb);
225 sbuf_printf(&wb->sbuf, "%cD'c %du'", c_ec, r);
226 wb->h += r;
229 void wb_drawe(struct wb *wb, int h, int v)
231 wb_font(wb);
232 sbuf_printf(&wb->sbuf, "%cD'e %du %du'", c_ec, h, v);
233 wb->h += h;
236 void wb_drawa(struct wb *wb, int h1, int v1, int h2, int v2)
238 wb_font(wb);
239 sbuf_printf(&wb->sbuf, "%cD'a %du %du %du %du'", c_ec, h1, v1, h2, v2);
240 wb->h += h1 + h2;
241 wb->v += v1 + v2;
242 wb_stsb(wb);
245 void wb_drawxbeg(struct wb *wb, int c)
247 wb_font(wb);
248 sbuf_printf(&wb->sbuf, "%cD'%c", c_ec, c);
251 void wb_drawxdot(struct wb *wb, int h, int v)
253 sbuf_printf(&wb->sbuf, " %du %du", h, v);
254 wb->h += h;
255 wb->v += v;
256 wb_stsb(wb);
259 void wb_drawxend(struct wb *wb)
261 sbuf_printf(&wb->sbuf, "'");
264 static void wb_reset(struct wb *wb)
266 wb_done(wb);
267 wb_init(wb);
270 static void wb_putc(struct wb *wb, int t, char *s)
272 switch (t) {
273 case 0:
274 case 'C':
275 wb_put(wb, s);
276 break;
277 case 'D':
278 ren_dcmd(wb, s);
279 break;
280 case 'f':
281 wb->r_f = atoi(s);
282 break;
283 case 'h':
284 wb_hmov(wb, atoi(s));
285 break;
286 case 'm':
287 wb->r_m = clr_get(s);
288 break;
289 case 's':
290 wb->r_s = atoi(s);
291 break;
292 case 'v':
293 wb_vmov(wb, atoi(s));
294 break;
295 case 'x':
296 wb_els(wb, atoi(s));
297 break;
298 case 'X':
299 wb_etc(wb, s);
300 break;
304 void wb_cat(struct wb *wb, struct wb *src)
306 char *s = sbuf_buf(&src->sbuf);
307 char d[ILNLEN];
308 int c, part;
309 while ((c = out_readc(&s, d)) >= 0)
310 wb_putc(wb, c, d);
311 part = src->part;
312 wb->r_s = -1;
313 wb->r_f = -1;
314 wb->r_m = -1;
315 wb_reset(src);
316 src->part = part;
319 int wb_wid(struct wb *wb)
321 return wb->h;
324 int wb_empty(struct wb *wb)
326 return sbuf_empty(&wb->sbuf);
329 /* return 1 if wb ends a sentence (.?!) */
330 int wb_eos(struct wb *wb)
332 int i = 0;
333 while (wb_prev(wb, i) && strchr("'\")]*", wb_prev(wb, i)[0]))
334 i++;
335 return wb_prev(wb, i) && strchr(".?!", wb_prev(wb, i)[0]);
338 void wb_wconf(struct wb *wb, int *ct, int *st, int *sb)
340 *ct = wb->ct;
341 *st = -wb->st;
342 *sb = -wb->sb;
345 /* skip troff requests; return 1 if read c_hc */
346 static int skipreqs(char **s, struct wb *w1)
348 char d[ILNLEN];
349 char *r = *s;
350 int c;
351 wb_reset(w1);
352 while ((c = out_readc(s, d)) > 0) {
353 wb_putc(w1, c, d);
354 r = *s;
356 if (c < 0 || !strcmp(c_hc, d))
357 return 1;
358 *s = r;
359 return 0;
362 static char *dashpos(char *s, int w, struct wb *w1, int any)
364 char d[ILNLEN];
365 char *r = NULL;
366 int c;
367 skipreqs(&s, w1);
368 while ((c = out_readc(&s, d)) >= 0) {
369 wb_putc(w1, c, d);
370 if (wb_wid(w1) > w && (!any || r))
371 continue;
372 if (!c && (!strcmp("-", d) || (!strcmp("em", d) || !strcmp("hy", d))))
373 r = s;
375 return r;
378 static int wb_dashwid(struct wb *wb)
380 struct glyph *g = dev_glyph("hy", R_F(wb));
381 return charwid(R_F(wb), R_S(wb), g ? g->wid : SC_DW);
384 static char *indicatorpos(char *s, int w, struct wb *w1, int flg)
386 char d[ILNLEN];
387 char *r = NULL;
388 int c;
389 skipreqs(&s, w1);
390 while ((c = out_readc(&s, d)) >= 0) {
391 wb_putc(w1, c, d);
392 if (wb_wid(w1) + wb_dashwid(w1) > w && (!(flg & HY_ANY) || r))
393 continue;
394 if (!c && !strcmp(c_hc, d))
395 r = s;
397 return r;
400 static char *hyphpos(char *s, int w, struct wb *w1, int flg)
402 char *map[ILNLEN] = {NULL}; /* mapping from word to s */
403 int fits[ILNLEN] = {0}; /* fits[i] if word[0..i]- fits w */
404 char word[ILNLEN];
405 char hyph[ILNLEN];
406 char d[ILNLEN];
407 char *prev_s = s;
408 char *r = NULL;
409 char *wp = word, *we = word + sizeof(word);
410 int beg, end;
411 int i, c;
412 skipreqs(&s, w1);
413 while ((c = out_readc(&s, d)) >= 0 && wp + strlen(d) + 1 < we) {
414 wb_putc(w1, c, d);
415 if (c == 0) {
416 strcpy(wp, d);
417 map[wp - word] = prev_s;
418 wp = strchr(wp, '\0');
419 fits[wp - word] = wb_wid(w1) + wb_dashwid(w1) <= w;
421 prev_s = s;
423 if (strlen(word) < 4)
424 return NULL;
425 hyphenate(hyph, word);
426 beg = flg & HY_FIRSTTWO ? 3 : 2;
427 end = strlen(word) - (flg & HY_FINAL ? 2 : 1);
428 for (i = beg; i < end; i++)
429 if (map[i] && hyph[i] && (fits[i] || ((flg & HY_ANY) && !r)))
430 r = map[i];
431 return r;
434 static void dohyph(char *s, char *pos, int dash, struct wb *w1, struct wb *w2)
436 char d[ILNLEN];
437 int c = -1;
438 wb_reset(w1);
439 wb_reset(w2);
440 while (s != pos && (c = out_readc(&s, d)) >= 0)
441 wb_putc(w1, c, d);
442 if (dash)
443 wb_putc(w1, 0, "hy");
444 w2->r_s = w1->r_s;
445 w2->r_f = w1->r_f;
446 w2->r_m = w1->r_m;
447 while ((c = out_readc(&s, d)) >= 0)
448 wb_putc(w2, c, d);
451 /* hyphenate wb into w1 and w2; return zero on success */
452 int wb_hyph(struct wb *wb, int w, struct wb *w1, struct wb *w2, int flg)
454 char *s = sbuf_buf(&wb->sbuf);
455 char *dp, *hp, *p;
456 if (skipreqs(&s, w1))
457 return 1;
458 dp = dashpos(sbuf_buf(&wb->sbuf), w, w1, flg & HY_ANY);
459 hp = indicatorpos(sbuf_buf(&wb->sbuf), w, w1, flg & HY_ANY);
460 p = flg & HY_ANY ? MIN(dp, hp) : MAX(dp, hp);
461 if (!p && flg & HY_MASK)
462 p = hyphpos(sbuf_buf(&wb->sbuf), w, w1, flg & HY_ANY);
463 if (p)
464 dohyph(sbuf_buf(&wb->sbuf), p, p != dp, w1, w2);
465 return !p;