tr: clean up macrobody()
[neatroff.git] / ren.c
blob7ace5f38f384c6834ec851072d5db1359fe848ec
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 charwid(int fn, int sz, int wid)
90 if (dev_getcs(fn))
91 return dev_getcs(n_f) * SC_EM / 36;
92 return DEVWID(sz, wid) + (dev_getbd(fn) ? dev_getbd(fn) - 1 : 0);
95 int f_divreg(void)
97 return cdiv ? cdiv->reg : -1;
100 int f_hpos(void)
102 return fmt_wid(cfmt) + wb_wid(cwb);
105 void tr_divbeg(char **args)
107 odiv_beg();
108 ren_div++;
111 void tr_divend(char **args)
113 if (ren_div <= 0)
114 errdie("neatroff: diversion stack empty\n");
115 odiv_end();
116 ren_div--;
119 static int trap_reg(int pos);
120 static int trap_pos(int pos);
121 static void trap_exec(int reg);
123 static void ren_page(int pg, int force)
125 if (!force && bp_final >= 2)
126 return;
127 n_nl = 0;
128 n_d = 0;
129 n_h = 0;
130 n_pg = pg;
131 bp_next = n_pg + 1;
132 bp_count++;
133 out("p%d\n", pg);
134 out("V%d\n", 0);
135 if (trap_pos(-1) == 0)
136 trap_exec(trap_reg(-1));
139 static int ren_first(void)
141 if (bp_first && !cdiv) {
142 bp_first = 0;
143 ren_page(bp_next, 1);
144 return 0;
146 return 1;
149 /* when nodiv, do not append .sp to diversions */
150 static void ren_sp(int n, int nodiv)
152 ren_first();
153 /* ignore .sp without arguments when reading diversions */
154 if (!n && ren_div && !n_u)
155 return;
156 n_d += n ? n : n_v;
157 if (n_d > n_h)
158 n_h = n_d;
159 if (cdiv && !nodiv) {
160 sbuf_putnl(&cdiv->sbuf);
161 sbuf_printf(&cdiv->sbuf, "%csp %du\n", c_cc, n ? n : n_v);
162 } else {
163 n_nl = n_d;
167 static int render_rec(int level);
168 static void trap_exec(int reg)
170 char cmd[16];
171 int partial = ren_partial;
172 if (str_get(reg)) {
173 sprintf(cmd, "%c%s %d\n", c_cc, TR_POPREN, ren_level);
174 in_pushnl(cmd, NULL);
175 in_pushnl(str_get(reg), NULL);
176 render_rec(++ren_level);
177 /* executed the trap while in the middle of an input line */
178 if (partial)
179 fmt_suppressnl(cfmt);
183 static int detect_traps(int beg, int end)
185 int pos = trap_pos(beg);
186 return pos >= 0 && (cdiv || pos < n_p) && pos <= end;
189 /* return 1 if executed a trap */
190 static int ren_traps(int beg, int end, int dosp)
192 int pos = trap_pos(beg);
193 if (detect_traps(beg, end)) {
194 if (dosp && pos > beg)
195 ren_sp(pos - beg, 0);
196 trap_exec(trap_reg(beg));
197 return 1;
199 return 0;
202 static int detect_pagelimit(int ne)
204 return !cdiv && n_nl + ne >= n_p;
207 /* start a new page if needed */
208 static int ren_pagelimit(int ne)
210 if (detect_pagelimit(ne)) {
211 ren_page(bp_next, 0);
212 return 1;
214 return 0;
217 /* return 1 if triggered a trap */
218 static int down(int n)
220 if (ren_traps(n_d, n_d + (n ? n : n_v), 1))
221 return 1;
222 ren_sp(n, 0);
223 return ren_pagelimit(0);
226 /* line adjustment */
227 static int ren_ljust(struct sbuf *spre, int w, int ad, int li, int ll)
229 int ljust = li;
230 int llen = ll - ljust;
231 n_n = w;
232 if ((ad & AD_B) == AD_C)
233 ljust += llen > w ? (llen - w) / 2 : 0;
234 if ((ad & AD_B) == AD_R)
235 ljust += llen - w;
236 if (ljust)
237 sbuf_printf(spre, "%ch'%du'", c_ec, ljust);
238 if (cdiv && cdiv->dl < w + ljust)
239 cdiv->dl = w + ljust;
240 return ljust;
243 /* append the line to the current diversion or send it to out.c */
244 static void ren_out(char *beg, char *mid, char *end)
246 if (cdiv) {
247 sbuf_append(&cdiv->sbuf, beg);
248 sbuf_append(&cdiv->sbuf, mid);
249 sbuf_append(&cdiv->sbuf, end);
250 } else {
251 out("H%d\n", n_o);
252 out("V%d\n", n_d);
253 out_line(beg);
254 out_line(mid);
255 out_line(end);
259 static void ren_transparent(char *s)
261 if (cdiv)
262 sbuf_printf(&cdiv->sbuf, "%s\n", s);
263 else
264 out("%s\n", s);
267 static int zwid(void)
269 struct glyph *g = dev_glyph("0", n_f);
270 return charwid(n_f, n_s, g ? g->wid : 0);
273 /* append the line number to the output line */
274 static void ren_lnum(struct sbuf *spre)
276 char num[16] = "";
277 char dig[16] = "";
278 struct wb wb;
279 int i = 0;
280 wb_init(&wb);
281 if (n_nn <= 0 && (n_ln % n_nM) == 0)
282 sprintf(num, "%d", n_ln);
283 wb_hmov(&wb, n_nI * zwid());
284 if (strlen(num) < 3)
285 wb_hmov(&wb, (3 - strlen(num)) * zwid());
286 while (num[i]) {
287 dig[0] = num[i++];
288 wb_put(&wb, dig);
290 wb_hmov(&wb, n_nS * zwid());
291 sbuf_append(spre, sbuf_buf(&wb.sbuf));
292 wb_done(&wb);
293 if (n_nn > 0)
294 n_nn--;
295 else
296 n_ln++;
299 /* append margin character */
300 static void ren_mc(struct sbuf *sbuf, int w, int ljust)
302 struct wb wb;
303 wb_init(&wb);
304 if (w + ljust < n_l + n_mcn)
305 wb_hmov(&wb, n_l + n_mcn - w - ljust);
306 wb_putexpand(&wb, c_mc);
307 sbuf_append(sbuf, sbuf_buf(&wb.sbuf));
308 wb_done(&wb);
311 /* process a line and print it with ren_out() */
312 static int ren_line(char *line, int w, int ad, int body,
313 int li, int ll, int els_neg, int els_pos)
315 struct sbuf sbeg, send;
316 int prev_d, lspc, ljust;
317 ren_first();
318 sbuf_init(&sbeg);
319 sbuf_init(&send);
320 lspc = MAX(1, n_L) * n_v; /* line space, ignoreing \x */
321 prev_d = n_d;
322 if (els_neg)
323 ren_sp(-els_neg, 1);
324 if (!n_ns || line[0] || els_neg || els_pos) {
325 ren_sp(0, 0);
326 if (line[0] && n_nm && body)
327 ren_lnum(&sbeg);
328 ljust = ren_ljust(&sbeg, w, ad, li, ll);
329 if (line[0] && body && n_mc)
330 ren_mc(&send, w, ljust);
331 ren_out(sbuf_buf(&sbeg), line, sbuf_buf(&send));
332 n_ns = 0;
334 sbuf_done(&sbeg);
335 sbuf_done(&send);
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 if (!ren_pagelimit(lspc - n_v))
341 ren_traps(prev_d, n_d, 0);
342 return 1;
344 if (lspc - n_v && down(lspc - n_v))
345 return 1;
346 return 0;
349 /* read a line from fmt and send it to ren_line() */
350 static int ren_passline(struct fmt *fmt)
352 struct sbuf sbuf;
353 int ll, li, els_neg, els_pos, w, ret;
354 int ad = n_j;
355 ren_first();
356 if (!fmt_morewords(fmt))
357 return 0;
358 sbuf_init(&sbuf);
359 fmt_nextline(fmt, &sbuf, &w, &li, &ll, &els_neg, &els_pos);
360 if ((n_cp && !n_u) || n_na)
361 ad = AD_L;
362 if (n_ce)
363 ad = AD_C;
364 ret = ren_line(sbuf_buf(&sbuf), w, ad, 1, li, ll, els_neg, els_pos);
365 sbuf_done(&sbuf);
366 return ret;
369 /* output formatted lines in fmt */
370 static void ren_fmtpop(struct fmt *fmt)
372 while (fmt_morelines(fmt))
373 ren_passline(fmt);
376 /* format and output all lines in fmt */
377 static void ren_fmtpopall(struct fmt *fmt)
379 while (fmt_fill(fmt))
380 ren_fmtpop(fmt);
381 ren_fmtpop(fmt);
384 /* output current line; returns 1 if triggered a trap */
385 static int ren_br(void)
387 ren_fmtpopall(cfmt);
388 while (fmt_br(cfmt))
389 ren_fmtpop(cfmt);
390 return ren_passline(cfmt);
393 void tr_br(char **args)
395 ren_fmtpopall(cfmt); /* output the completed lines first */
396 if (args[0][0] == c_cc)
397 ren_br();
400 void tr_sp(char **args)
402 int traps = 0;
403 int n = args[1] ? eval(args[1], 'v') : n_v;
404 if (args[0][0] == c_cc)
405 traps = ren_br();
406 if (n && !n_ns && !traps)
407 down(n);
410 void tr_sv(char **args)
412 int n = eval(args[1], 'v');
413 n_sv = 0;
414 if (n_d + n < f_nexttrap())
415 down(n);
416 else
417 n_sv = n;
420 void tr_ns(char **args)
422 n_ns = 1;
425 void tr_rs(char **args)
427 n_ns = 0;
430 void tr_os(char **args)
432 if (n_sv)
433 down(n_sv);
434 n_sv = 0;
437 void tr_mk(char **args)
439 if (args[1])
440 num_set(map(args[1]), n_d);
441 else
442 n_mk = n_d;
445 void tr_rt(char **args)
447 int n = args[1] ? eval_re(args[1], n_d, 'v') : n_mk;
448 if (n >= 0 && n < n_d)
449 ren_sp(n - n_d, 0);
452 void tr_ne(char **args)
454 int n = args[1] ? eval(args[1], 'v') : n_v;
455 if (!ren_first())
456 return;
457 if (!ren_traps(n_d, n_d + n - 1, 1))
458 ren_pagelimit(n);
461 static void ren_ejectpage(int br)
463 bp_ejected = bp_count;
464 if (br)
465 ren_br();
466 while (bp_count == bp_ejected && !cdiv) {
467 if (detect_traps(n_d, n_p)) {
468 ren_traps(n_d, n_p, 1);
469 } else {
470 bp_ejected = 0;
471 ren_page(bp_next, 0);
476 void tr_bp(char **args)
478 if (!cdiv && (args[1] || !n_ns)) {
479 if (args[1])
480 bp_next = eval_re(args[1], n_pg, 0);
481 ren_ejectpage(args[0][0] == c_cc);
485 void tr_pn(char **args)
487 if (args[1])
488 bp_next = eval_re(args[1], n_pg, 0);
491 static void ren_ps(char *s)
493 int ps = !s || !*s || !strcmp("0", s) ? n_s0 : eval_re(s, n_s, 0);
494 n_s0 = n_s;
495 n_s = MAX(1, ps);
498 void tr_ps(char **args)
500 ren_ps(args[1]);
503 void tr_ll(char **args)
505 int ll = args[1] ? eval_re(args[1], n_l, 'm') : n_l0;
506 n_l0 = n_l;
507 n_l = MAX(0, ll);
510 void tr_in(char **args)
512 int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0;
513 if (args[0][0] == c_cc)
514 ren_br();
515 n_i0 = n_i;
516 n_i = MAX(0, in);
517 n_ti = -1;
520 void tr_ti(char **args)
522 if (args[0][0] == c_cc)
523 ren_br();
524 if (args[1])
525 n_ti = eval_re(args[1], n_i, 'm');
528 static void ren_ft(char *s)
530 int fn = !s || !*s || !strcmp("P", s) ? n_f0 : dev_pos(s);
531 if (fn < 0) {
532 errmsg("neatroff: failed to mount <%s>\n", s);
533 } else {
534 n_f0 = n_f;
535 n_f = fn;
539 void tr_ft(char **args)
541 ren_ft(args[1]);
544 void tr_fp(char **args)
546 if (!args[2])
547 return;
548 if (dev_mnt(atoi(args[1]), args[2], args[3] ? args[3] : args[2]) < 0)
549 errmsg("neatroff: failed to mount <%s>\n", args[2]);
552 void tr_nf(char **args)
554 if (args[0][0] == c_cc)
555 ren_br();
556 n_u = 0;
559 void tr_fi(char **args)
561 if (args[0][0] == c_cc)
562 ren_br();
563 n_u = 1;
566 void tr_ce(char **args)
568 if (args[0][0] == c_cc)
569 ren_br();
570 n_ce = args[1] ? atoi(args[1]) : 1;
573 void tr_fc(char **args)
575 char *fa = args[1];
576 char *fb = args[2];
577 if (fa && charread(&fa, c_fa) >= 0) {
578 if (!fb || charread(&fb, c_fb) < 0)
579 strcpy(c_fb, " ");
580 } else {
581 c_fa[0] = '\0';
582 c_fb[0] = '\0';
586 static void ren_cl(char *s)
588 int m = !s || !*s ? n_m0 : clr_get(s);
589 n_m0 = n_m;
590 n_m = m;
593 void tr_cl(char **args)
595 ren_cl(args[1]);
598 void tr_ab(char **args)
600 fprintf(stderr, "%s\n", args[1]);
601 ren_aborted = 1;
604 static void ren_cmd(struct wb *wb, int c, char *arg)
606 switch (c) {
607 case ' ':
608 wb_hmov(wb, N_SS(n_f, n_s));
609 break;
610 case 'b':
611 ren_bcmd(wb, arg);
612 break;
613 case 'c':
614 wb_setpart(wb);
615 break;
616 case 'D':
617 ren_dcmd(wb, arg);
618 break;
619 case 'd':
620 wb_vmov(wb, SC_EM / 2);
621 break;
622 case 'f':
623 ren_ft(arg);
624 break;
625 case 'h':
626 wb_hmov(wb, eval(arg, 'm'));
627 break;
628 case 'k':
629 num_set(map(arg), wb == cwb ? f_hpos() - n_lb : wb_wid(wb));
630 break;
631 case 'L':
632 ren_vlcmd(wb, arg);
633 break;
634 case 'l':
635 ren_hlcmd(wb, arg);
636 break;
637 case 'm':
638 ren_cl(arg);
639 break;
640 case 'o':
641 ren_ocmd(wb, arg);
642 break;
643 case 'p':
644 if (wb == cwb)
645 while (fmt_fillreq(cfmt))
646 ren_fmtpop(cfmt);
647 break;
648 case 'r':
649 wb_vmov(wb, -SC_EM);
650 break;
651 case 's':
652 ren_ps(arg);
653 break;
654 case 'u':
655 wb_vmov(wb, -SC_EM / 2);
656 break;
657 case 'v':
658 wb_vmov(wb, eval(arg, 'v'));
659 break;
660 case 'X':
661 wb_etc(wb, arg);
662 break;
663 case 'x':
664 wb_els(wb, eval(arg, 'v'));
665 break;
666 case '0':
667 wb_hmov(wb, zwid());
668 break;
669 case '|':
670 wb_hmov(wb, SC_EM / 6);
671 break;
672 case '&':
673 wb_hmov(wb, 0);
674 break;
675 case '^':
676 wb_hmov(wb, SC_EM / 12);
677 break;
678 case '/':
679 wb_italiccorrection(wb);
680 break;
681 case ',':
682 wb_italiccorrectionleft(wb);
683 break;
684 case '{':
685 case '}':
686 break;
690 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int));
691 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int));
693 /* insert a character, escape sequence, field or etc into wb */
694 static void ren_put(struct wb *wb, char *c, int (*next)(void), void (*back)(int))
696 char arg[ILNLEN];
697 char *s;
698 int w, n;
699 if (c[0] == ' ' || c[0] == '\n') {
700 wb_put(wb, c);
701 return;
703 if (c[0] == '\t' || c[0] == '\x01') {
704 ren_tab(wb, c[0] == '\t' ? c_tc : c_lc, next, back);
705 return;
707 if (c_fa[0] && !strcmp(c_fa, c)) {
708 ren_field(wb, next, back);
709 return;
711 if (c[0] == c_ec) {
712 if (c[1] == 'z') {
713 w = wb_wid(wb);
714 ren_char(wb, next, back);
715 wb_hmov(wb, w - wb_wid(wb));
716 return;
718 if (c[1] == '!') {
719 if (ren_nl && next == ren_next) {
720 s = arg;
721 n = next();
722 while (n >= 0 && n != '\n') {
723 *s++ = n;
724 n = next();
726 *s = '\0';
727 ren_transparent(arg);
729 return;
731 if (strchr(" bCcDdefHhkLlmNoprSsuvXxz0^|{}&/,", c[1])) {
732 argnext(arg, c[1], 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 && cdef_map(c, n_f)) { /* .char characters */
744 wb_putexpand(wb, c);
745 return;
747 if (!n_lg || ren_div || wb_lig(wb, c)) {
748 if (n_kn && !ren_div)
749 wb_kern(wb, c);
750 wb_put(wb, c);
754 /* read one character and place it inside wb buffer */
755 int ren_char(struct wb *wb, int (*next)(void), void (*back)(int))
757 char c[GNLEN * 4];
758 if (charnext(c, next, back) < 0)
759 return -1;
760 ren_put(wb, c, next, back);
761 return 0;
764 /* like ren_char(); return 1 if d1 was read and d2 if d2 was read */
765 static int ren_chardel(struct wb *wb, int (*next)(void), void (*back)(int),
766 char *d1, char *d2)
768 char c[GNLEN * 4];
769 if (charnext(c, next, back) < 0)
770 return -1;
771 if (d1 && !strcmp(d1, c))
772 return 1;
773 if (d2 && !strcmp(d2, c))
774 return 2;
775 ren_put(wb, c, next, back);
776 return 0;
779 /* read the argument of \w and push its width */
780 int ren_wid(int (*next)(void), void (*back)(int))
782 char delim[GNLEN];
783 int c, n;
784 struct wb wb;
785 wb_init(&wb);
786 charnext(delim, next, back);
787 odiv_beg();
788 c = next();
789 while (c >= 0 && c != '\n') {
790 back(c);
791 if (ren_chardel(&wb, next, back, delim, NULL))
792 break;
793 c = next();
795 odiv_end();
796 n = wb_wid(&wb);
797 wb_wconf(&wb, &n_ct, &n_st, &n_sb, &n_llx, &n_lly, &n_urx, &n_ury);
798 wb_done(&wb);
799 return n;
802 /* return 1 if d1 was read and 2 if d2 was read */
803 static int ren_until(struct wb *wb, char *d1, char *d2,
804 int (*next)(void), void (*back)(int))
806 int c, ret;
807 c = next();
808 while (c >= 0 && c != '\n') {
809 back(c);
810 ret = ren_chardel(wb, next, back, d1, d2);
811 if (ret)
812 return ret;
813 c = next();
815 if (c == '\n')
816 back(c);
817 return 0;
820 static void wb_cpy(struct wb *dst, struct wb *src, int left)
822 wb_hmov(dst, left - wb_wid(dst));
823 wb_cat(dst, src);
826 void ren_tl(int (*next)(void), void (*back)(int))
828 struct wb wb, wb2;
829 char delim[GNLEN];
830 wb_init(&wb);
831 wb_init(&wb2);
832 charnext(delim, next, back);
833 /* the left-adjusted string */
834 ren_until(&wb2, delim, NULL, next, back);
835 wb_cpy(&wb, &wb2, 0);
836 /* the centered string */
837 ren_until(&wb2, delim, NULL, next, back);
838 wb_cpy(&wb, &wb2, (n_lt - wb_wid(&wb2)) / 2);
839 /* the right-adjusted string */
840 ren_until(&wb2, delim, NULL, next, back);
841 wb_cpy(&wb, &wb2, n_lt - wb_wid(&wb2));
842 /* flushing the line */
843 ren_line(sbuf_buf(&wb.sbuf), wb_wid(&wb), AD_L, 0,
844 0, n_lt, wb.els_neg, wb.els_pos);
845 wb_done(&wb2);
846 wb_done(&wb);
849 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int))
851 struct wb wbs[NFIELDS];
852 int i, n = 0;
853 int wid = 0;
854 int left, right, cur_left;
855 int pad, rem;
856 while (n < LEN(wbs)) {
857 wb_init(&wbs[n]);
858 if (ren_until(&wbs[n++], c_fb, c_fa, next, back) != 1)
859 break;
861 left = wb == cwb ? f_hpos() : wb_wid(wb);
862 right = tab_next(left);
863 for (i = 0; i < n; i++)
864 wid += wb_wid(&wbs[i]);
865 pad = (right - left - wid) / (n > 1 ? n - 1 : 1);
866 rem = (right - left - wid) % (n > 1 ? n - 1 : 1);
867 for (i = 0; i < n; i++) {
868 if (i == 0)
869 cur_left = left;
870 else if (i == n - 1)
871 cur_left = right - wb_wid(&wbs[i]);
872 else
873 cur_left = wb_wid(wb) + pad + (i + rem >= n);
874 wb_cpy(wb, &wbs[i], cur_left);
875 wb_done(&wbs[i]);
879 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int))
881 struct wb t;
882 int pos = wb == cwb ? f_hpos() : wb_wid(wb);
883 int ins = tab_next(pos); /* insertion position */
884 int typ = tab_type(pos); /* tab type */
885 int c;
886 wb_init(&t);
887 if (typ == 'R' || typ == 'C') {
888 c = next();
889 while (c >= 0 && c != '\n' && c != '\t' && c != '\x01') {
890 back(c);
891 ren_char(&t, next, back);
892 c = next();
894 back(c);
896 if (typ == 'C')
897 ins -= wb_wid(&t) / 2;
898 if (typ == 'R')
899 ins -= wb_wid(&t);
900 if (!tc[0] || ins <= pos)
901 wb_hmov(wb, ins - pos);
902 else
903 ren_hline(wb, ins - pos, tc);
904 wb_cat(wb, &t);
905 wb_done(&t);
908 /* parse characters and troff requests of s and append them to wb */
909 int ren_parse(struct wb *wb, char *s)
911 int c;
912 odiv_beg();
913 sstr_push(s);
914 c = sstr_next();
915 while (c >= 0) {
916 sstr_back(c);
917 if (ren_char(wb, sstr_next, sstr_back))
918 break;
919 c = sstr_next();
921 sstr_pop();
922 odiv_end();
923 return 0;
926 /* cause nested render_rec() to exit */
927 void tr_popren(char **args)
929 ren_level = args[1] ? atoi(args[1]) : 0;
932 /* read characters from tr.c and pass the rendered lines to out.c */
933 static int render_rec(int level)
935 int c;
936 while (ren_level >= level) {
937 while (!tr_nextreq())
938 if (ren_level < level)
939 break;
940 if (ren_level < level)
941 break;
942 if (ren_aborted)
943 return 1;
944 c = ren_next();
945 if (c < 0) {
946 if (bp_final >= 2)
947 break;
948 if (bp_final == 0) {
949 bp_final = 1;
950 ren_fmtpopall(cfmt);
951 if (trap_em >= 0)
952 trap_exec(trap_em);
953 } else {
954 bp_final = 2;
955 ren_ejectpage(1);
958 if (c >= 0)
959 ren_partial = c != '\n';
960 /* add cwb (the current word) to cfmt */
961 if (c == ' ' || c == '\n') {
962 if (!wb_part(cwb)) { /* not after a \c */
963 while (fmt_word(cfmt, cwb))
964 ren_fmtpop(cfmt);
965 wb_reset(cwb);
966 if (c == '\n')
967 while (fmt_newline(cfmt))
968 ren_fmtpop(cfmt);
969 if (!(n_j & AD_P))
970 ren_fmtpopall(cfmt);
971 ren_fmtpop(cfmt);
972 if (c == ' ')
973 fmt_space(cfmt);
976 /* flush the line if necessary */
977 if (c == ' ' || c == '\n' || c < 0)
978 ren_fmtpop(cfmt);
979 if (c == '\n' || ren_nl) /* end or start of input line */
980 n_lb = f_hpos();
981 if (c == '\n' && n_it && --n_itn == 0)
982 trap_exec(n_it);
983 if (c == '\n' && !wb_part(cwb))
984 n_ce = MAX(0, n_ce - 1);
985 if (c != ' ' && c >= 0) {
986 ren_back(c);
987 ren_char(cwb, ren_next, ren_back);
989 if (c >= 0)
990 ren_nl = c == '\n';
992 return 0;
995 /* render input words */
996 int render(void)
998 n_nl = -1;
999 while (!tr_nextreq())
1001 ren_first(); /* transition to the first page */
1002 render_rec(0);
1003 bp_final = 3;
1004 if (fmt_morewords(cfmt))
1005 ren_page(bp_next, 1);
1006 ren_br();
1007 return 0;
1010 /* trap handling */
1012 #define tposval(i) (tpos[i] < 0 ? n_p + tpos[i] : tpos[i])
1014 static int tpos[NTRAPS]; /* trap positions */
1015 static int treg[NTRAPS]; /* trap registers */
1016 static int ntraps;
1018 static int trap_first(int pos)
1020 int best = -1;
1021 int i;
1022 for (i = 0; i < ntraps; i++)
1023 if (treg[i] >= 0 && tposval(i) > pos)
1024 if (best < 0 || tposval(i) < tposval(best))
1025 best = i;
1026 return best;
1029 static int trap_byreg(int reg)
1031 int i;
1032 for (i = 0; i < ntraps; i++)
1033 if (treg[i] == reg)
1034 return i;
1035 return -1;
1038 static int trap_bypos(int reg, int pos)
1040 int i;
1041 for (i = 0; i < ntraps; i++)
1042 if (treg[i] >= 0 && tposval(i) == pos)
1043 if (reg == -1 || treg[i] == reg)
1044 return i;
1045 return -1;
1048 void tr_wh(char **args)
1050 int reg, pos, id;
1051 if (!args[1])
1052 return;
1053 pos = eval(args[1], 'v');
1054 id = trap_bypos(-1, pos);
1055 if (!args[2]) {
1056 if (id >= 0)
1057 treg[id] = -1;
1058 return;
1060 reg = map(args[2]);
1061 if (id < 0)
1062 id = trap_byreg(-1);
1063 if (id < 0)
1064 id = ntraps++;
1065 tpos[id] = pos;
1066 treg[id] = reg;
1069 void tr_ch(char **args)
1071 int reg;
1072 int id;
1073 if (!args[1])
1074 return;
1075 reg = map(args[1]);
1076 id = trap_byreg(reg);
1077 if (id >= 0)
1078 tpos[id] = args[2] ? eval(args[2], 'v') : -1;
1081 void tr_dt(char **args)
1083 if (!cdiv)
1084 return;
1085 if (args[2]) {
1086 cdiv->tpos = eval(args[1], 'v');
1087 cdiv->treg = map(args[2]);
1088 } else {
1089 cdiv->treg = -1;
1093 void tr_em(char **args)
1095 trap_em = args[1] ? map(args[1]) : -1;
1098 static int trap_pos(int pos)
1100 int ret = trap_first(pos);
1101 if (bp_final >= 3)
1102 return -1;
1103 if (cdiv)
1104 return cdiv->treg && cdiv->tpos > pos ? cdiv->tpos : -1;
1105 return ret >= 0 ? tposval(ret) : -1;
1108 static int trap_reg(int pos)
1110 int ret = trap_first(pos);
1111 if (cdiv)
1112 return cdiv->treg && cdiv->tpos > pos ? cdiv->treg : -1;
1113 return ret >= 0 ? treg[ret] : -1;
1116 int f_nexttrap(void)
1118 int pos = trap_pos(n_d);
1119 if (cdiv)
1120 return pos >= 0 ? pos : 0x7fffffff;
1121 return (pos >= 0 && pos < n_p ? pos : n_p) - n_d;