fmt: shrink word spaces with .ssh request
[neatroff.git] / ren.c
blob0d002cd71cd957378e9362bf05a6829eaa494ffb
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() /* 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 a newline */
30 static int ren_partial; /* reading an input line in render_rec() */
31 static int ren_unbuf[8]; /* ren_back() buffer */
32 static int ren_un;
33 static int ren_aborted; /* .ab executed */
35 static int bp_first = 1; /* prior to the first page */
36 static int bp_next = 1; /* next page number */
37 static int bp_count; /* number of pages so far */
38 static int bp_ejected; /* current ejected page */
39 static int bp_final; /* 1: executing em, 2: the final page, 3: the 2nd final page */
40 static int ren_level; /* the depth of render_rec() calls */
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 fmt_wid(cfmt) + 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 int ren_first(void)
145 if (bp_first && !cdiv) {
146 bp_first = 0;
147 ren_page(bp_next, 1);
148 return 0;
150 return 1;
153 /* when nodiv, do not append .sp to diversions */
154 static void ren_sp(int n, int nodiv)
156 ren_first();
157 /* ignore .sp without arguments when reading diversions */
158 if (!n && ren_div && !n_u)
159 return;
160 n_d += n ? n : n_v;
161 if (n_d > n_h)
162 n_h = n_d;
163 if (cdiv && !nodiv) {
164 sbuf_putnl(&cdiv->sbuf);
165 sbuf_printf(&cdiv->sbuf, "%csp %du\n", c_cc, n ? n : n_v);
166 } else {
167 n_nl = n_d;
171 static int render_rec(int level);
172 static void trap_exec(int reg)
174 char cmd[16];
175 int partial = ren_partial;
176 if (str_get(reg)) {
177 sprintf(cmd, "%c%s %d\n", c_cc, TR_POPREN, ren_level);
178 in_pushnl(cmd, NULL);
179 in_pushnl(str_get(reg), NULL);
180 render_rec(++ren_level);
181 /* executed the trap while in the middle of an input line */
182 if (partial)
183 fmt_suppressnl(cfmt);
187 static int detect_traps(int beg, int end)
189 int pos = trap_pos(beg);
190 return pos >= 0 && (cdiv || pos < n_p) && pos <= end;
193 /* return 1 if executed a trap */
194 static int ren_traps(int beg, int end, int dosp)
196 int pos = trap_pos(beg);
197 if (detect_traps(beg, end)) {
198 if (dosp && pos > beg)
199 ren_sp(pos - beg, 0);
200 trap_exec(trap_reg(beg));
201 return 1;
203 return 0;
206 static int detect_pagelimit(int ne)
208 return !cdiv && n_nl + ne >= n_p;
211 /* start a new page if needed */
212 static int ren_pagelimit(int ne)
214 if (detect_pagelimit(ne)) {
215 ren_page(bp_next, 0);
216 return 1;
218 return 0;
221 /* return 1 if triggered a trap */
222 static int down(int n)
224 if (ren_traps(n_d, n_d + (n ? n : n_v), 1))
225 return 1;
226 ren_sp(n, 0);
227 return ren_pagelimit(0);
230 /* line adjustment */
231 static int ren_ljust(struct sbuf *spre, int w, int ad, int li, int ll)
233 int ljust = li;
234 int llen = ll - ljust;
235 n_n = w;
236 if ((ad & AD_B) == AD_C)
237 ljust += llen > w ? (llen - w) / 2 : 0;
238 if ((ad & AD_B) == AD_R)
239 ljust += llen - w;
240 if (ljust)
241 sbuf_printf(spre, "%ch'%du'", c_ec, ljust);
242 if (cdiv && cdiv->dl < w + ljust)
243 cdiv->dl = w + ljust;
244 return ljust;
247 /* append the line to the current diversion or send it to out.c */
248 static void ren_out(char *beg, char *mid, char *end)
250 if (cdiv) {
251 sbuf_append(&cdiv->sbuf, beg);
252 sbuf_append(&cdiv->sbuf, mid);
253 sbuf_append(&cdiv->sbuf, end);
254 } else {
255 out("H%d\n", n_o);
256 out("V%d\n", n_d);
257 out_line(beg);
258 out_line(mid);
259 out_line(end);
263 static void ren_transparent(char *s)
265 if (cdiv)
266 sbuf_printf(&cdiv->sbuf, "%s\n", s);
267 else
268 out("%s\n", s);
271 static int zwid(void)
273 struct glyph *g = dev_glyph("0", n_f);
274 return charwid(n_f, n_s, g ? g->wid : 0);
277 /* append the line number to the output line */
278 static void ren_lnum(struct sbuf *spre)
280 char num[16] = "";
281 char dig[16] = "";
282 struct wb wb;
283 int i = 0;
284 wb_init(&wb);
285 if (n_nn <= 0 && (n_ln % n_nM) == 0)
286 sprintf(num, "%d", n_ln);
287 wb_hmov(&wb, n_nI * zwid());
288 if (strlen(num) < 3)
289 wb_hmov(&wb, (3 - strlen(num)) * zwid());
290 while (num[i]) {
291 dig[0] = num[i++];
292 wb_put(&wb, dig);
294 wb_hmov(&wb, n_nS * zwid());
295 sbuf_append(spre, sbuf_buf(&wb.sbuf));
296 wb_done(&wb);
297 if (n_nn > 0)
298 n_nn--;
299 else
300 n_ln++;
303 /* append margin character */
304 static void ren_mc(struct sbuf *sbuf, int w, int ljust)
306 struct wb wb;
307 wb_init(&wb);
308 if (w + ljust < n_l + n_mcn)
309 wb_hmov(&wb, n_l + n_mcn - w - ljust);
310 wb_putexpand(&wb, c_mc);
311 sbuf_append(sbuf, sbuf_buf(&wb.sbuf));
312 wb_done(&wb);
315 /* process a line and print it with ren_out() */
316 static int ren_line(char *line, int w, int ad, int body,
317 int li, int ll, int els_neg, int els_pos)
319 struct sbuf sbeg, send;
320 int prev_d, lspc, ljust;
321 ren_first();
322 sbuf_init(&sbeg);
323 sbuf_init(&send);
324 lspc = MAX(1, n_L) * n_v; /* line space, ignoreing \x */
325 prev_d = n_d;
326 if (els_neg)
327 ren_sp(-els_neg, 1);
328 if (!n_ns || line[0] || els_neg || els_pos) {
329 ren_sp(0, 0);
330 if (line[0] && n_nm && body)
331 ren_lnum(&sbeg);
332 ljust = ren_ljust(&sbeg, w, ad, li, ll);
333 if (line[0] && body && n_mc)
334 ren_mc(&send, w, ljust);
335 ren_out(sbuf_buf(&sbeg), line, sbuf_buf(&send));
336 n_ns = 0;
338 sbuf_done(&sbeg);
339 sbuf_done(&send);
340 if (els_pos)
341 ren_sp(els_pos, 1);
342 n_a = els_pos;
343 if (detect_traps(prev_d, n_d) || detect_pagelimit(lspc - n_v)) {
344 if (!ren_pagelimit(lspc - n_v))
345 ren_traps(prev_d, n_d, 0);
346 return 1;
348 if (lspc - n_v && down(lspc - n_v))
349 return 1;
350 return 0;
353 /* read a line from fmt and send it to ren_line() */
354 static int ren_passline(struct fmt *fmt)
356 struct sbuf sbuf;
357 int ll, li, els_neg, els_pos, w, ret;
358 int ad = n_j;
359 ren_first();
360 if (!fmt_morewords(fmt))
361 return 0;
362 sbuf_init(&sbuf);
363 fmt_nextline(fmt, &sbuf, &w, &li, &ll, &els_neg, &els_pos);
364 if ((n_cp && !n_u) || n_na)
365 ad = AD_L;
366 if (n_ce)
367 ad = AD_C;
368 ret = ren_line(sbuf_buf(&sbuf), w, ad, 1, li, ll, els_neg, els_pos);
369 sbuf_done(&sbuf);
370 return ret;
373 /* output formatted lines in fmt */
374 static void ren_fmtpop(struct fmt *fmt)
376 while (fmt_morelines(fmt))
377 ren_passline(fmt);
380 /* format and output all lines in fmt */
381 static void ren_fmtpopall(struct fmt *fmt)
383 while (fmt_fill(fmt))
384 ren_fmtpop(fmt);
385 ren_fmtpop(fmt);
388 /* output current line; returns 1 if triggered a trap */
389 static int ren_br(void)
391 ren_fmtpopall(cfmt);
392 while (fmt_br(cfmt))
393 ren_fmtpop(cfmt);
394 return ren_passline(cfmt);
397 void tr_br(char **args)
399 if (args[0][0] == c_cc)
400 ren_br();
403 void tr_sp(char **args)
405 int traps = 0;
406 int n = args[1] ? eval(args[1], 'v') : n_v;
407 if (args[0][0] == c_cc)
408 traps = ren_br();
409 if (n && !n_ns && !traps)
410 down(n);
413 void tr_sv(char **args)
415 int n = eval(args[1], 'v');
416 n_sv = 0;
417 if (n_d + n < f_nexttrap())
418 down(n);
419 else
420 n_sv = n;
423 void tr_ns(char **args)
425 n_ns = 1;
428 void tr_rs(char **args)
430 n_ns = 0;
433 void tr_os(char **args)
435 if (n_sv)
436 down(n_sv);
437 n_sv = 0;
440 void tr_mk(char **args)
442 if (args[1])
443 num_set(map(args[1]), n_d);
444 else
445 n_mk = n_d;
448 void tr_rt(char **args)
450 int n = args[1] ? eval_re(args[1], n_d, 'v') : n_mk;
451 if (n >= 0 && n < n_d)
452 ren_sp(n - n_d, 0);
455 void tr_ne(char **args)
457 int n = args[1] ? eval(args[1], 'v') : n_v;
458 if (!ren_first())
459 return;
460 if (!ren_traps(n_d, n_d + n - 1, 1))
461 ren_pagelimit(n);
464 static void ren_ejectpage(int br)
466 bp_ejected = bp_count;
467 if (br)
468 ren_br();
469 while (bp_count == bp_ejected && !cdiv) {
470 if (detect_traps(n_d, n_p)) {
471 ren_traps(n_d, n_p, 1);
472 } else {
473 bp_ejected = 0;
474 ren_page(bp_next, 0);
479 void tr_bp(char **args)
481 if (!cdiv && (args[1] || !n_ns)) {
482 if (args[1])
483 bp_next = eval_re(args[1], n_pg, 0);
484 ren_ejectpage(args[0][0] == c_cc);
488 void tr_pn(char **args)
490 if (args[1])
491 bp_next = eval_re(args[1], n_pg, 0);
494 static void ren_ps(char *s)
496 int ps = !s || !*s || !strcmp("0", s) ? n_s0 : eval_re(s, n_s, 0);
497 n_s0 = n_s;
498 n_s = MAX(1, ps);
501 void tr_ps(char **args)
503 ren_ps(args[1]);
506 void tr_ll(char **args)
508 int ll = args[1] ? eval_re(args[1], n_l, 'm') : n_l0;
509 n_l0 = n_l;
510 n_l = MAX(0, ll);
513 void tr_in(char **args)
515 int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0;
516 if (args[0][0] == c_cc)
517 ren_br();
518 n_i0 = n_i;
519 n_i = MAX(0, in);
520 n_ti = -1;
523 void tr_ti(char **args)
525 if (args[0][0] == c_cc)
526 ren_br();
527 if (args[1])
528 n_ti = eval_re(args[1], n_i, 'm');
531 static void ren_ft(char *s)
533 int fn = !s || !*s || !strcmp("P", s) ? n_f0 : dev_pos(s);
534 if (fn < 0) {
535 errmsg("neatroff: failed to mount <%s>\n", s);
536 } else {
537 n_f0 = n_f;
538 n_f = fn;
542 void tr_ft(char **args)
544 ren_ft(args[1]);
547 void tr_fp(char **args)
549 if (!args[2])
550 return;
551 if (dev_mnt(atoi(args[1]), args[2], args[3] ? args[3] : args[2]) < 0)
552 errmsg("neatroff: failed to mount <%s>\n", args[2]);
555 void tr_nf(char **args)
557 if (args[0][0] == c_cc)
558 ren_br();
559 n_u = 0;
562 void tr_fi(char **args)
564 if (args[0][0] == c_cc)
565 ren_br();
566 n_u = 1;
569 void tr_ce(char **args)
571 if (args[0][0] == c_cc)
572 ren_br();
573 n_ce = args[1] ? atoi(args[1]) : 1;
576 void tr_fc(char **args)
578 char *fa = args[1];
579 char *fb = args[2];
580 if (fa && charread(&fa, c_fa) >= 0) {
581 if (!fb || charread(&fb, c_fb) < 0)
582 strcpy(c_fb, " ");
583 } else {
584 c_fa[0] = '\0';
585 c_fb[0] = '\0';
589 static void ren_cl(char *s)
591 int m = !s || !*s ? n_m0 : clr_get(s);
592 n_m0 = n_m;
593 n_m = m;
596 void tr_cl(char **args)
598 ren_cl(args[1]);
601 void tr_ab(char **args)
603 fprintf(stderr, "%s\n", args[1]);
604 ren_aborted = 1;
607 static void ren_cmd(struct wb *wb, int c, char *arg)
609 switch (c) {
610 case ' ':
611 wb_hmov(wb, spacewid(n_f, n_s));
612 break;
613 case 'b':
614 ren_bcmd(wb, arg);
615 break;
616 case 'c':
617 wb_setpart(wb);
618 break;
619 case 'D':
620 ren_dcmd(wb, arg);
621 break;
622 case 'd':
623 wb_vmov(wb, SC_EM / 2);
624 break;
625 case 'f':
626 ren_ft(arg);
627 break;
628 case 'h':
629 wb_hmov(wb, eval(arg, 'm'));
630 break;
631 case 'k':
632 num_set(map(arg), RENWB(wb) ? f_hpos() - n_lb : wb_wid(wb));
633 break;
634 case 'L':
635 ren_vlcmd(wb, arg);
636 break;
637 case 'l':
638 ren_hlcmd(wb, arg);
639 break;
640 case 'm':
641 ren_cl(arg);
642 break;
643 case 'o':
644 ren_ocmd(wb, arg);
645 break;
646 case 'p':
647 if (RENWB(wb))
648 while (fmt_fillreq(cfmt))
649 ren_fmtpop(cfmt);
650 break;
651 case 'r':
652 wb_vmov(wb, -SC_EM);
653 break;
654 case 's':
655 ren_ps(arg);
656 break;
657 case 'u':
658 wb_vmov(wb, -SC_EM / 2);
659 break;
660 case 'v':
661 wb_vmov(wb, eval(arg, 'v'));
662 break;
663 case 'X':
664 wb_etc(wb, arg);
665 break;
666 case 'x':
667 wb_els(wb, eval(arg, 'v'));
668 break;
669 case '0':
670 wb_hmov(wb, zwid());
671 break;
672 case '|':
673 wb_hmov(wb, SC_EM / 6);
674 break;
675 case '&':
676 wb_hmov(wb, 0);
677 break;
678 case '^':
679 wb_hmov(wb, SC_EM / 12);
680 break;
681 case '/':
682 wb_italiccorrection(wb);
683 break;
684 case ',':
685 wb_italiccorrectionleft(wb);
686 break;
687 case '{':
688 case '}':
689 break;
693 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int));
694 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int));
696 /* insert a character, escape sequence, field or etc into wb */
697 static void ren_put(struct wb *wb, char *c, int (*next)(void), void (*back)(int))
699 char arg[ILNLEN];
700 char *s;
701 int w, n;
702 if (c[0] == ' ' || c[0] == '\n') {
703 wb_put(wb, c);
704 return;
706 if (c[0] == '\t' || c[0] == '\x01') {
707 ren_tab(wb, c[0] == '\t' ? c_tc : c_lc, next, back);
708 return;
710 if (c_fa[0] && !strcmp(c_fa, c)) {
711 ren_field(wb, next, back);
712 return;
714 if (c[0] == c_ec) {
715 if (c[1] == 'z') {
716 w = wb_wid(wb);
717 ren_char(wb, next, back);
718 wb_hmov(wb, w - wb_wid(wb));
719 return;
721 if (c[1] == '!') {
722 if (ren_nl && next == ren_next) {
723 s = arg;
724 n = next();
725 while (n >= 0 && n != '\n') {
726 *s++ = n;
727 n = next();
729 *s = '\0';
730 ren_transparent(arg);
732 return;
734 if (strchr(" bCcDdfHhkLlmNoprSsuvXxz0^|{}&/,", c[1])) {
735 argnext(arg, c[1], next, back);
736 if (c[1] == 'S' || c[1] == 'H')
737 return; /* not implemented */
738 if (c[1] != 'N') {
739 ren_cmd(wb, c[1], arg);
740 return;
742 snprintf(c, GNLEN, "GID=%s", arg);
745 if (!ren_div && cdef_map(c, n_f)) { /* .char characters */
746 wb_putexpand(wb, c);
747 return;
749 if (!n_lg || ren_div || wb_lig(wb, c)) {
750 if (n_kn && !ren_div)
751 wb_kern(wb, c);
752 wb_put(wb, c);
756 /* read one character and place it inside wb buffer */
757 int ren_char(struct wb *wb, int (*next)(void), void (*back)(int))
759 char c[GNLEN * 4];
760 if (charnext(c, next, back) < 0)
761 return -1;
762 ren_put(wb, c, next, back);
763 return 0;
766 /* like ren_char(); return 1 if d1 was read and d2 if d2 was read */
767 static int ren_chardel(struct wb *wb, int (*next)(void), void (*back)(int),
768 char *d1, char *d2)
770 char c[GNLEN * 4];
771 if (charnext(c, next, back) < 0)
772 return -1;
773 if (d1 && !strcmp(d1, c))
774 return 1;
775 if (d2 && !strcmp(d2, c))
776 return 2;
777 ren_put(wb, c, next, back);
778 return 0;
781 /* read the argument of \w and push its width */
782 int ren_wid(int (*next)(void), void (*back)(int))
784 char delim[GNLEN];
785 int c, n;
786 struct wb wb;
787 wb_init(&wb);
788 charnext(delim, next, back);
789 odiv_beg();
790 c = next();
791 while (c >= 0 && c != '\n') {
792 back(c);
793 if (ren_chardel(&wb, next, back, delim, NULL))
794 break;
795 c = next();
797 odiv_end();
798 n = wb_wid(&wb);
799 wb_wconf(&wb, &n_ct, &n_st, &n_sb, &n_llx, &n_lly, &n_urx, &n_ury);
800 wb_done(&wb);
801 return n;
804 /* return 1 if d1 was read and 2 if d2 was read */
805 static int ren_until(struct wb *wb, char *d1, char *d2,
806 int (*next)(void), void (*back)(int))
808 int c, ret;
809 c = next();
810 while (c >= 0 && c != '\n') {
811 back(c);
812 ret = ren_chardel(wb, next, back, d1, d2);
813 if (ret)
814 return ret;
815 c = next();
817 if (c == '\n')
818 back(c);
819 return 0;
822 static void wb_cpy(struct wb *dst, struct wb *src, int left)
824 wb_hmov(dst, left - wb_wid(dst));
825 wb_cat(dst, src);
828 void ren_tl(int (*next)(void), void (*back)(int))
830 struct wb wb, wb2;
831 char delim[GNLEN];
832 wb_init(&wb);
833 wb_init(&wb2);
834 charnext(delim, next, back);
835 /* the left-adjusted string */
836 ren_until(&wb2, delim, NULL, next, back);
837 wb_cpy(&wb, &wb2, 0);
838 /* the centered string */
839 ren_until(&wb2, delim, NULL, next, back);
840 wb_cpy(&wb, &wb2, (n_lt - wb_wid(&wb2)) / 2);
841 /* the right-adjusted string */
842 ren_until(&wb2, delim, NULL, next, back);
843 wb_cpy(&wb, &wb2, n_lt - wb_wid(&wb2));
844 /* flushing the line */
845 ren_line(sbuf_buf(&wb.sbuf), wb_wid(&wb), AD_L, 0,
846 0, n_lt, wb.els_neg, wb.els_pos);
847 wb_done(&wb2);
848 wb_done(&wb);
851 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int))
853 struct wb wbs[NFIELDS];
854 int i, n = 0;
855 int wid = 0;
856 int left, right, cur_left;
857 int pad, rem;
858 while (n < LEN(wbs)) {
859 wb_init(&wbs[n]);
860 if (ren_until(&wbs[n++], c_fb, c_fa, next, back) != 1)
861 break;
863 left = RENWB(wb) ? f_hpos() : wb_wid(wb);
864 right = tab_next(left);
865 for (i = 0; i < n; i++)
866 wid += wb_wid(&wbs[i]);
867 pad = (right - left - wid) / (n > 1 ? n - 1 : 1);
868 rem = (right - left - wid) % (n > 1 ? n - 1 : 1);
869 for (i = 0; i < n; i++) {
870 if (i == 0)
871 cur_left = left;
872 else if (i == n - 1)
873 cur_left = right - wb_wid(&wbs[i]);
874 else
875 cur_left = wb_wid(wb) + pad + (i + rem >= n);
876 wb_cpy(wb, &wbs[i], cur_left);
877 wb_done(&wbs[i]);
881 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int))
883 struct wb t;
884 int pos = RENWB(wb) ? f_hpos() : wb_wid(wb);
885 int ins = tab_next(pos); /* insertion position */
886 int typ = tab_type(pos); /* tab type */
887 int c;
888 wb_init(&t);
889 if (typ == 'R' || typ == 'C') {
890 c = next();
891 while (c >= 0 && c != '\n' && c != '\t' && c != '\x01') {
892 back(c);
893 ren_char(&t, next, back);
894 c = next();
896 back(c);
898 if (typ == 'C')
899 ins -= wb_wid(&t) / 2;
900 if (typ == 'R')
901 ins -= wb_wid(&t);
902 if (!tc[0] || ins <= pos)
903 wb_hmov(wb, ins - pos);
904 else
905 ren_hline(wb, ins - pos, tc);
906 wb_cat(wb, &t);
907 wb_done(&t);
910 /* parse characters and troff requests of s and append them to wb */
911 int ren_parse(struct wb *wb, char *s)
913 int c;
914 odiv_beg();
915 sstr_push(s);
916 c = sstr_next();
917 while (c >= 0) {
918 sstr_back(c);
919 if (ren_char(wb, sstr_next, sstr_back))
920 break;
921 c = sstr_next();
923 sstr_pop();
924 odiv_end();
925 return 0;
928 /* cause nested render_rec() to exit */
929 void tr_popren(char **args)
931 ren_level = args[1] ? atoi(args[1]) : 0;
934 /* read characters from tr.c and pass the rendered lines to out.c */
935 static int render_rec(int level)
937 struct wb *wb = &ren_wb;
938 int c;
939 while (ren_level >= level) {
940 while (!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 wb (the current word) to cfmt */
964 if (c == ' ' || c == '\n') {
965 if (!wb_part(wb)) { /* not after a \c */
966 while (fmt_word(cfmt, wb))
967 ren_fmtpop(cfmt);
968 wb_reset(wb);
969 if (c == '\n')
970 while (fmt_newline(cfmt))
971 ren_fmtpop(cfmt);
972 if (!(n_j & AD_P))
973 ren_fmtpopall(cfmt);
974 ren_fmtpop(cfmt);
975 if (c == ' ')
976 fmt_space(cfmt);
979 /* flush the line if necessary */
980 if (c == ' ' || c == '\n' || c < 0)
981 ren_fmtpop(cfmt);
982 if (c == '\n' || ren_nl) /* end or start of input line */
983 n_lb = f_hpos();
984 if (c == '\n' && n_it && --n_itn == 0)
985 trap_exec(n_it);
986 if (c == '\n' && !wb_part(wb))
987 n_ce = MAX(0, n_ce - 1);
988 if (c != ' ' && c >= 0) {
989 ren_back(c);
990 ren_char(wb, ren_next, ren_back);
992 if (c >= 0)
993 ren_nl = c == '\n';
995 return 0;
998 /* render input words */
999 int render(void)
1001 struct wb *wb = &ren_wb;
1002 n_nl = -1;
1003 wb_init(wb);
1004 while (!tr_nextreq())
1006 ren_first(); /* transition to the first page */
1007 render_rec(0);
1008 bp_final = 3;
1009 if (fmt_morewords(cfmt))
1010 ren_page(bp_next, 1);
1011 ren_br();
1012 wb_done(wb);
1013 return 0;
1016 /* trap handling */
1018 #define tposval(i) (tpos[i] < 0 ? n_p + tpos[i] : tpos[i])
1020 static int tpos[NTRAPS]; /* trap positions */
1021 static int treg[NTRAPS]; /* trap registers */
1022 static int ntraps;
1024 static int trap_first(int pos)
1026 int best = -1;
1027 int i;
1028 for (i = 0; i < ntraps; i++)
1029 if (treg[i] >= 0 && tposval(i) > pos)
1030 if (best < 0 || tposval(i) < tposval(best))
1031 best = i;
1032 return best;
1035 static int trap_byreg(int reg)
1037 int i;
1038 for (i = 0; i < ntraps; i++)
1039 if (treg[i] == reg)
1040 return i;
1041 return -1;
1044 static int trap_bypos(int reg, int pos)
1046 int i;
1047 for (i = 0; i < ntraps; i++)
1048 if (treg[i] >= 0 && tposval(i) == pos)
1049 if (reg == -1 || treg[i] == reg)
1050 return i;
1051 return -1;
1054 void tr_wh(char **args)
1056 int reg, pos, id;
1057 if (!args[1])
1058 return;
1059 pos = eval(args[1], 'v');
1060 id = trap_bypos(-1, pos);
1061 if (!args[2]) {
1062 if (id >= 0)
1063 treg[id] = -1;
1064 return;
1066 reg = map(args[2]);
1067 if (id < 0)
1068 id = trap_byreg(-1);
1069 if (id < 0)
1070 id = ntraps++;
1071 tpos[id] = pos;
1072 treg[id] = reg;
1075 void tr_ch(char **args)
1077 int reg;
1078 int id;
1079 if (!args[1])
1080 return;
1081 reg = map(args[1]);
1082 id = trap_byreg(reg);
1083 if (id >= 0)
1084 tpos[id] = args[2] ? eval(args[2], 'v') : -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;