tr: fix .cc and .c2
[neatroff.git] / ren.c
blob322605fad2536eac4f96005dbd4bb80f53de5544
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 cfmt env_fmt() /* current formatter */
9 #define cwb env_wb() /* current word buffer */
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 int ren_nl; /* just after a newline */
29 static int ren_partial; /* reading an input line in render_rec() */
30 static int ren_unbuf[8]; /* ren_back() buffer */
31 static int ren_un;
32 static int ren_aborted; /* .ab executed */
34 static int bp_first = 1; /* prior to the first page */
35 static int bp_next = 1; /* next page number */
36 static int bp_count; /* number of pages so far */
37 static int bp_ejected; /* current ejected page */
38 static int bp_final; /* 1: executing em, 2: the final page, 3: the 2nd final page */
39 static int ren_level; /* the depth of render_rec() calls */
41 static char c_fa[GNLEN]; /* field delimiter */
42 static char c_fb[GNLEN]; /* field padding */
44 static int ren_next(void)
46 return ren_un > 0 ? ren_unbuf[--ren_un] : tr_next();
49 static void ren_back(int c)
51 ren_unbuf[ren_un++] = c;
54 void tr_di(char **args)
56 if (args[1]) {
57 cdiv = cdiv ? cdiv + 1 : divs;
58 memset(cdiv, 0, sizeof(*cdiv));
59 sbuf_init(&cdiv->sbuf);
60 cdiv->reg = map(args[1]);
61 cdiv->treg = -1;
62 if (args[0][2] == 'a' && str_get(cdiv->reg)) /* .da */
63 sbuf_append(&cdiv->sbuf, str_get(cdiv->reg));
64 sbuf_printf(&cdiv->sbuf, "%c%s\n", c_cc, TR_DIVBEG);
65 cdiv->prev_d = n_d;
66 cdiv->prev_h = n_h;
67 cdiv->prev_mk = n_mk;
68 cdiv->prev_ns = n_ns;
69 n_d = 0;
70 n_h = 0;
71 n_mk = 0;
72 n_ns = 0;
73 } else if (cdiv) {
74 sbuf_putnl(&cdiv->sbuf);
75 sbuf_printf(&cdiv->sbuf, "%c%s\n", c_cc, TR_DIVEND);
76 str_set(cdiv->reg, sbuf_buf(&cdiv->sbuf));
77 sbuf_done(&cdiv->sbuf);
78 n_dl = cdiv->dl;
79 n_dn = n_d;
80 n_d = cdiv->prev_d;
81 n_h = cdiv->prev_h;
82 n_mk = cdiv->prev_mk;
83 n_ns = cdiv->prev_ns;
84 cdiv = cdiv > divs ? cdiv - 1 : NULL;
88 int f_divreg(void)
90 return cdiv ? cdiv->reg : -1;
93 int f_hpos(void)
95 return fmt_wid(cfmt) + wb_wid(cwb);
98 void tr_divbeg(char **args)
100 odiv_beg();
101 ren_div++;
104 void tr_divend(char **args)
106 if (ren_div <= 0)
107 errdie("neatroff: diversion stack empty\n");
108 odiv_end();
109 ren_div--;
112 static int trap_reg(int pos);
113 static int trap_pos(int pos);
114 static void trap_exec(int reg);
116 static void ren_page(int pg, int force)
118 if (!force && bp_final >= 2)
119 return;
120 n_nl = 0;
121 n_d = 0;
122 n_h = 0;
123 n_pg = pg;
124 bp_next = n_pg + 1;
125 bp_count++;
126 out("p%d\n", pg);
127 out("V%d\n", 0);
128 if (trap_pos(-1) == 0)
129 trap_exec(trap_reg(-1));
132 static int ren_first(void)
134 if (bp_first && !cdiv) {
135 bp_first = 0;
136 ren_page(bp_next, 1);
137 return 0;
139 return 1;
142 /* when nodiv, do not append .sp to diversions */
143 static void ren_sp(int n, int nodiv)
145 ren_first();
146 /* ignore .sp without arguments when reading diversions */
147 if (!n && ren_div && !n_u)
148 return;
149 n_d += n ? n : n_v;
150 if (n_d > n_h)
151 n_h = n_d;
152 if (cdiv && !nodiv) {
153 sbuf_putnl(&cdiv->sbuf);
154 sbuf_printf(&cdiv->sbuf, "%csp %du\n", c_cc, n ? n : n_v);
155 } else {
156 n_nl = n_d;
160 static int render_rec(int level);
161 static void trap_exec(int reg)
163 char cmd[16];
164 int partial = ren_partial && (!ren_un || ren_unbuf[0] != '\n');
165 if (str_get(reg)) {
166 sprintf(cmd, "%c%s %d\n", c_cc, TR_POPREN, ren_level);
167 in_push(cmd, NULL);
168 in_push(str_get(reg), NULL);
169 if (partial)
170 in_push("\n", NULL);
171 render_rec(++ren_level);
172 /* executed the trap while in the middle of an input line */
173 if (partial)
174 fmt_suppressnl(cfmt);
178 static int detect_traps(int beg, int end)
180 int pos = trap_pos(beg);
181 return pos >= 0 && (cdiv || pos < n_p) && pos <= end;
184 /* return 1 if executed a trap */
185 static int ren_traps(int beg, int end, int dosp)
187 int pos = trap_pos(beg);
188 if (detect_traps(beg, end)) {
189 if (dosp && pos > beg)
190 ren_sp(pos - beg, 0);
191 trap_exec(trap_reg(beg));
192 return 1;
194 return 0;
197 static int detect_pagelimit(int ne)
199 return !cdiv && n_nl + ne >= n_p;
202 /* start a new page if needed */
203 static int ren_pagelimit(int ne)
205 if (detect_pagelimit(ne)) {
206 ren_page(bp_next, 0);
207 return 1;
209 return 0;
212 /* return 1 if triggered a trap */
213 static int down(int n)
215 if (ren_traps(n_d, n_d + (n ? n : n_v), 1))
216 return 1;
217 ren_sp(n, 0);
218 return ren_pagelimit(0);
221 /* line adjustment */
222 static int ren_ljust(struct sbuf *spre, int w, int ad, int li, int ll)
224 int ljust = li;
225 int llen = ll - ljust;
226 n_n = w;
227 if ((ad & AD_B) == AD_C)
228 ljust += llen > w ? (llen - w) / 2 : 0;
229 if ((ad & AD_B) == AD_R)
230 ljust += llen - w;
231 if (ljust)
232 sbuf_printf(spre, "%ch'%du'", c_ec, ljust);
233 if (cdiv && cdiv->dl < w + ljust)
234 cdiv->dl = w + ljust;
235 return ljust;
238 /* append the line to the current diversion or send it to out.c */
239 static void ren_out(char *beg, char *mid, char *end)
241 if (cdiv) {
242 sbuf_append(&cdiv->sbuf, beg);
243 sbuf_append(&cdiv->sbuf, mid);
244 sbuf_append(&cdiv->sbuf, end);
245 } else {
246 out("H%d\n", n_o);
247 out("V%d\n", n_d);
248 out_line(beg);
249 out_line(mid);
250 out_line(end);
254 static void ren_transparent(char *s)
256 if (cdiv)
257 sbuf_printf(&cdiv->sbuf, "%s\n", s);
258 else
259 out("%s\n", s);
262 static int zwid(void)
264 struct glyph *g = dev_glyph("0", n_f);
265 return g ? font_gwid(g->font, dev_font(n_f), n_s, g->wid) : 0;
268 /* append the line number to the output line */
269 static void ren_lnum(struct sbuf *spre)
271 char num[16] = "";
272 char dig[16] = "";
273 struct wb wb;
274 int i = 0;
275 wb_init(&wb);
276 if (n_nn <= 0 && (n_ln % n_nM) == 0)
277 sprintf(num, "%d", n_ln);
278 wb_hmov(&wb, n_nI * zwid());
279 if (strlen(num) < 3)
280 wb_hmov(&wb, (3 - strlen(num)) * zwid());
281 while (num[i]) {
282 dig[0] = num[i++];
283 wb_put(&wb, dig);
285 wb_hmov(&wb, n_nS * zwid());
286 sbuf_append(spre, wb_buf(&wb));
287 wb_done(&wb);
288 if (n_nn > 0)
289 n_nn--;
290 else
291 n_ln++;
294 /* append margin character */
295 static void ren_mc(struct sbuf *sbuf, int w, int ljust)
297 struct wb wb;
298 wb_init(&wb);
299 if (w + ljust < n_l + n_mcn)
300 wb_hmov(&wb, n_l + n_mcn - w - ljust);
301 wb_putexpand(&wb, c_mc);
302 sbuf_append(sbuf, wb_buf(&wb));
303 wb_done(&wb);
306 /* process a line and print it with ren_out() */
307 static int ren_line(char *line, int w, int ad, int body,
308 int li, int ll, int els_neg, int els_pos)
310 struct sbuf sbeg, send;
311 int prev_d, lspc, ljust;
312 ren_first();
313 sbuf_init(&sbeg);
314 sbuf_init(&send);
315 lspc = MAX(1, n_L) * n_v; /* line space, ignoreing \x */
316 prev_d = n_d;
317 if (els_neg)
318 ren_sp(-els_neg, 1);
319 if (!n_ns || line[0] || els_neg || els_pos) {
320 ren_sp(0, 0);
321 if (line[0] && n_nm && body)
322 ren_lnum(&sbeg);
323 ljust = ren_ljust(&sbeg, w, ad, li, ll);
324 if (line[0] && body && n_mc)
325 ren_mc(&send, w, ljust);
326 ren_out(sbuf_buf(&sbeg), line, sbuf_buf(&send));
327 n_ns = 0;
329 sbuf_done(&sbeg);
330 sbuf_done(&send);
331 if (els_pos)
332 ren_sp(els_pos, 1);
333 n_a = els_pos;
334 if (detect_traps(prev_d, n_d) || detect_pagelimit(lspc - n_v)) {
335 if (!ren_pagelimit(lspc - n_v))
336 ren_traps(prev_d, n_d, 0);
337 return 1;
339 if (lspc - n_v && down(lspc - n_v))
340 return 1;
341 return 0;
344 /* read a line from fmt and send it to ren_line() */
345 static int ren_passline(struct fmt *fmt)
347 struct sbuf sbuf;
348 int ll, li, els_neg, els_pos, w, ret;
349 int ad = n_j;
350 ren_first();
351 if (!fmt_morewords(fmt))
352 return 0;
353 sbuf_init(&sbuf);
354 fmt_nextline(fmt, &sbuf, &w, &li, &ll, &els_neg, &els_pos);
355 if ((n_cp && !n_u) || n_na)
356 ad = AD_L;
357 if (n_ce)
358 ad = AD_C;
359 ret = ren_line(sbuf_buf(&sbuf), w, ad, 1, li, ll, els_neg, els_pos);
360 sbuf_done(&sbuf);
361 return ret;
364 /* output formatted lines in fmt */
365 static void ren_fmtpop(struct fmt *fmt)
367 while (fmt_morelines(fmt))
368 ren_passline(fmt);
371 /* format and output all lines in fmt */
372 static void ren_fmtpopall(struct fmt *fmt)
374 while (fmt_fill(fmt))
375 ren_fmtpop(fmt);
376 ren_fmtpop(fmt);
379 /* output current line; returns 1 if triggered a trap */
380 static int ren_br(void)
382 ren_fmtpopall(cfmt);
383 while (fmt_br(cfmt))
384 ren_fmtpop(cfmt);
385 return ren_passline(cfmt);
388 void tr_br(char **args)
390 ren_fmtpopall(cfmt); /* output the completed lines first */
391 if (args[0][0] == c_cc)
392 ren_br();
395 void tr_sp(char **args)
397 int traps = 0;
398 int n = args[1] ? eval(args[1], 'v') : n_v;
399 if (args[0][0] == c_cc)
400 traps = ren_br();
401 if (n && !n_ns && !traps)
402 down(n);
405 void tr_sv(char **args)
407 int n = eval(args[1], 'v');
408 n_sv = 0;
409 if (n_d + n < f_nexttrap())
410 down(n);
411 else
412 n_sv = n;
415 void tr_ns(char **args)
417 n_ns = 1;
420 void tr_rs(char **args)
422 n_ns = 0;
425 void tr_os(char **args)
427 if (n_sv)
428 down(n_sv);
429 n_sv = 0;
432 void tr_mk(char **args)
434 if (args[1])
435 num_set(map(args[1]), n_d);
436 else
437 n_mk = n_d;
440 void tr_rt(char **args)
442 int n = args[1] ? eval_re(args[1], n_d, 'v') : n_mk;
443 if (n >= 0 && n < n_d)
444 ren_sp(n - n_d, 0);
447 void tr_ne(char **args)
449 int n = args[1] ? eval(args[1], 'v') : n_v;
450 if (!ren_first())
451 return;
452 if (!ren_traps(n_d, n_d + n - 1, 1))
453 ren_pagelimit(n);
456 static void ren_ejectpage(int br)
458 bp_ejected = bp_count;
459 if (br)
460 ren_br();
461 while (bp_count == bp_ejected && !cdiv) {
462 if (detect_traps(n_d, n_p)) {
463 ren_traps(n_d, n_p, 1);
464 } else {
465 bp_ejected = 0;
466 ren_page(bp_next, 0);
471 void tr_bp(char **args)
473 if (!cdiv && (args[1] || !n_ns)) {
474 if (args[1])
475 bp_next = eval_re(args[1], n_pg, 0);
476 ren_ejectpage(args[0][0] == c_cc);
480 void tr_pn(char **args)
482 if (args[1])
483 bp_next = eval_re(args[1], n_pg, 0);
486 static void ren_ps(char *s)
488 int ps = !s || !*s || !strcmp("0", s) ?
489 n_s0 * SC_PT : eval_re(s, n_s * SC_PT, 'p');
490 n_s0 = n_s;
491 n_s = MAX(1, (ps + SC_PT / 2) / SC_PT);
494 void tr_ps(char **args)
496 ren_ps(args[1]);
499 void tr_ll(char **args)
501 int ll = args[1] ? eval_re(args[1], n_l, 'm') : n_l0;
502 n_l0 = n_l;
503 n_l = MAX(0, ll);
506 void tr_in(char **args)
508 int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0;
509 if (args[0][0] == c_cc)
510 ren_br();
511 n_i0 = n_i;
512 n_i = MAX(0, in);
513 n_ti = -1;
516 void tr_ti(char **args)
518 if (args[0][0] == c_cc)
519 ren_br();
520 if (args[1])
521 n_ti = eval_re(args[1], n_i, 'm');
524 static void ren_ft(char *s)
526 int fn = !s || !*s || !strcmp("P", s) ? n_f0 : dev_pos(s);
527 if (fn < 0) {
528 errmsg("neatroff: failed to mount <%s>\n", s);
529 } else {
530 n_f0 = n_f;
531 n_f = fn;
535 void tr_ft(char **args)
537 ren_ft(args[1]);
540 void tr_fp(char **args)
542 if (!args[2])
543 return;
544 if (dev_mnt(atoi(args[1]), args[2], args[3] ? args[3] : args[2]) < 0)
545 errmsg("neatroff: failed to mount <%s>\n", args[2]);
548 void tr_nf(char **args)
550 if (args[0][0] == c_cc)
551 ren_br();
552 n_u = 0;
555 void tr_fi(char **args)
557 if (args[0][0] == c_cc)
558 ren_br();
559 n_u = 1;
562 void tr_ce(char **args)
564 if (args[0][0] == c_cc)
565 ren_br();
566 n_ce = args[1] ? atoi(args[1]) : 1;
569 void tr_fc(char **args)
571 char *fa = args[1];
572 char *fb = args[2];
573 if (fa && charread(&fa, c_fa) >= 0) {
574 if (!fb || charread(&fb, c_fb) < 0)
575 strcpy(c_fb, " ");
576 } else {
577 c_fa[0] = '\0';
578 c_fb[0] = '\0';
582 static void ren_cl(char *s)
584 int m = !s || !*s ? n_m0 : clr_get(s);
585 n_m0 = n_m;
586 n_m = m;
589 void tr_cl(char **args)
591 ren_cl(args[1]);
594 void tr_ab(char **args)
596 fprintf(stderr, "%s\n", args[1]);
597 ren_aborted = 1;
600 static void ren_cmd(struct wb *wb, int c, char *arg)
602 switch (c) {
603 case ' ':
604 wb_hmov(wb, font_swid(dev_font(n_f), n_s, n_ss));
605 break;
606 case 'b':
607 ren_bcmd(wb, arg);
608 break;
609 case 'c':
610 wb_setpart(wb);
611 break;
612 case 'D':
613 ren_dcmd(wb, arg);
614 break;
615 case 'd':
616 wb_vmov(wb, SC_EM / 2);
617 break;
618 case 'f':
619 ren_ft(arg);
620 break;
621 case 'h':
622 wb_hmov(wb, eval(arg, 'm'));
623 break;
624 case 'k':
625 num_set(map(arg), wb == cwb ? f_hpos() - n_lb : wb_wid(wb));
626 break;
627 case 'L':
628 ren_vlcmd(wb, arg);
629 break;
630 case 'l':
631 ren_hlcmd(wb, arg);
632 break;
633 case 'm':
634 ren_cl(arg);
635 break;
636 case 'o':
637 ren_ocmd(wb, arg);
638 break;
639 case 'p':
640 if (wb == cwb)
641 while (fmt_fillreq(cfmt))
642 ren_fmtpop(cfmt);
643 break;
644 case 'r':
645 wb_vmov(wb, -SC_EM);
646 break;
647 case 's':
648 ren_ps(arg);
649 break;
650 case 'u':
651 wb_vmov(wb, -SC_EM / 2);
652 break;
653 case 'v':
654 wb_vmov(wb, eval(arg, 'v'));
655 break;
656 case 'X':
657 wb_etc(wb, arg);
658 break;
659 case 'x':
660 wb_els(wb, eval(arg, 'v'));
661 break;
662 case 'Z':
663 ren_zcmd(wb, arg);
664 break;
665 case '0':
666 wb_hmov(wb, zwid());
667 break;
668 case '|':
669 wb_hmov(wb, SC_EM / 6);
670 break;
671 case '&':
672 wb_hmov(wb, 0);
673 break;
674 case '^':
675 wb_hmov(wb, SC_EM / 12);
676 break;
677 case '/':
678 wb_italiccorrection(wb);
679 break;
680 case ',':
681 wb_italiccorrectionleft(wb);
682 break;
686 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int));
687 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int));
689 /* insert a character, escape sequence, field or etc into wb */
690 static void ren_put(struct wb *wb, char *c, int (*next)(void), void (*back)(int))
692 char arg[ILNLEN];
693 char *s;
694 int w, n;
695 if (c[0] == ' ' || c[0] == '\n') {
696 wb_put(wb, c);
697 return;
699 if (c[0] == '\t' || c[0] == '\x01') {
700 ren_tab(wb, c[0] == '\t' ? c_tc : c_lc, next, back);
701 return;
703 if (c_fa[0] && !strcmp(c_fa, c)) {
704 ren_field(wb, next, back);
705 return;
707 if (c[0] == c_ec) {
708 if (c[1] == 'z') {
709 w = wb_wid(wb);
710 ren_char(wb, next, back);
711 wb_hmov(wb, w - wb_wid(wb));
712 return;
714 if (c[1] == '!') {
715 if (ren_nl && next == ren_next) {
716 s = arg;
717 n = next();
718 while (n >= 0 && n != '\n') {
719 *s++ = n;
720 n = next();
722 *s = '\0';
723 ren_transparent(arg);
725 return;
727 if (strchr(" bCcDdefHhkLlmNoprSsuvXxZz0^|{}&/,", c[1])) {
728 arg[0] = '\0';
729 if (strchr(ESC_P, c[1]))
730 unquotednext(arg, c[1], next, back);
731 if (strchr(ESC_Q, c[1]))
732 quotednext(arg, next, back);
733 if (c[1] == 'e') {
734 snprintf(c, GNLEN, "%c%c", c_ec, c_ec);
735 } else if (c[1] == 'N') {
736 snprintf(c, GNLEN, "GID=%s", arg);
737 } else {
738 ren_cmd(wb, c[1], arg);
739 return;
743 if (ren_div) {
744 wb_putraw(wb, c);
745 return;
747 if (cdef_map(c, n_f)) /* .char characters */
748 wb_putexpand(wb, c);
749 else
750 wb_put(wb, c);
753 /* read one character and place it inside wb buffer */
754 int ren_char(struct wb *wb, int (*next)(void), void (*back)(int))
756 char c[GNLEN * 4];
757 if (charnext(c, next, back) < 0)
758 return -1;
759 ren_put(wb, c, next, back);
760 return 0;
763 /* like ren_char(); return 1 if d1 was read and 2 if d2 was read */
764 static int ren_chardel(struct wb *wb, int (*next)(void), void (*back)(int),
765 char *d1, char *d2)
767 char c[GNLEN * 4];
768 if (charnext(c, next, back) < 0)
769 return -1;
770 if (d1 && !strcmp(d1, c))
771 return 1;
772 if (d2 && !strcmp(d2, c))
773 return 2;
774 ren_put(wb, c, next, back);
775 return 0;
778 /* read the argument of \w and push its width */
779 int ren_wid(int (*next)(void), void (*back)(int))
781 char delim[GNLEN];
782 int c, n;
783 struct wb wb;
784 wb_init(&wb);
785 charnext(delim, next, back);
786 odiv_beg();
787 c = next();
788 while (c >= 0 && c != '\n') {
789 back(c);
790 if (ren_chardel(&wb, next, back, delim, NULL))
791 break;
792 c = next();
794 odiv_end();
795 n = wb_wid(&wb);
796 wb_wconf(&wb, &n_ct, &n_st, &n_sb, &n_llx, &n_lly, &n_urx, &n_ury);
797 wb_done(&wb);
798 return n;
801 /* return 1 if d1 was read and 2 if d2 was read */
802 static int ren_until(struct wb *wb, char *d1, char *d2,
803 int (*next)(void), void (*back)(int))
805 int c, ret;
806 c = next();
807 while (c >= 0 && c != '\n') {
808 back(c);
809 ret = ren_chardel(wb, next, back, d1, d2);
810 if (ret)
811 return ret;
812 c = next();
814 if (c == '\n')
815 back(c);
816 return 0;
819 static void wb_cpy(struct wb *dst, struct wb *src, int left)
821 wb_hmov(dst, left - wb_wid(dst));
822 wb_cat(dst, src);
825 void ren_tl(int (*next)(void), void (*back)(int))
827 struct wb wb, wb2;
828 char delim[GNLEN];
829 wb_init(&wb);
830 wb_init(&wb2);
831 charnext(delim, next, back);
832 if (!strcmp("\n", delim))
833 back('\n');
834 /* the left-adjusted string */
835 ren_until(&wb2, delim, NULL, next, back);
836 wb_cpy(&wb, &wb2, 0);
837 /* the centered string */
838 ren_until(&wb2, delim, NULL, next, back);
839 wb_cpy(&wb, &wb2, (n_lt - wb_wid(&wb2)) / 2);
840 /* the right-adjusted string */
841 ren_until(&wb2, delim, NULL, next, back);
842 wb_cpy(&wb, &wb2, n_lt - wb_wid(&wb2));
843 /* flushing the line */
844 ren_line(wb_buf(&wb), wb_wid(&wb), AD_L, 0,
845 0, n_lt, wb.els_neg, wb.els_pos);
846 wb_done(&wb2);
847 wb_done(&wb);
850 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int))
852 struct wb wbs[NFIELDS];
853 int i, n = 0;
854 int wid = 0;
855 int left, right, cur_left;
856 int pad, rem;
857 while (n < LEN(wbs)) {
858 wb_init(&wbs[n]);
859 if (ren_until(&wbs[n++], c_fb, c_fa, next, back) != 1)
860 break;
862 left = wb == cwb ? f_hpos() : wb_wid(wb);
863 right = tab_next(left);
864 for (i = 0; i < n; i++)
865 wid += wb_wid(&wbs[i]);
866 pad = (right - left - wid) / (n > 1 ? n - 1 : 1);
867 rem = (right - left - wid) % (n > 1 ? n - 1 : 1);
868 for (i = 0; i < n; i++) {
869 if (i == 0)
870 cur_left = left;
871 else if (i == n - 1)
872 cur_left = right - wb_wid(&wbs[i]);
873 else
874 cur_left = wb_wid(wb) + pad + (i + rem >= n);
875 wb_cpy(wb, &wbs[i], cur_left);
876 wb_done(&wbs[i]);
880 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int))
882 struct wb t;
883 int pos = wb == cwb ? f_hpos() : wb_wid(wb);
884 int ins = tab_next(pos); /* insertion position */
885 int typ = tab_type(pos); /* tab type */
886 int c;
887 wb_init(&t);
888 if (typ == 'R' || typ == 'C') {
889 c = next();
890 while (c >= 0 && c != '\n' && c != '\t' && c != '\x01') {
891 back(c);
892 ren_char(&t, next, back);
893 c = next();
895 back(c);
897 if (typ == 'C')
898 ins -= wb_wid(&t) / 2;
899 if (typ == 'R')
900 ins -= wb_wid(&t);
901 if (!tc[0] || ins <= pos)
902 wb_hmov(wb, ins - pos);
903 else
904 ren_hline(wb, ins - pos, tc);
905 wb_cat(wb, &t);
906 wb_done(&t);
909 /* parse characters and troff requests of s and append them to wb */
910 int ren_parse(struct wb *wb, char *s)
912 int c;
913 odiv_beg();
914 sstr_push(s);
915 c = sstr_next();
916 while (c >= 0) {
917 sstr_back(c);
918 if (ren_char(wb, sstr_next, sstr_back))
919 break;
920 c = sstr_next();
922 sstr_pop();
923 odiv_end();
924 return 0;
927 /* cause nested render_rec() to exit */
928 void tr_popren(char **args)
930 ren_level = args[1] ? atoi(args[1]) : 0;
933 #define FMT_PAR() (n_u && !n_na && !n_ce && (n_j & AD_P) == AD_P)
935 /* read characters from tr.c and pass the rendered lines to out.c */
936 static int render_rec(int level)
938 int c;
939 while (ren_level >= level) {
940 while (!ren_un && !tr_nextreq())
941 if (ren_level < level)
942 break;
943 if (ren_level < level)
944 break;
945 if (ren_aborted)
946 return 1;
947 c = ren_next();
948 if (c < 0) {
949 if (bp_final >= 2)
950 break;
951 if (bp_final == 0) {
952 bp_final = 1;
953 ren_fmtpopall(cfmt);
954 if (trap_em >= 0)
955 trap_exec(trap_em);
956 } else {
957 bp_final = 2;
958 ren_ejectpage(1);
961 if (c >= 0)
962 ren_partial = c != '\n';
963 /* add cwb (the current word) to cfmt */
964 if (c == ' ' || c == '\n') {
965 if (!wb_part(cwb)) { /* not after a \c */
966 while (fmt_word(cfmt, cwb))
967 ren_fmtpop(cfmt);
968 wb_reset(cwb);
969 if (c == '\n')
970 while (fmt_newline(cfmt))
971 ren_fmtpop(cfmt);
972 if (!FMT_PAR())
973 ren_fmtpopall(cfmt);
974 if (c == ' ')
975 fmt_space(cfmt);
978 /* flush the line if necessary */
979 if ((!FMT_PAR() && (c == ' ' || c == '\n')) || c < 0)
980 ren_fmtpop(cfmt);
981 if (c == '\n' || ren_nl) /* end or start of input line */
982 n_lb = f_hpos();
983 if (c == '\n' && n_it && --n_itn == 0)
984 trap_exec(n_it);
985 if (c == '\n' && !wb_part(cwb))
986 n_ce = MAX(0, n_ce - 1);
987 if (c != ' ' && c >= 0) {
988 ren_back(c);
989 ren_char(cwb, ren_next, ren_back);
991 if (c >= 0)
992 ren_nl = c == '\n';
994 return 0;
997 /* render input words */
998 int render(void)
1000 n_nl = -1;
1001 while (!tr_nextreq())
1003 ren_first(); /* transition to the first page */
1004 render_rec(0);
1005 bp_final = 3;
1006 if (fmt_morewords(cfmt))
1007 ren_page(bp_next, 1);
1008 ren_br();
1009 return 0;
1012 /* trap handling */
1014 #define tposval(i) (tpos[i] < 0 ? n_p + tpos[i] : tpos[i])
1016 static int tpos[NTRAPS]; /* trap positions */
1017 static int treg[NTRAPS]; /* trap registers */
1018 static int ntraps;
1020 static int trap_first(int pos)
1022 int best = -1;
1023 int i;
1024 for (i = 0; i < ntraps; i++)
1025 if (treg[i] >= 0 && tposval(i) > pos)
1026 if (best < 0 || tposval(i) < tposval(best))
1027 best = i;
1028 return best;
1031 static int trap_byreg(int reg)
1033 int i;
1034 for (i = 0; i < ntraps; i++)
1035 if (treg[i] == reg)
1036 return i;
1037 return -1;
1040 static int trap_bypos(int reg, int pos)
1042 int i;
1043 for (i = 0; i < ntraps; i++)
1044 if (treg[i] >= 0 && tposval(i) == pos)
1045 if (reg == -1 || treg[i] == reg)
1046 return i;
1047 return -1;
1050 void tr_wh(char **args)
1052 int reg, pos, id;
1053 if (!args[1])
1054 return;
1055 pos = eval(args[1], 'v');
1056 id = trap_bypos(-1, pos);
1057 if (!args[2]) {
1058 if (id >= 0)
1059 treg[id] = -1;
1060 return;
1062 reg = map(args[2]);
1063 if (id < 0) /* find an unused position in treg[] */
1064 id = trap_byreg(-1);
1065 if (id < 0)
1066 id = ntraps++;
1067 tpos[id] = pos;
1068 treg[id] = reg;
1071 void tr_ch(char **args)
1073 int reg;
1074 int id;
1075 if (!args[1])
1076 return;
1077 reg = map(args[1]);
1078 id = trap_byreg(reg);
1079 if (id >= 0) {
1080 if (args[2])
1081 tpos[id] = eval(args[2], 'v');
1082 else
1083 treg[id] = -1;
1087 void tr_dt(char **args)
1089 if (!cdiv)
1090 return;
1091 if (args[2]) {
1092 cdiv->tpos = eval(args[1], 'v');
1093 cdiv->treg = map(args[2]);
1094 } else {
1095 cdiv->treg = -1;
1099 void tr_em(char **args)
1101 trap_em = args[1] ? map(args[1]) : -1;
1104 static int trap_pos(int pos)
1106 int ret = trap_first(pos);
1107 if (bp_final >= 3)
1108 return -1;
1109 if (cdiv)
1110 return cdiv->treg && cdiv->tpos > pos ? cdiv->tpos : -1;
1111 return ret >= 0 ? tposval(ret) : -1;
1114 static int trap_reg(int pos)
1116 int ret = trap_first(pos);
1117 if (cdiv)
1118 return cdiv->treg && cdiv->tpos > pos ? cdiv->treg : -1;
1119 return ret >= 0 ? treg[ret] : -1;
1122 int f_nexttrap(void)
1124 int pos = trap_pos(n_d);
1125 if (cdiv)
1126 return pos >= 0 ? pos : 0x7fffffff;
1127 return (pos >= 0 && pos < n_p ? pos : n_p) - n_d;