roff.h: replace charwid_base() with DEVWID()
[neatroff.git] / ren.c
blob313e17a2df6903daa6124b5c37a91ec5bf6c140d
1 /* rendering lines and managing traps */
2 #include <ctype.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include "roff.h"
8 #define cadj env_adj() /* line buffer */
9 #define RENWB(wb) ((wb) == &ren_wb) /* is ren_wb */
11 /* diversions */
12 struct div {
13 struct sbuf sbuf; /* diversion output */
14 int reg; /* diversion register */
15 int tpos; /* diversion trap position */
16 int treg; /* diversion trap register */
17 int dl; /* diversion width */
18 int prev_d; /* previous \n(.d value */
19 int prev_h; /* previous \n(.h value */
20 int prev_mk; /* previous .mk internal register */
21 int prev_ns; /* previous .ns value */
23 static struct div divs[NPREV]; /* diversion stack */
24 static struct div *cdiv; /* current diversion */
25 static int ren_div; /* rendering a diversion */
26 static int trap_em = -1; /* end macro */
28 static struct wb ren_wb; /* the main ren.c word buffer */
29 static int ren_nl; /* just after newline */
30 static int ren_cnl; /* current char is a newline */
31 static int ren_unbuf[8]; /* ren_back() buffer */
32 static int ren_un;
33 static int ren_fillreq; /* \p request */
34 static int ren_aborted; /* .ab executed */
36 static int bp_first = 1; /* prior to the first page */
37 static int bp_next = 1; /* next page number */
38 static int bp_count; /* number of pages so far */
39 static int bp_ejected; /* current ejected page */
40 static int bp_final; /* 1: executing em, 2: the final page, 3: the 2nd final page */
42 static char c_fa[GNLEN]; /* field delimiter */
43 static char c_fb[GNLEN]; /* field padding */
45 static int ren_next(void)
47 return ren_un > 0 ? ren_unbuf[--ren_un] : tr_next();
50 static void ren_back(int c)
52 ren_unbuf[ren_un++] = c;
55 void tr_di(char **args)
57 if (args[1]) {
58 cdiv = cdiv ? cdiv + 1 : divs;
59 memset(cdiv, 0, sizeof(*cdiv));
60 sbuf_init(&cdiv->sbuf);
61 cdiv->reg = map(args[1]);
62 cdiv->treg = -1;
63 if (args[0][2] == 'a' && str_get(cdiv->reg)) /* .da */
64 sbuf_append(&cdiv->sbuf, str_get(cdiv->reg));
65 sbuf_printf(&cdiv->sbuf, "%c%s\n", c_cc, TR_DIVBEG);
66 cdiv->prev_d = n_d;
67 cdiv->prev_h = n_h;
68 cdiv->prev_mk = n_mk;
69 cdiv->prev_ns = n_ns;
70 n_d = 0;
71 n_h = 0;
72 n_mk = 0;
73 n_ns = 0;
74 } else if (cdiv) {
75 sbuf_putnl(&cdiv->sbuf);
76 sbuf_printf(&cdiv->sbuf, "%c%s\n", c_cc, TR_DIVEND);
77 str_set(cdiv->reg, sbuf_buf(&cdiv->sbuf));
78 sbuf_done(&cdiv->sbuf);
79 n_dl = cdiv->dl;
80 n_dn = n_d;
81 n_d = cdiv->prev_d;
82 n_h = cdiv->prev_h;
83 n_mk = cdiv->prev_mk;
84 n_ns = cdiv->prev_ns;
85 cdiv = cdiv > divs ? cdiv - 1 : NULL;
89 int charwid(int fn, int sz, int wid)
91 if (dev_getcs(fn))
92 return dev_getcs(n_f) * SC_EM / 36;
93 return DEVWID(sz, wid) + (dev_getbd(fn) ? dev_getbd(fn) - 1 : 0);
96 int spacewid(int fn, int sz)
98 return charwid(fn, sz, (dev_font(fn)->spacewid * n_ss + 6) / 12);
101 int f_divreg(void)
103 return cdiv ? cdiv->reg : -1;
106 int f_hpos(void)
108 return adj_wid(cadj) + wb_wid(&ren_wb);
111 void tr_divbeg(char **args)
113 odiv_beg();
114 ren_div++;
117 void tr_divend(char **args)
119 odiv_end();
120 ren_div--;
123 static int trap_reg(int pos);
124 static int trap_pos(int pos);
125 static void trap_exec(int reg);
127 static void ren_page(int pg, int force)
129 if (!force && bp_final >= 2)
130 return;
131 n_nl = 0;
132 n_d = 0;
133 n_h = 0;
134 n_pg = pg;
135 bp_next = n_pg + 1;
136 bp_count++;
137 out("p%d\n", pg);
138 out("V%d\n", 0);
139 if (trap_pos(-1) == 0)
140 trap_exec(trap_reg(-1));
143 static void ren_first(void)
145 if (bp_first && !cdiv) {
146 bp_first = 0;
147 ren_page(bp_next, 1);
151 /* when nodiv, do not append .sp to diversions */
152 static void ren_sp(int n, int nodiv)
154 ren_first();
155 /* ignore .sp without arguments when reading diversions */
156 if (!n && ren_div && !n_u)
157 return;
158 n_d += n ? n : n_v;
159 if (n_d > n_h)
160 n_h = n_d;
161 if (cdiv && !nodiv) {
162 sbuf_putnl(&cdiv->sbuf);
163 sbuf_printf(&cdiv->sbuf, "%csp %du\n", c_cc, n ? n : n_v);
164 } else {
165 n_nl = n_d;
169 static void trap_exec(int reg)
171 if (str_get(reg))
172 in_pushnl(str_get(reg), NULL);
175 static int detect_traps(int beg, int end)
177 int pos = trap_pos(beg);
178 return pos >= 0 && (cdiv || pos < n_p) && pos <= end;
181 /* return 1 if executed a trap */
182 static int ren_traps(int beg, int end, int dosp)
184 int pos = trap_pos(beg);
185 if (detect_traps(beg, end)) {
186 if (dosp && pos > beg)
187 ren_sp(pos - beg, 0);
188 trap_exec(trap_reg(beg));
189 return 1;
191 return 0;
194 static int detect_pagelimit(int ne)
196 return !cdiv && n_nl + ne >= n_p;
199 /* start a new page if needed */
200 static int ren_pagelimit(int ne)
202 if (detect_pagelimit(ne)) {
203 ren_page(bp_next, 0);
204 return 1;
206 return 0;
209 /* return 1 if triggered a trap */
210 static int down(int n)
212 if (ren_traps(n_d, n_d + (n ? n : n_v), 1))
213 return 1;
214 ren_sp(n, 0);
215 return ren_pagelimit(0);
218 /* line adjustment */
219 static int ren_ljust(struct sbuf *spre, int w, int ad, int ll, int li, int lt)
221 int ljust = lt >= 0 ? lt : li;
222 int llen = ll - ljust;
223 n_n = w;
224 if (ad == AD_C)
225 ljust += llen > w ? (llen - w) / 2 : 0;
226 if (ad == AD_R)
227 ljust += llen - w;
228 if (ljust)
229 sbuf_printf(spre, "%ch'%du'", c_ec, ljust);
230 if (cdiv && cdiv->dl < w + ljust)
231 cdiv->dl = w + ljust;
232 return ljust;
235 /* append the line to the current diversion or send it to out.c */
236 static void ren_line(struct sbuf *spre, struct sbuf *sbuf)
238 if (cdiv) {
239 if (!sbuf_empty(spre))
240 sbuf_append(&cdiv->sbuf, sbuf_buf(spre));
241 sbuf_append(&cdiv->sbuf, sbuf_buf(sbuf));
242 } else {
243 out("H%d\n", n_o);
244 out("V%d\n", n_d);
245 if (!sbuf_empty(spre))
246 out_line(sbuf_buf(spre));
247 out_line(sbuf_buf(sbuf));
251 static void ren_transparent(char *s)
253 if (cdiv)
254 sbuf_printf(&cdiv->sbuf, "%s\n", s);
255 else
256 out("%s\n", s);
259 static int zwid(void)
261 struct glyph *g = dev_glyph("0", n_f);
262 return charwid(n_f, n_s, g ? g->wid : SC_DW);
265 /* append the line number to the output line */
266 static void ren_lnum(struct sbuf *spre)
268 char num[16] = "";
269 char dig[16] = "";
270 struct wb wb;
271 int i = 0;
272 wb_init(&wb);
273 if (n_nn <= 0 && (n_ln % n_nM) == 0)
274 sprintf(num, "%d", n_ln);
275 wb_hmov(&wb, n_nI * zwid());
276 if (strlen(num) < 3)
277 wb_hmov(&wb, (3 - strlen(num)) * zwid());
278 while (num[i]) {
279 dig[0] = num[i++];
280 wb_put(&wb, dig);
282 wb_hmov(&wb, n_nS * zwid());
283 sbuf_append(spre, sbuf_buf(&wb.sbuf));
284 wb_done(&wb);
285 if (n_nn > 0)
286 n_nn--;
287 else
288 n_ln++;
291 /* append margin character */
292 static void ren_mc(struct sbuf *sbuf, int w, int ljust)
294 struct wb wb;
295 wb_init(&wb);
296 if (w + ljust < n_l + n_mcn)
297 wb_hmov(&wb, n_l + n_mcn - w - ljust);
298 wb_putexpand(&wb, c_mc);
299 sbuf_append(sbuf, sbuf_buf(&wb.sbuf));
300 wb_done(&wb);
303 /* output current line; returns 1 if triggered a trap */
304 static int ren_bradj(struct adj *adj, int fill, int ad, int body)
306 char cmd[16];
307 struct sbuf sbuf, spre;
308 int ll, li, lt, els_neg, els_pos;
309 int w, hyph, prev_d, lspc, ljust;
310 ren_first();
311 if (!adj_empty(adj, fill)) {
312 sbuf_init(&sbuf);
313 sbuf_init(&spre);
314 hyph = n_hy;
315 lspc = MAX(1, n_L) * n_v; /* line space, ignoreing \x */
316 if (n_hy & HY_LAST && (detect_traps(n_d, n_d + lspc) ||
317 detect_pagelimit(lspc)))
318 hyph = 0; /* disable for last lines */
319 w = adj_fill(adj, ad == AD_B, fill, hyph, &sbuf,
320 &ll, &li, &lt, &els_neg, &els_pos);
321 prev_d = n_d;
322 if (els_neg)
323 ren_sp(-els_neg, 1);
324 if (!n_ns || !sbuf_empty(&sbuf) || els_neg || els_pos) {
325 ren_sp(0, 0);
326 if (!sbuf_empty(&sbuf) && n_nm && body)
327 ren_lnum(&spre);
328 ljust = ren_ljust(&spre, w, ad, ll, li, lt);
329 if (!sbuf_empty(&sbuf) && body && n_mc)
330 ren_mc(&sbuf, w, ljust);
331 ren_line(&spre, &sbuf);
332 n_ns = 0;
334 sbuf_done(&spre);
335 sbuf_done(&sbuf);
336 if (els_pos)
337 ren_sp(els_pos, 1);
338 n_a = els_pos;
339 if (detect_traps(prev_d, n_d) || detect_pagelimit(lspc - n_v)) {
340 sprintf(cmd, "%c&", c_ec);
341 if (!ren_cnl) /* prevent unwanted newlines */
342 in_push(cmd, NULL);
343 if (!ren_traps(prev_d, n_d, 0))
344 ren_pagelimit(lspc - n_v);
345 return 1;
347 if (lspc - n_v && down(lspc - n_v))
348 return 1;
350 return 0;
353 /* output current line; returns 1 if triggered a trap */
354 static int ren_br(int force)
356 int ad = n_j;
357 if (!n_u || n_na || (n_j == AD_B && force))
358 ad = AD_L;
359 if (n_ce)
360 ad = AD_C;
361 return ren_bradj(cadj, !force && !n_ce && n_u, ad, 1);
364 void tr_br(char **args)
366 if (args[0][0] == c_cc)
367 ren_br(1);
370 void tr_sp(char **args)
372 int traps = 0;
373 int n = args[1] ? eval(args[1], 'v') : n_v;
374 if (args[0][0] == c_cc)
375 traps = ren_br(1);
376 if (n && !n_ns && !traps)
377 down(n);
380 void tr_sv(char **args)
382 int n = eval(args[1], 'v');
383 n_sv = 0;
384 if (n_d + n < f_nexttrap())
385 down(n);
386 else
387 n_sv = n;
390 void tr_ns(char **args)
392 n_ns = 1;
395 void tr_rs(char **args)
397 n_ns = 0;
400 void tr_os(char **args)
402 if (n_sv)
403 down(n_sv);
404 n_sv = 0;
407 void tr_mk(char **args)
409 if (args[1])
410 num_set(map(args[1]), n_d);
411 else
412 n_mk = n_d;
415 void tr_rt(char **args)
417 int n = args[1] ? eval_re(args[1], n_d, 'v') : n_mk;
418 if (n >= 0 && n < n_d)
419 ren_sp(n - n_d, 0);
422 void tr_ne(char **args)
424 int n = args[1] ? eval(args[1], 'v') : n_v;
425 if (!ren_traps(n_d, n_d + n - 1, 1))
426 ren_pagelimit(n);
429 static void push_eject(void)
431 char buf[32];
432 bp_ejected = bp_count;
433 sprintf(buf, "%c%s %d\n", c_cc, TR_EJECT, bp_ejected);
434 in_pushnl(buf, NULL);
437 static void push_br(void)
439 char br[8] = {c_cc, 'b', 'r', '\n'};
440 in_pushnl(br, NULL);
443 static void ren_eject(int id)
445 if (id == bp_ejected && id == bp_count && !cdiv) {
446 if (detect_traps(n_d, n_p)) {
447 push_eject();
448 ren_traps(n_d, n_p, 1);
449 } else {
450 bp_ejected = 0;
451 ren_page(bp_next, 0);
456 void tr_eject(char **args)
458 ren_eject(atoi(args[1]));
461 void tr_bp(char **args)
463 if (!cdiv && (args[1] || !n_ns)) {
464 if (bp_ejected != bp_count)
465 push_eject();
466 if (args[0][0] == c_cc)
467 push_br();
468 if (args[1])
469 bp_next = eval_re(args[1], n_pg, 0);
473 void tr_pn(char **args)
475 if (args[1])
476 bp_next = eval_re(args[1], n_pg, 0);
479 static void ren_ps(char *s)
481 int ps = !s || !*s || !strcmp("0", s) ? n_s0 : eval_re(s, n_s, 0);
482 n_s0 = n_s;
483 n_s = MAX(1, ps);
486 void tr_ps(char **args)
488 ren_ps(args[1]);
491 void tr_ll(char **args)
493 int ll = args[1] ? eval_re(args[1], n_l, 'm') : n_l0;
494 n_l0 = n_l;
495 n_l = MAX(0, ll);
496 adj_ll(cadj, n_l);
499 void tr_in(char **args)
501 int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0;
502 if (args[0][0] == c_cc)
503 ren_br(1);
504 n_i0 = n_i;
505 n_i = MAX(0, in);
506 adj_in(cadj, n_i);
507 adj_ti(cadj, -1);
510 void tr_ti(char **args)
512 if (args[0][0] == c_cc)
513 ren_br(1);
514 if (args[1])
515 adj_ti(cadj, eval_re(args[1], n_i, 'm'));
518 static void ren_ft(char *s)
520 int fn = !s || !*s || !strcmp("P", s) ? n_f0 : dev_pos(s);
521 if (fn < 0) {
522 errmsg("neatroff: failed to mount <%s>\n", s);
523 } else {
524 n_f0 = n_f;
525 n_f = fn;
529 void tr_ft(char **args)
531 ren_ft(args[1]);
534 void tr_fp(char **args)
536 if (!args[2])
537 return;
538 if (dev_mnt(atoi(args[1]), args[2], args[3] ? args[3] : args[2]) < 0)
539 errmsg("neatroff: failed to mount <%s>\n", args[2]);
542 void tr_nf(char **args)
544 if (args[0][0] == c_cc)
545 ren_br(1);
546 n_u = 0;
549 void tr_fi(char **args)
551 if (args[0][0] == c_cc)
552 ren_br(1);
553 n_u = 1;
556 void tr_ce(char **args)
558 if (args[0][0] == c_cc)
559 ren_br(1);
560 n_ce = args[1] ? atoi(args[1]) : 1;
563 void tr_fc(char **args)
565 char *fa = args[1];
566 char *fb = args[2];
567 if (fa && charread(&fa, c_fa) >= 0) {
568 if (!fb || charread(&fb, c_fb) < 0)
569 strcpy(c_fb, " ");
570 } else {
571 c_fa[0] = '\0';
572 c_fb[0] = '\0';
576 static void ren_cl(char *s)
578 int m = !s || !*s ? n_m0 : clr_get(s);
579 n_m0 = n_m;
580 n_m = m;
583 void tr_cl(char **args)
585 ren_cl(args[1]);
588 void tr_ab(char **args)
590 fprintf(stderr, "%s\n", args[1]);
591 ren_aborted = 1;
594 static void ren_cmd(struct wb *wb, int c, char *arg)
596 switch (c) {
597 case ' ':
598 wb_hmov(wb, spacewid(n_f, n_s));
599 break;
600 case 'b':
601 ren_bcmd(wb, arg);
602 break;
603 case 'c':
604 wb_setpart(wb);
605 break;
606 case 'D':
607 ren_dcmd(wb, arg);
608 break;
609 case 'd':
610 wb_vmov(wb, SC_EM / 2);
611 break;
612 case 'f':
613 ren_ft(arg);
614 break;
615 case 'h':
616 wb_hmov(wb, eval(arg, 'm'));
617 break;
618 case 'k':
619 num_set(map(arg), RENWB(wb) ? f_hpos() - n_lb : wb_wid(wb));
620 break;
621 case 'L':
622 ren_vlcmd(wb, arg);
623 break;
624 case 'l':
625 ren_hlcmd(wb, arg);
626 break;
627 case 'm':
628 ren_cl(arg);
629 break;
630 case 'o':
631 ren_ocmd(wb, arg);
632 break;
633 case 'p':
634 if (RENWB(wb))
635 ren_fillreq = 1;
636 break;
637 case 'r':
638 wb_vmov(wb, -SC_EM);
639 break;
640 case 's':
641 ren_ps(arg);
642 break;
643 case 'u':
644 wb_vmov(wb, -SC_EM / 2);
645 break;
646 case 'v':
647 wb_vmov(wb, eval(arg, 'v'));
648 break;
649 case 'X':
650 wb_etc(wb, arg);
651 break;
652 case 'x':
653 wb_els(wb, eval(arg, 'v'));
654 break;
655 case '0':
656 wb_hmov(wb, zwid());
657 break;
658 case '|':
659 wb_hmov(wb, SC_EM / 6);
660 break;
661 case '&':
662 wb_hmov(wb, 0);
663 break;
664 case '^':
665 wb_hmov(wb, SC_EM / 12);
666 break;
667 case '/':
668 wb_italiccorrection(wb);
669 break;
670 case ',':
671 wb_italiccorrectionleft(wb);
672 break;
673 case '{':
674 case '}':
675 break;
679 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int));
680 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int));
682 /* insert a character, escape sequence, field or etc into wb */
683 static void ren_put(struct wb *wb, char *c, int (*next)(void), void (*back)(int))
685 char arg[ILNLEN];
686 struct glyph *g;
687 char *s;
688 int w, n;
689 if (c[0] == ' ' || c[0] == '\n') {
690 wb_put(wb, c);
691 return;
693 if (c[0] == '\t' || c[0] == '\x01') {
694 ren_tab(wb, c[0] == '\t' ? c_tc : c_lc, next, back);
695 return;
697 if (c_fa[0] && !strcmp(c_fa, c)) {
698 ren_field(wb, next, back);
699 return;
701 if (c[0] == c_ec) {
702 if (c[1] == 'z') {
703 w = wb_wid(wb);
704 ren_char(wb, next, back);
705 wb_hmov(wb, w - wb_wid(wb));
706 return;
708 if (c[1] == '!') {
709 if (ren_nl && next == ren_next) {
710 s = arg;
711 n = next();
712 while (n >= 0 && n != '\n') {
713 *s++ = n;
714 n = next();
716 *s = '\0';
717 ren_transparent(arg);
719 return;
721 if (strchr(" bCcDdfHhkLlmNoprSsuvXxz0^|{}&/,", c[1])) {
722 argnext(arg, c[1], next, back);
723 if (c[1] == 'S' || c[1] == 'H')
724 return; /* not implemented */
725 if (c[1] != 'N') {
726 ren_cmd(wb, c[1], arg);
727 return;
729 g = dev_glyph_byid(arg, n_f);
730 strcpy(c, g ? g->name : "cnull");
733 if (!ren_div && cdef_map(c, n_f)) { /* .char characters */
734 wb_putexpand(wb, c);
735 return;
737 if (!n_lg || ren_div || wb_lig(wb, c)) {
738 if (n_kn && !ren_div)
739 wb_kern(wb, c);
740 wb_put(wb, c);
744 /* read one character and place it inside wb buffer */
745 int ren_char(struct wb *wb, int (*next)(void), void (*back)(int))
747 char c[GNLEN * 4];
748 if (charnext(c, next, back) < 0)
749 return -1;
750 ren_put(wb, c, next, back);
751 return 0;
754 /* like ren_char(); return 1 if d1 was read and d2 if d2 was read */
755 static int ren_chardel(struct wb *wb, int (*next)(void), void (*back)(int),
756 char *d1, char *d2)
758 char c[GNLEN * 4];
759 if (charnext(c, next, back) < 0)
760 return -1;
761 if (d1 && !strcmp(d1, c))
762 return 1;
763 if (d2 && !strcmp(d2, c))
764 return 2;
765 ren_put(wb, c, next, back);
766 return 0;
769 /* read the argument of \w and push its width */
770 int ren_wid(int (*next)(void), void (*back)(int))
772 char delim[GNLEN];
773 int c, n;
774 struct wb wb;
775 wb_init(&wb);
776 charnext(delim, next, back);
777 odiv_beg();
778 c = next();
779 while (c >= 0 && c != '\n') {
780 back(c);
781 if (ren_chardel(&wb, next, back, delim, NULL))
782 break;
783 c = next();
785 odiv_end();
786 n = wb_wid(&wb);
787 wb_wconf(&wb, &n_ct, &n_st, &n_sb);
788 wb_done(&wb);
789 return n;
792 /* return 1 if d1 was read and 2 if d2 was read */
793 static int ren_until(struct wb *wb, char *d1, char *d2,
794 int (*next)(void), void (*back)(int))
796 int c, ret;
797 c = next();
798 while (c >= 0 && c != '\n') {
799 back(c);
800 ret = ren_chardel(wb, next, back, d1, d2);
801 if (ret)
802 return ret;
803 c = next();
805 if (c == '\n')
806 back(c);
807 return 0;
810 static void wb_cpy(struct wb *dst, struct wb *src, int left)
812 wb_hmov(dst, left - wb_wid(dst));
813 wb_cat(dst, src);
816 void ren_tl(int (*next)(void), void (*back)(int))
818 struct adj *adj;
819 struct wb wb, wb2;
820 char delim[GNLEN];
821 adj = adj_alloc();
822 wb_init(&wb);
823 wb_init(&wb2);
824 charnext(delim, next, back);
825 /* the left-adjusted string */
826 ren_until(&wb2, delim, NULL, next, back);
827 wb_cpy(&wb, &wb2, 0);
828 /* the centered string */
829 ren_until(&wb2, delim, NULL, next, back);
830 wb_cpy(&wb, &wb2, (n_lt - wb_wid(&wb2)) / 2);
831 /* the right-adjusted string */
832 ren_until(&wb2, delim, NULL, next, back);
833 wb_cpy(&wb, &wb2, n_lt - wb_wid(&wb2));
834 /* flushing the line */
835 adj_ll(adj, n_lt);
836 adj_wb(adj, &wb);
837 adj_nl(adj);
838 ren_bradj(adj, 0, AD_L, 0);
839 adj_free(adj);
840 wb_done(&wb2);
841 wb_done(&wb);
844 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int))
846 struct wb wbs[NFIELDS];
847 int i, n = 0;
848 int wid = 0;
849 int left, right, cur_left;
850 int pad, rem;
851 while (n < LEN(wbs)) {
852 wb_init(&wbs[n]);
853 if (ren_until(&wbs[n++], c_fb, c_fa, next, back) != 1)
854 break;
856 left = RENWB(wb) ? f_hpos() : wb_wid(wb);
857 right = tab_next(left);
858 for (i = 0; i < n; i++)
859 wid += wb_wid(&wbs[i]);
860 pad = (right - left - wid) / (n > 1 ? n - 1 : 1);
861 rem = (right - left - wid) % (n > 1 ? n - 1 : 1);
862 for (i = 0; i < n; i++) {
863 if (i == 0)
864 cur_left = left;
865 else if (i == n - 1)
866 cur_left = right - wb_wid(&wbs[i]);
867 else
868 cur_left = wb_wid(wb) + pad + (i + rem >= n);
869 wb_cpy(wb, &wbs[i], cur_left);
870 wb_done(&wbs[i]);
874 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int))
876 struct wb t;
877 int pos = RENWB(wb) ? f_hpos() : wb_wid(wb);
878 int ins = tab_next(pos); /* insertion position */
879 int typ = tab_type(pos); /* tab type */
880 int c;
881 wb_init(&t);
882 if (typ == 'R' || typ == 'C') {
883 c = next();
884 while (c >= 0 && c != '\n' && c != '\t' && c != '\x01') {
885 back(c);
886 ren_char(&t, next, back);
887 c = next();
889 back(c);
891 if (typ == 'C')
892 ins -= wb_wid(&t) / 2;
893 if (typ == 'R')
894 ins -= wb_wid(&t);
895 if (!tc[0] || ins <= pos)
896 wb_hmov(wb, ins - pos);
897 else
898 ren_hline(wb, ins - pos, tc);
899 wb_cat(wb, &t);
900 wb_done(&t);
903 /* parse characters and troff requests of s and append them to wb */
904 int ren_parse(struct wb *wb, char *s)
906 int c;
907 odiv_beg();
908 sstr_push(s);
909 c = sstr_next();
910 while (c >= 0) {
911 sstr_back(c);
912 if (ren_char(wb, sstr_next, sstr_back))
913 break;
914 c = sstr_next();
916 sstr_pop();
917 odiv_end();
918 return 0;
921 /* read characters from in.c and pass rendered lines to out.c */
922 int render(void)
924 struct wb *wb = &ren_wb;
925 int c;
926 n_nl = -1;
927 wb_init(wb);
928 tr_first();
929 ren_first(); /* transition to the first page */
930 c = ren_next();
931 while (1) {
932 if (ren_aborted)
933 return 1;
934 if (c < 0) {
935 if (bp_final >= 2)
936 break;
937 if (bp_final == 0 && trap_em >= 0) {
938 trap_exec(trap_em);
939 bp_final = 1;
940 } else {
941 bp_final = 2;
942 push_eject();
943 push_br();
945 c = ren_next();
946 continue;
948 ren_cnl = c == '\n';
949 /* add wb (the current word) to cadj */
950 if (c == ' ' || c == '\n') {
951 adj_swid(cadj, spacewid(n_f, n_s));
952 if (!wb_part(wb)) { /* not after a \c */
953 adj_wb(cadj, wb);
954 if (c == '\n')
955 adj_nl(cadj);
956 else
957 adj_sp(cadj);
960 /* flush the line if necessary */
961 if (c == ' ' || c == '\n') {
962 while ((ren_fillreq && !wb_part(wb) && !n_ce && n_u) ||
963 adj_full(cadj, !n_ce && n_u)) {
964 ren_br(0);
965 ren_fillreq = 0;
968 if (c == '\n' || ren_nl) /* end or start of input line */
969 n_lb = f_hpos();
970 if (c == '\n' && n_it && --n_itn == 0)
971 trap_exec(n_it);
972 if (c == '\n' && !wb_part(wb))
973 n_ce = MAX(0, n_ce - 1);
974 if (c != ' ') {
975 ren_back(c);
976 ren_char(wb, ren_next, ren_back);
977 if (c != '\n' && wb_empty(wb))
978 adj_nonl(cadj);
980 ren_nl = c == '\n';
981 c = ren_next();
983 bp_final = 3;
984 if (!adj_empty(cadj, 0))
985 ren_page(bp_next, 1);
986 ren_br(1);
987 wb_done(wb);
988 return 0;
991 /* trap handling */
993 #define tposval(i) (tpos[i] < 0 ? n_p + tpos[i] : tpos[i])
995 static int tpos[NTRAPS]; /* trap positions */
996 static int treg[NTRAPS]; /* trap registers */
997 static int ntraps;
999 static int trap_first(int pos)
1001 int best = -1;
1002 int i;
1003 for (i = 0; i < ntraps; i++)
1004 if (treg[i] >= 0 && tposval(i) > pos)
1005 if (best < 0 || tposval(i) < tposval(best))
1006 best = i;
1007 return best;
1010 static int trap_byreg(int reg)
1012 int i;
1013 for (i = 0; i < ntraps; i++)
1014 if (treg[i] == reg)
1015 return i;
1016 return -1;
1019 static int trap_bypos(int reg, int pos)
1021 int i;
1022 for (i = 0; i < ntraps; i++)
1023 if (treg[i] >= 0 && tposval(i) == pos)
1024 if (reg == -1 || treg[i] == reg)
1025 return i;
1026 return -1;
1029 void tr_wh(char **args)
1031 int reg, pos, id;
1032 if (!args[1])
1033 return;
1034 pos = eval(args[1], 'v');
1035 id = trap_bypos(-1, pos);
1036 if (!args[2]) {
1037 if (id >= 0)
1038 treg[id] = -1;
1039 return;
1041 reg = map(args[2]);
1042 if (id < 0)
1043 id = trap_byreg(-1);
1044 if (id < 0)
1045 id = ntraps++;
1046 tpos[id] = pos;
1047 treg[id] = reg;
1050 void tr_ch(char **args)
1052 int reg;
1053 int id;
1054 if (!args[1])
1055 return;
1056 reg = map(args[1]);
1057 id = trap_byreg(reg);
1058 if (id >= 0)
1059 tpos[id] = args[2] ? eval(args[2], 'v') : -1;
1062 void tr_dt(char **args)
1064 if (!cdiv)
1065 return;
1066 if (args[2]) {
1067 cdiv->tpos = eval(args[1], 'v');
1068 cdiv->treg = map(args[2]);
1069 } else {
1070 cdiv->treg = -1;
1074 void tr_em(char **args)
1076 trap_em = args[1] ? map(args[1]) : -1;
1079 static int trap_pos(int pos)
1081 int ret = trap_first(pos);
1082 if (bp_final >= 3)
1083 return -1;
1084 if (cdiv)
1085 return cdiv->treg && cdiv->tpos > pos ? cdiv->tpos : -1;
1086 return ret >= 0 ? tposval(ret) : -1;
1089 static int trap_reg(int pos)
1091 int ret = trap_first(pos);
1092 if (cdiv)
1093 return cdiv->treg && cdiv->tpos > pos ? cdiv->treg : -1;
1094 return ret >= 0 ? treg[ret] : -1;
1097 int f_nexttrap(void)
1099 int pos = trap_pos(n_d);
1100 if (cdiv)
1101 return pos >= 0 ? pos : 0x7fffffff;
1102 return (pos >= 0 && pos < n_p ? pos : n_p) - n_d;