ren: assigning \n% should set the next page number
[neatroff.git] / ren.c
blob6b499a300e3d5dcde1abc7bc699fd6bdcb13666e
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 NOPAGE 0x40000000 /* undefined bp_next */
9 #define cfmt env_fmt() /* current formatter */
10 #define cwb env_wb() /* current word buffer */
12 /* diversions */
13 struct div {
14 struct sbuf sbuf; /* diversion output */
15 int reg; /* diversion register */
16 int tpos; /* diversion trap position */
17 int treg; /* diversion trap register */
18 int dl; /* diversion width */
19 int prev_d; /* previous \n(.d value */
20 int prev_h; /* previous \n(.h value */
21 int prev_mk; /* previous .mk internal register */
22 int prev_ns; /* previous .ns value */
24 static struct div divs[NPREV]; /* diversion stack */
25 static struct div *cdiv; /* current diversion */
26 static int ren_div; /* rendering a diversion */
27 static int trap_em = -1; /* end macro */
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 = NOPAGE; /* 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 f_divreg(void)
91 return cdiv ? cdiv->reg : -1;
94 int f_hpos(void)
96 return fmt_wid(cfmt) + wb_wid(cwb);
99 void tr_divbeg(char **args)
101 odiv_beg();
102 ren_div++;
105 void tr_divend(char **args)
107 if (ren_div <= 0)
108 errdie("neatroff: diversion stack empty\n");
109 odiv_end();
110 ren_div--;
113 static int trap_reg(int pos);
114 static int trap_pos(int pos);
115 static void trap_exec(int reg);
117 static void ren_page(int force)
119 if (!force && bp_final >= 2)
120 return;
121 n_nl = 0;
122 n_d = 0;
123 n_h = 0;
124 n_pg = bp_next != NOPAGE ? bp_next : n_pg + 1;
125 bp_next = NOPAGE;
126 bp_count++;
127 out("p%d\n", n_pg);
128 out("V%d\n", 0);
129 if (trap_pos(-1) == 0)
130 trap_exec(trap_reg(-1));
133 static int ren_first(void)
135 if (bp_first && !cdiv) {
136 bp_first = 0;
137 ren_page(1);
138 return 0;
140 return 1;
143 /* when nodiv, do not append .sp to diversions */
144 static void ren_sp(int n, int nodiv)
146 ren_first();
147 /* ignore .sp without arguments when reading diversions */
148 if (!n && ren_div && !n_u)
149 return;
150 n_d += n ? n : n_v;
151 if (n_d > n_h)
152 n_h = n_d;
153 if (cdiv && !nodiv) {
154 sbuf_putnl(&cdiv->sbuf);
155 sbuf_printf(&cdiv->sbuf, "%csp %du\n", c_cc, n ? n : n_v);
156 } else {
157 n_nl = n_d;
161 static int render_rec(int level);
162 static void trap_exec(int reg)
164 char cmd[16];
165 int partial = ren_partial && (!ren_un || ren_unbuf[0] != '\n');
166 if (str_get(reg)) {
167 sprintf(cmd, "%c%s %d\n", c_cc, TR_POPREN, ren_level);
168 in_push(cmd, NULL);
169 in_push(str_get(reg), NULL);
170 if (partial)
171 in_push("\n", NULL);
172 render_rec(++ren_level);
173 /* executed the trap while in the middle of an input line */
174 if (partial)
175 fmt_suppressnl(cfmt);
179 static int detect_traps(int beg, int end)
181 int pos = trap_pos(beg);
182 return pos >= 0 && (cdiv || pos < n_p) && pos <= end;
185 /* return 1 if executed a trap */
186 static int ren_traps(int beg, int end, int dosp)
188 int pos = trap_pos(beg);
189 if (detect_traps(beg, end)) {
190 if (dosp && pos > beg)
191 ren_sp(pos - beg, 0);
192 trap_exec(trap_reg(beg));
193 return 1;
195 return 0;
198 static int detect_pagelimit(int ne)
200 return !cdiv && n_nl + ne >= n_p;
203 /* start a new page if needed */
204 static int ren_pagelimit(int ne)
206 if (detect_pagelimit(ne)) {
207 ren_page(0);
208 return 1;
210 return 0;
213 /* return 1 if triggered a trap */
214 static int down(int n)
216 if (ren_traps(n_d, n_d + (n ? n : n_v), 1))
217 return 1;
218 ren_sp(n, 0);
219 return ren_pagelimit(0);
222 /* line adjustment */
223 static int ren_ljust(struct sbuf *spre, int w, int ad, int li, int ll)
225 int ljust = li;
226 int llen = ll - ljust;
227 n_n = w;
228 if ((ad & AD_B) == AD_C)
229 ljust += llen > w ? (llen - w) / 2 : 0;
230 if ((ad & AD_B) == AD_R)
231 ljust += llen - w;
232 if (ljust)
233 sbuf_printf(spre, "%ch'%du'", c_ec, ljust);
234 if (cdiv && cdiv->dl < w + ljust)
235 cdiv->dl = w + ljust;
236 return ljust;
239 /* append the line to the current diversion or send it to out.c */
240 static void ren_out(char *beg, char *mid, char *end)
242 if (cdiv) {
243 sbuf_append(&cdiv->sbuf, beg);
244 sbuf_append(&cdiv->sbuf, mid);
245 sbuf_append(&cdiv->sbuf, end);
246 } else {
247 out("H%d\n", n_o);
248 out("V%d\n", n_d);
249 out_line(beg);
250 out_line(mid);
251 out_line(end);
255 static void ren_transparent(char *s)
257 if (cdiv)
258 sbuf_printf(&cdiv->sbuf, "%s\n", s);
259 else
260 out("%s\n", s);
263 static int zwid(void)
265 struct glyph *g = dev_glyph("0", n_f);
266 return g ? font_gwid(g->font, dev_font(n_f), n_s, g->wid) : 0;
269 /* append the line number to the output line */
270 static void ren_lnum(struct sbuf *spre)
272 char num[16] = "";
273 char dig[16] = "";
274 struct wb wb;
275 int i = 0;
276 wb_init(&wb);
277 if (n_nn <= 0 && (n_ln % n_nM) == 0)
278 sprintf(num, "%d", n_ln);
279 wb_hmov(&wb, n_nI * zwid());
280 if (strlen(num) < 3)
281 wb_hmov(&wb, (3 - strlen(num)) * zwid());
282 while (num[i]) {
283 dig[0] = num[i++];
284 wb_put(&wb, dig);
286 wb_hmov(&wb, n_nS * zwid());
287 sbuf_append(spre, wb_buf(&wb));
288 wb_done(&wb);
289 if (n_nn > 0)
290 n_nn--;
291 else
292 n_ln++;
295 /* append margin character */
296 static void ren_mc(struct sbuf *sbuf, int w, int ljust)
298 struct wb wb;
299 wb_init(&wb);
300 if (w + ljust < n_l + n_mcn)
301 wb_hmov(&wb, n_l + n_mcn - w - ljust);
302 wb_putexpand(&wb, c_mc);
303 sbuf_append(sbuf, wb_buf(&wb));
304 wb_done(&wb);
307 /* process a line and print it with ren_out() */
308 static int ren_line(char *line, int w, int ad, int body,
309 int li, int ll, int els_neg, int els_pos)
311 struct sbuf sbeg, send;
312 int prev_d, lspc, ljust;
313 ren_first();
314 sbuf_init(&sbeg);
315 sbuf_init(&send);
316 lspc = MAX(1, n_L) * n_v; /* line space, ignoreing \x */
317 prev_d = n_d;
318 if (els_neg)
319 ren_sp(-els_neg, 1);
320 if (!n_ns || line[0] || els_neg || els_pos) {
321 ren_sp(0, 0);
322 if (line[0] && n_nm && body)
323 ren_lnum(&sbeg);
324 ljust = ren_ljust(&sbeg, w, ad, li, ll);
325 if (line[0] && body && n_mc)
326 ren_mc(&send, w, ljust);
327 ren_out(sbuf_buf(&sbeg), line, sbuf_buf(&send));
328 n_ns = 0;
330 sbuf_done(&sbeg);
331 sbuf_done(&send);
332 if (els_pos)
333 ren_sp(els_pos, 1);
334 n_a = els_pos;
335 if (detect_traps(prev_d, n_d) || detect_pagelimit(lspc - n_v)) {
336 if (!ren_pagelimit(lspc - n_v))
337 ren_traps(prev_d, n_d, 0);
338 return 1;
340 if (lspc - n_v && down(lspc - n_v))
341 return 1;
342 return 0;
345 /* read a line from fmt and send it to ren_line() */
346 static int ren_passline(struct fmt *fmt)
348 struct sbuf sbuf;
349 int ll, li, els_neg, els_pos, w, ret;
350 int ad = n_j;
351 ren_first();
352 if (!fmt_morewords(fmt))
353 return 0;
354 sbuf_init(&sbuf);
355 fmt_nextline(fmt, &sbuf, &w, &li, &ll, &els_neg, &els_pos);
356 if ((n_cp && !n_u) || n_na)
357 ad = AD_L;
358 if (n_ce)
359 ad = AD_C;
360 ret = ren_line(sbuf_buf(&sbuf), w, ad, 1, li, ll, els_neg, els_pos);
361 sbuf_done(&sbuf);
362 return ret;
365 /* output formatted lines in fmt */
366 static void ren_fmtpop(struct fmt *fmt)
368 while (fmt_morelines(fmt))
369 ren_passline(fmt);
372 /* format and output all lines in fmt */
373 static void ren_fmtpopall(struct fmt *fmt)
375 while (fmt_fill(fmt))
376 ren_fmtpop(fmt);
377 ren_fmtpop(fmt);
380 /* pass the given word buffer to the current line buffer (cfmt) */
381 static void ren_fmtword(struct wb *wb)
383 while (fmt_word(cfmt, wb))
384 ren_fmtpop(cfmt);
385 wb_reset(wb);
388 /* output current line; returns 1 if triggered a trap */
389 static int ren_br(void)
391 ren_first();
392 ren_fmtword(cwb);
393 ren_fmtpopall(cfmt);
394 while (fmt_br(cfmt))
395 ren_fmtpop(cfmt);
396 return ren_passline(cfmt);
399 void tr_br(char **args)
401 ren_fmtpopall(cfmt); /* output the completed lines first */
402 if (args[0][0] == c_cc)
403 ren_br();
406 void tr_sp(char **args)
408 int traps = 0;
409 int n = args[1] ? eval(args[1], 'v') : n_v;
410 if (args[0][0] == c_cc)
411 traps = ren_br();
412 if (n && !n_ns && !traps)
413 down(n);
416 void tr_sv(char **args)
418 int n = eval(args[1], 'v');
419 n_sv = 0;
420 if (n_d + n < f_nexttrap())
421 down(n);
422 else
423 n_sv = n;
426 void tr_ns(char **args)
428 n_ns = 1;
431 void tr_rs(char **args)
433 n_ns = 0;
436 void tr_os(char **args)
438 if (n_sv)
439 down(n_sv);
440 n_sv = 0;
443 void tr_mk(char **args)
445 if (args[1])
446 num_set(map(args[1]), n_d);
447 else
448 n_mk = n_d;
451 void tr_rt(char **args)
453 int n = args[1] ? eval_re(args[1], n_d, 'v') : n_mk;
454 if (n >= 0 && n < n_d)
455 ren_sp(n - n_d, 0);
458 void tr_ne(char **args)
460 int n = args[1] ? eval(args[1], 'v') : n_v;
461 if (!ren_first())
462 return;
463 if (!ren_traps(n_d, n_d + n - 1, 1))
464 ren_pagelimit(n);
467 static void ren_ejectpage(int br)
469 bp_ejected = bp_count;
470 if (br)
471 ren_br();
472 while (bp_count == bp_ejected && !cdiv) {
473 if (detect_traps(n_d, n_p)) {
474 ren_traps(n_d, n_p, 1);
475 } else {
476 bp_ejected = 0;
477 ren_page(0);
482 void tr_bp(char **args)
484 if (!cdiv && (args[1] || !n_ns)) {
485 if (args[1])
486 bp_next = eval_re(args[1], n_pg, 0);
487 ren_ejectpage(args[0][0] == c_cc);
491 void tr_pn(char **args)
493 if (args[1])
494 bp_next = eval_re(args[1], n_pg, 0);
497 static void ren_ps(char *s)
499 int ps = !s || !*s || !strcmp("0", s) ?
500 n_s0 * SC_PT : eval_re(s, n_s * SC_PT, 'p');
501 n_s0 = n_s;
502 n_s = MAX(1, (ps + SC_PT / 2) / SC_PT);
505 void tr_ps(char **args)
507 ren_ps(args[1]);
510 void tr_ll(char **args)
512 int ll = args[1] ? eval_re(args[1], n_l, 'm') : n_l0;
513 n_l0 = n_l;
514 n_l = MAX(0, ll);
517 void tr_in(char **args)
519 int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0;
520 if (args[0][0] == c_cc)
521 ren_br();
522 n_i0 = n_i;
523 n_i = MAX(0, in);
524 n_ti = -1;
527 void tr_ti(char **args)
529 if (args[0][0] == c_cc)
530 ren_br();
531 if (args[1])
532 n_ti = eval_re(args[1], n_i, 'm');
535 static void ren_ft(char *s)
537 int fn = !s || !*s || !strcmp("P", s) ? n_f0 : dev_pos(s);
538 if (fn < 0) {
539 errmsg("neatroff: failed to mount <%s>\n", s);
540 } else {
541 n_f0 = n_f;
542 n_f = fn;
546 void tr_ft(char **args)
548 ren_ft(args[1]);
551 void tr_fp(char **args)
553 if (!args[2])
554 return;
555 if (dev_mnt(atoi(args[1]), args[2], args[3] ? args[3] : args[2]) < 0)
556 errmsg("neatroff: failed to mount <%s>\n", args[2]);
559 void tr_nf(char **args)
561 if (args[0][0] == c_cc)
562 ren_br();
563 n_u = 0;
566 void tr_fi(char **args)
568 if (args[0][0] == c_cc)
569 ren_br();
570 n_u = 1;
573 void tr_ce(char **args)
575 if (args[0][0] == c_cc)
576 ren_br();
577 n_ce = args[1] ? atoi(args[1]) : 1;
580 void tr_fc(char **args)
582 char *fa = args[1];
583 char *fb = args[2];
584 if (fa && charread(&fa, c_fa) >= 0) {
585 if (!fb || charread(&fb, c_fb) < 0)
586 strcpy(c_fb, " ");
587 } else {
588 c_fa[0] = '\0';
589 c_fb[0] = '\0';
593 static void ren_cl(char *s)
595 int m = !s || !*s ? n_m0 : clr_get(s);
596 n_m0 = n_m;
597 n_m = m;
600 void tr_cl(char **args)
602 ren_cl(args[1]);
605 void tr_ab(char **args)
607 fprintf(stderr, "%s\n", args[1]);
608 ren_aborted = 1;
611 static void ren_cmd(struct wb *wb, int c, char *arg)
613 switch (c) {
614 case ' ':
615 wb_hmov(wb, font_swid(dev_font(n_f), n_s, n_ss));
616 break;
617 case 'b':
618 ren_bcmd(wb, arg);
619 break;
620 case 'c':
621 wb_setpart(wb);
622 break;
623 case 'D':
624 ren_dcmd(wb, arg);
625 break;
626 case 'd':
627 wb_vmov(wb, SC_EM / 2);
628 break;
629 case 'f':
630 ren_ft(arg);
631 break;
632 case 'h':
633 wb_hmov(wb, eval(arg, 'm'));
634 break;
635 case 'k':
636 num_set(map(arg), wb == cwb ? f_hpos() - n_lb : wb_wid(wb));
637 break;
638 case 'L':
639 ren_vlcmd(wb, arg);
640 break;
641 case 'l':
642 ren_hlcmd(wb, arg);
643 break;
644 case 'm':
645 ren_cl(arg);
646 break;
647 case 'o':
648 ren_ocmd(wb, arg);
649 break;
650 case 'p':
651 if (wb == cwb)
652 while (fmt_fillreq(cfmt))
653 ren_fmtpop(cfmt);
654 break;
655 case 'r':
656 wb_vmov(wb, -SC_EM);
657 break;
658 case 's':
659 ren_ps(arg);
660 break;
661 case 'u':
662 wb_vmov(wb, -SC_EM / 2);
663 break;
664 case 'v':
665 wb_vmov(wb, eval(arg, 'v'));
666 break;
667 case 'X':
668 wb_etc(wb, arg);
669 break;
670 case 'x':
671 wb_els(wb, eval(arg, 'v'));
672 break;
673 case 'Z':
674 ren_zcmd(wb, arg);
675 break;
676 case '0':
677 wb_hmov(wb, zwid());
678 break;
679 case '|':
680 wb_hmov(wb, SC_EM / 6);
681 break;
682 case '&':
683 wb_hmov(wb, 0);
684 break;
685 case '^':
686 wb_hmov(wb, SC_EM / 12);
687 break;
688 case '/':
689 wb_italiccorrection(wb);
690 break;
691 case ',':
692 wb_italiccorrectionleft(wb);
693 break;
697 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int));
698 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int));
700 /* insert a character, escape sequence, field or etc into wb */
701 static void ren_put(struct wb *wb, char *c, int (*next)(void), void (*back)(int))
703 char arg[ILNLEN];
704 char *s;
705 int w, n;
706 if (c[0] == ' ' || c[0] == '\n') {
707 wb_put(wb, c);
708 return;
710 if (c[0] == '\t' || c[0] == '\x01') {
711 ren_tab(wb, c[0] == '\t' ? c_tc : c_lc, next, back);
712 return;
714 if (c_fa[0] && !strcmp(c_fa, c)) {
715 ren_field(wb, next, back);
716 return;
718 if (c[0] == c_ec) {
719 if (c[1] == 'z') {
720 w = wb_wid(wb);
721 ren_char(wb, next, back);
722 wb_hmov(wb, w - wb_wid(wb));
723 return;
725 if (c[1] == '!') {
726 if (ren_nl && next == ren_next) {
727 s = arg;
728 n = next();
729 while (n >= 0 && n != '\n') {
730 *s++ = n;
731 n = next();
733 *s = '\0';
734 ren_transparent(arg);
736 return;
738 if (strchr(" bCcDdefHhkLlmNoprSsuvXxZz0^|{}&/,", c[1])) {
739 arg[0] = '\0';
740 if (strchr(ESC_P, c[1]))
741 unquotednext(arg, c[1], next, back);
742 if (strchr(ESC_Q, c[1]))
743 quotednext(arg, next, back);
744 if (c[1] == 'e') {
745 snprintf(c, GNLEN, "%c%c", c_ec, c_ec);
746 } else if (c[1] == 'N') {
747 snprintf(c, GNLEN, "GID=%s", arg);
748 } else {
749 ren_cmd(wb, c[1], arg);
750 return;
754 if (ren_div) {
755 wb_putraw(wb, c);
756 return;
758 if (cdef_map(c, n_f)) /* .char characters */
759 wb_putexpand(wb, c);
760 else
761 wb_put(wb, c);
764 /* read one character and place it inside wb buffer */
765 int ren_char(struct wb *wb, int (*next)(void), void (*back)(int))
767 char c[GNLEN * 4];
768 if (charnext(c, next, back) < 0)
769 return -1;
770 ren_put(wb, c, next, back);
771 return 0;
774 /* like ren_char(); return 1 if d1 was read and 2 if d2 was read */
775 static int ren_chardel(struct wb *wb, int (*next)(void), void (*back)(int),
776 char *d1, char *d2)
778 char c[GNLEN * 4];
779 if (charnext(c, next, back) < 0)
780 return -1;
781 if (d1 && !strcmp(d1, c))
782 return 1;
783 if (d2 && !strcmp(d2, c))
784 return 2;
785 ren_put(wb, c, next, back);
786 return 0;
789 /* read the argument of \w and push its width */
790 int ren_wid(int (*next)(void), void (*back)(int))
792 char delim[GNLEN];
793 int c, n;
794 struct wb wb;
795 wb_init(&wb);
796 charnext(delim, next, back);
797 odiv_beg();
798 c = next();
799 while (c >= 0 && c != '\n') {
800 back(c);
801 if (ren_chardel(&wb, next, back, delim, NULL))
802 break;
803 c = next();
805 odiv_end();
806 n = wb_wid(&wb);
807 wb_wconf(&wb, &n_ct, &n_st, &n_sb, &n_llx, &n_lly, &n_urx, &n_ury);
808 wb_done(&wb);
809 return n;
812 /* return 1 if d1 was read and 2 if d2 was read */
813 static int ren_until(struct wb *wb, char *d1, char *d2,
814 int (*next)(void), void (*back)(int))
816 int c, ret;
817 c = next();
818 while (c >= 0 && c != '\n') {
819 back(c);
820 ret = ren_chardel(wb, next, back, d1, d2);
821 if (ret)
822 return ret;
823 c = next();
825 if (c == '\n')
826 back(c);
827 return 0;
830 static void wb_cpy(struct wb *dst, struct wb *src, int left)
832 wb_hmov(dst, left - wb_wid(dst));
833 wb_cat(dst, src);
836 void ren_tl(int (*next)(void), void (*back)(int))
838 struct wb wb, wb2;
839 char delim[GNLEN];
840 wb_init(&wb);
841 wb_init(&wb2);
842 charnext(delim, next, back);
843 if (!strcmp("\n", delim))
844 back('\n');
845 /* the left-adjusted string */
846 ren_until(&wb2, delim, NULL, next, back);
847 wb_cpy(&wb, &wb2, 0);
848 /* the centered string */
849 ren_until(&wb2, delim, NULL, next, back);
850 wb_cpy(&wb, &wb2, (n_lt - wb_wid(&wb2)) / 2);
851 /* the right-adjusted string */
852 ren_until(&wb2, delim, NULL, next, back);
853 wb_cpy(&wb, &wb2, n_lt - wb_wid(&wb2));
854 /* flushing the line */
855 ren_line(wb_buf(&wb), wb_wid(&wb), AD_L, 0,
856 0, n_lt, wb.els_neg, wb.els_pos);
857 wb_done(&wb2);
858 wb_done(&wb);
861 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int))
863 struct wb wbs[NFIELDS];
864 int i, n = 0;
865 int wid = 0;
866 int left, right, cur_left;
867 int pad, rem;
868 while (n < LEN(wbs)) {
869 wb_init(&wbs[n]);
870 if (ren_until(&wbs[n++], c_fb, c_fa, next, back) != 1)
871 break;
873 left = wb == cwb ? f_hpos() : wb_wid(wb);
874 right = tab_next(left);
875 for (i = 0; i < n; i++)
876 wid += wb_wid(&wbs[i]);
877 pad = (right - left - wid) / (n > 1 ? n - 1 : 1);
878 rem = (right - left - wid) % (n > 1 ? n - 1 : 1);
879 for (i = 0; i < n; i++) {
880 if (i == 0)
881 cur_left = left;
882 else if (i == n - 1)
883 cur_left = right - wb_wid(&wbs[i]);
884 else
885 cur_left = wb_wid(wb) + pad + (i + rem >= n);
886 wb_cpy(wb, &wbs[i], cur_left);
887 wb_done(&wbs[i]);
891 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int))
893 struct wb t;
894 int pos = wb == cwb ? f_hpos() : wb_wid(wb);
895 int ins = tab_next(pos); /* insertion position */
896 int typ = tab_type(pos); /* tab type */
897 int c;
898 wb_init(&t);
899 if (typ == 'R' || typ == 'C') {
900 c = next();
901 while (c >= 0 && c != '\n' && c != '\t' && c != '\x01') {
902 back(c);
903 ren_char(&t, next, back);
904 c = next();
906 back(c);
908 if (typ == 'C')
909 ins -= wb_wid(&t) / 2;
910 if (typ == 'R')
911 ins -= wb_wid(&t);
912 if (!tc[0] || ins <= pos)
913 wb_hmov(wb, ins - pos);
914 else
915 ren_hline(wb, ins - pos, tc);
916 wb_cat(wb, &t);
917 wb_done(&t);
920 /* parse characters and troff requests of s and append them to wb */
921 int ren_parse(struct wb *wb, char *s)
923 int c;
924 odiv_beg();
925 sstr_push(s);
926 c = sstr_next();
927 while (c >= 0) {
928 sstr_back(c);
929 if (ren_char(wb, sstr_next, sstr_back))
930 break;
931 c = sstr_next();
933 sstr_pop();
934 odiv_end();
935 return 0;
938 /* cause nested render_rec() to exit */
939 void tr_popren(char **args)
941 ren_level = args[1] ? atoi(args[1]) : 0;
944 #define FMT_PAR() (n_u && !n_na && !n_ce && (n_j & AD_P) == AD_P)
946 /* read characters from tr.c and pass the rendered lines to out.c */
947 static int render_rec(int level)
949 int c;
950 while (ren_level >= level) {
951 while (!ren_un && !tr_nextreq())
952 if (ren_level < level)
953 break;
954 if (ren_level < level)
955 break;
956 if (ren_aborted)
957 return 1;
958 c = ren_next();
959 if (c < 0) {
960 if (bp_final >= 2)
961 break;
962 if (bp_final == 0) {
963 bp_final = 1;
964 ren_fmtpopall(cfmt);
965 if (trap_em >= 0)
966 trap_exec(trap_em);
967 } else {
968 bp_final = 2;
969 ren_ejectpage(1);
972 if (c >= 0)
973 ren_partial = c != '\n';
974 /* add cwb (the current word) to cfmt */
975 if (c == ' ' || c == '\n') {
976 if (!wb_part(cwb)) { /* not after a \c */
977 ren_fmtword(cwb);
978 if (c == '\n')
979 while (fmt_newline(cfmt))
980 ren_fmtpop(cfmt);
981 if (!FMT_PAR())
982 ren_fmtpopall(cfmt);
983 if (c == ' ')
984 fmt_space(cfmt);
987 /* flush the line if necessary */
988 if ((!FMT_PAR() && (c == ' ' || c == '\n')) || c < 0)
989 ren_fmtpop(cfmt);
990 if (c == '\n' || ren_nl) /* end or start of input line */
991 n_lb = f_hpos();
992 if (c == '\n' && n_it && --n_itn == 0)
993 trap_exec(n_it);
994 if (c == '\n' && !wb_part(cwb))
995 n_ce = MAX(0, n_ce - 1);
996 if (c != ' ' && c >= 0) {
997 ren_back(c);
998 ren_char(cwb, ren_next, ren_back);
1000 if (c >= 0)
1001 ren_nl = c == '\n';
1003 return 0;
1006 /* render input words */
1007 int render(void)
1009 n_nl = -1;
1010 while (!tr_nextreq())
1012 ren_first(); /* transition to the first page */
1013 render_rec(0);
1014 bp_final = 3;
1015 if (fmt_morewords(cfmt))
1016 ren_page(1);
1017 ren_br();
1018 return 0;
1021 /* trap handling */
1023 #define tposval(i) (tpos[i] < 0 ? n_p + tpos[i] : tpos[i])
1025 static int tpos[NTRAPS]; /* trap positions */
1026 static int treg[NTRAPS]; /* trap registers */
1027 static int ntraps;
1029 static int trap_first(int pos)
1031 int best = -1;
1032 int i;
1033 for (i = 0; i < ntraps; i++)
1034 if (treg[i] >= 0 && tposval(i) > pos)
1035 if (best < 0 || tposval(i) < tposval(best))
1036 best = i;
1037 return best;
1040 static int trap_byreg(int reg)
1042 int i;
1043 for (i = 0; i < ntraps; i++)
1044 if (treg[i] == reg)
1045 return i;
1046 return -1;
1049 static int trap_bypos(int reg, int pos)
1051 int i;
1052 for (i = 0; i < ntraps; i++)
1053 if (treg[i] >= 0 && tposval(i) == pos)
1054 if (reg == -1 || treg[i] == reg)
1055 return i;
1056 return -1;
1059 void tr_wh(char **args)
1061 int reg, pos, id;
1062 if (!args[1])
1063 return;
1064 pos = eval(args[1], 'v');
1065 id = trap_bypos(-1, pos);
1066 if (!args[2]) {
1067 if (id >= 0)
1068 treg[id] = -1;
1069 return;
1071 reg = map(args[2]);
1072 if (id < 0) /* find an unused position in treg[] */
1073 id = trap_byreg(-1);
1074 if (id < 0)
1075 id = ntraps++;
1076 tpos[id] = pos;
1077 treg[id] = reg;
1080 void tr_ch(char **args)
1082 int reg;
1083 int id;
1084 if (!args[1])
1085 return;
1086 reg = map(args[1]);
1087 id = trap_byreg(reg);
1088 if (id >= 0) {
1089 if (args[2])
1090 tpos[id] = eval(args[2], 'v');
1091 else
1092 treg[id] = -1;
1096 void tr_dt(char **args)
1098 if (!cdiv)
1099 return;
1100 if (args[2]) {
1101 cdiv->tpos = eval(args[1], 'v');
1102 cdiv->treg = map(args[2]);
1103 } else {
1104 cdiv->treg = -1;
1108 void tr_em(char **args)
1110 trap_em = args[1] ? map(args[1]) : -1;
1113 static int trap_pos(int pos)
1115 int ret = trap_first(pos);
1116 if (bp_final >= 3)
1117 return -1;
1118 if (cdiv)
1119 return cdiv->treg && cdiv->tpos > pos ? cdiv->tpos : -1;
1120 return ret >= 0 ? tposval(ret) : -1;
1123 static int trap_reg(int pos)
1125 int ret = trap_first(pos);
1126 if (cdiv)
1127 return cdiv->treg && cdiv->tpos > pos ? cdiv->treg : -1;
1128 return ret >= 0 ? treg[ret] : -1;
1131 int f_nexttrap(void)
1133 int pos = trap_pos(n_d);
1134 if (cdiv)
1135 return pos >= 0 ? pos : 0x7fffffff;
1136 return (pos >= 0 && pos < n_p ? pos : n_p) - n_d;