dir: support text direction with .>>, .<<, \> and \<
[neatroff.git] / ren.c
blob03f3e88454e25076a0407f85e985c9f3bef0b09c
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 ren_divvs; /* the amount of .v in diversions */
28 static int trap_em = -1; /* end macro */
30 static int ren_nl; /* just after a newline */
31 static int ren_partial; /* reading an input line in render_rec() */
32 static int ren_unbuf[8]; /* ren_back() buffer */
33 static int ren_un;
34 static int ren_aborted; /* .ab executed */
36 static int bp_first = 1; /* prior to the first page */
37 static int bp_next = NOPAGE; /* next page number */
38 static int bp_count; /* number of pages so far */
39 static int bp_ejected; /* current ejected page */
40 static int bp_final; /* 1: executing em, 2: the final page, 3: the 2nd final page */
41 static int ren_level; /* the depth of render_rec() calls */
43 static char c_fa[GNLEN]; /* field delimiter */
44 static char c_fb[GNLEN]; /* field padding */
46 static int ren_next(void)
48 return ren_un > 0 ? ren_unbuf[--ren_un] : tr_next();
51 static void ren_back(int c)
53 ren_unbuf[ren_un++] = c;
56 void tr_di(char **args)
58 if (args[1]) {
59 cdiv = cdiv ? cdiv + 1 : divs;
60 memset(cdiv, 0, sizeof(*cdiv));
61 sbuf_init(&cdiv->sbuf);
62 cdiv->reg = map(args[1]);
63 cdiv->treg = -1;
64 if (args[0][2] == 'a' && str_get(cdiv->reg)) /* .da */
65 sbuf_append(&cdiv->sbuf, str_get(cdiv->reg));
66 sbuf_printf(&cdiv->sbuf, "%c%s\n", c_cc, TR_DIVBEG);
67 cdiv->prev_d = n_d;
68 cdiv->prev_h = n_h;
69 cdiv->prev_mk = n_mk;
70 cdiv->prev_ns = n_ns;
71 n_d = 0;
72 n_h = 0;
73 n_mk = 0;
74 n_ns = 0;
75 } else if (cdiv) {
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 void tr_divvs(char **args)
115 ren_divvs = eval(args[1], 'u');
118 void tr_transparent(char **args)
120 if (cdiv)
121 sbuf_printf(&cdiv->sbuf, "%s\n", args[1]);
122 else
123 out("%s\n", args[1]);
126 static int trap_reg(int pos);
127 static int trap_pos(int pos);
128 static void trap_exec(int reg);
130 static void ren_page(int force)
132 if (!force && bp_final >= 2)
133 return;
134 n_nl = 0;
135 n_d = 0;
136 n_h = 0;
137 n_pg = bp_next != NOPAGE ? bp_next : n_pg + 1;
138 bp_next = NOPAGE;
139 bp_count++;
140 out("p%d\n", n_pg);
141 out("V%d\n", 0);
142 if (trap_pos(-1) == 0)
143 trap_exec(trap_reg(-1));
146 static int ren_first(void)
148 if (bp_first && !cdiv) {
149 bp_first = 0;
150 ren_page(1);
151 return 0;
153 return 1;
156 /* when nodiv, do not append .sp to diversions */
157 static void ren_sp(int n, int nodiv)
159 int linevs = !n; /* the vertical spacing before a line */
160 ren_first();
161 if (!n && ren_div && ren_divvs && !n_u)
162 n = ren_divvs; /* .v at the time of diversion */
163 ren_divvs = 0;
164 n_ns = 0;
165 n_d += n ? n : n_v;
166 if (n_d > n_h)
167 n_h = n_d;
168 if (cdiv && !nodiv) {
169 if (linevs)
170 sbuf_printf(&cdiv->sbuf, "%c%s %du\n", c_cc, TR_DIVVS, n_v);
171 else
172 sbuf_printf(&cdiv->sbuf, "%csp %du\n", c_cc, n ? n : n_v);
174 if (!cdiv)
175 n_nl = n_d;
178 static int render_rec(int level);
180 static void trap_exec(int reg)
182 char cmd[16];
183 int partial = ren_partial && (!ren_un || ren_unbuf[0] != '\n');
184 if (str_get(reg)) {
185 sprintf(cmd, "%c%s %d\n", c_cc, TR_POPREN, ren_level);
186 in_push(cmd, NULL);
187 in_push(str_get(reg), NULL);
188 if (partial)
189 in_push("\n", NULL);
190 render_rec(++ren_level);
191 /* executed the trap while in the middle of an input line */
192 if (partial)
193 fmt_suppressnl(cfmt);
197 static int detect_traps(int beg, int end)
199 int pos = trap_pos(beg);
200 return pos >= 0 && (cdiv || pos < n_p) && pos <= end;
203 /* return 1 if executed a trap */
204 static int ren_traps(int beg, int end, int dosp)
206 int pos = trap_pos(beg);
207 if (detect_traps(beg, end)) {
208 if (dosp && pos > beg)
209 ren_sp(pos - beg, 0);
210 trap_exec(trap_reg(beg));
211 return 1;
213 return 0;
216 static int detect_pagelimit(int ne)
218 return !cdiv && n_nl + ne >= n_p;
221 /* start a new page if needed */
222 static int ren_pagelimit(int ne)
224 if (detect_pagelimit(ne)) {
225 ren_page(0);
226 return 1;
228 return 0;
231 /* return 1 if triggered a trap */
232 static int down(int n)
234 if (ren_traps(n_d, n_d + (n ? n : n_v), 1))
235 return 1;
236 ren_sp(MAX(n, -n_d), 0);
237 return ren_pagelimit(0);
240 /* line adjustment */
241 static int ren_ljust(struct sbuf *spre, int w, int ad, int li, int lI, int ll)
243 int ljust = li;
244 int llen = ll - lI - li;
245 n_n = w;
246 if ((ad & AD_B) == AD_C)
247 ljust += llen > w ? (llen - w) / 2 : 0;
248 if ((ad & AD_B) == AD_R)
249 ljust += llen - w;
250 if (ljust)
251 sbuf_printf(spre, "%ch'%du'", c_ec, ljust);
252 if (cdiv && cdiv->dl < w + ljust)
253 cdiv->dl = w + ljust;
254 return ljust;
257 /* append the line to the current diversion or send it to out.c */
258 static void ren_out(char *beg, char *mid, char *end)
260 if (cdiv) {
261 sbuf_append(&cdiv->sbuf, beg);
262 sbuf_append(&cdiv->sbuf, mid);
263 sbuf_append(&cdiv->sbuf, end);
264 sbuf_append(&cdiv->sbuf, "\n");
265 } else {
266 out("H%d\n", n_o);
267 out("V%d\n", n_d);
268 out_line(beg);
269 out_line(mid);
270 out_line(end);
274 static void ren_dir(struct sbuf *sbuf)
276 struct sbuf fixed;
277 sbuf_init(&fixed);
278 dir_fix(&fixed, sbuf_buf(sbuf));
279 sbuf_done(sbuf);
280 sbuf_init(sbuf);
281 sbuf_append(sbuf, sbuf_buf(&fixed));
282 sbuf_done(&fixed);
285 static int zwid(void)
287 struct glyph *g = dev_glyph("0", n_f);
288 return g ? font_gwid(g->font, dev_font(n_f), n_s, g->wid) : 0;
291 /* append the line number to the output line */
292 static void ren_lnum(struct sbuf *spre)
294 char num[16] = "";
295 char dig[16] = "";
296 struct wb wb;
297 int i = 0;
298 wb_init(&wb);
299 if (n_nn <= 0 && (n_ln % n_nM) == 0)
300 sprintf(num, "%d", n_ln);
301 wb_hmov(&wb, n_nI * zwid());
302 if (strlen(num) < 3)
303 wb_hmov(&wb, (3 - strlen(num)) * zwid());
304 while (num[i]) {
305 dig[0] = num[i++];
306 wb_put(&wb, dig);
308 wb_hmov(&wb, n_nS * zwid());
309 sbuf_append(spre, wb_buf(&wb));
310 wb_done(&wb);
311 if (n_nn > 0)
312 n_nn--;
313 else
314 n_ln++;
317 /* append margin character */
318 static void ren_mc(struct sbuf *sbuf, int w, int ljust)
320 struct wb wb;
321 wb_init(&wb);
322 if (w + ljust < n_l + n_mcn)
323 wb_hmov(&wb, n_l + n_mcn - w - ljust);
324 wb_putexpand(&wb, c_mc);
325 sbuf_append(sbuf, wb_buf(&wb));
326 wb_done(&wb);
329 /* process a line and print it with ren_out() */
330 static int ren_line(char *line, int w, int ad, int body,
331 int li, int lI, int ll, int els_neg, int els_pos)
333 struct sbuf sbeg, send, sbuf;
334 int prev_d, lspc, ljust;
335 ren_first();
336 sbuf_init(&sbeg);
337 sbuf_init(&send);
338 sbuf_init(&sbuf);
339 sbuf_append(&sbuf, line);
340 lspc = MAX(1, n_L) * n_v; /* line space, ignoreing \x */
341 prev_d = n_d;
342 if (!n_ns || line[0] || els_neg || els_pos) {
343 if (els_neg)
344 ren_sp(-els_neg, 1);
345 ren_sp(0, 0);
346 if (line[0] && n_nm && body)
347 ren_lnum(&sbeg);
348 if (!ren_div && dir_do)
349 ren_dir(&sbuf);
350 ljust = ren_ljust(&sbeg, w, ad, li, lI, ll);
351 if (line[0] && body && n_mc)
352 ren_mc(&send, w, ljust);
353 ren_out(sbuf_buf(&sbeg), sbuf_buf(&sbuf), sbuf_buf(&send));
354 n_ns = 0;
355 if (els_pos)
356 ren_sp(els_pos, 1);
358 sbuf_done(&sbeg);
359 sbuf_done(&send);
360 sbuf_done(&sbuf);
361 n_a = els_pos;
362 if (detect_traps(prev_d, n_d) || detect_pagelimit(lspc - n_v)) {
363 if (!ren_pagelimit(lspc - n_v))
364 ren_traps(prev_d, n_d, 0);
365 return 1;
367 if (lspc - n_v && down(lspc - n_v))
368 return 1;
369 return 0;
372 /* read a line from fmt and send it to ren_line() */
373 static int ren_passline(struct fmt *fmt)
375 char *buf;
376 int ll, li, lI, els_neg, els_pos, w, ret;
377 int ad = n_j;
378 ren_first();
379 if (!fmt_morewords(fmt))
380 return 0;
381 buf = fmt_nextline(fmt, &w, &li, &lI, &ll, &els_neg, &els_pos);
382 if ((n_cp && !n_u) || n_na)
383 ad = AD_L;
384 else if ((ad & AD_B) == AD_B)
385 ad = n_td > 0 ? AD_R : AD_L;
386 if (n_ce)
387 ad = AD_C;
388 ret = ren_line(buf, w, ad, 1, li, lI, ll, els_neg, els_pos);
389 free(buf);
390 return ret;
393 /* output formatted lines in fmt */
394 static int ren_fmtpop(struct fmt *fmt)
396 int ret = 0;
397 while (fmt_morelines(fmt))
398 ret = ren_passline(fmt);
399 return ret;
402 /* format and output all lines in fmt */
403 static void ren_fmtpopall(struct fmt *fmt)
405 while (fmt_fill(fmt, 0))
406 ren_fmtpop(fmt);
407 ren_fmtpop(fmt);
410 /* pass the given word buffer to the current line buffer (cfmt) */
411 static void ren_fmtword(struct wb *wb)
413 while (fmt_word(cfmt, wb))
414 ren_fmtpop(cfmt);
415 wb_reset(wb);
418 /* output current line; returns 1 if triggered a trap */
419 static int ren_br(void)
421 ren_first();
422 ren_fmtword(cwb);
423 while (fmt_fill(cfmt, 1))
424 ren_fmtpop(cfmt);
425 return ren_fmtpop(cfmt);
428 void tr_br(char **args)
430 if (args[0][0] == c_cc)
431 ren_br();
432 else
433 ren_fmtpopall(cfmt); /* output the completed lines */
436 void tr_sp(char **args)
438 int traps = 0;
439 int n;
440 if (args[0][0] == c_cc)
441 traps = ren_br();
442 n = args[1] ? eval(args[1], 'v') : n_v;
443 if (n && (!n_ns || ren_div) && !traps)
444 down(n);
447 void tr_sv(char **args)
449 int n = eval(args[1], 'v');
450 n_sv = 0;
451 if (n_d + n < f_nexttrap())
452 down(n);
453 else
454 n_sv = n;
457 void tr_ns(char **args)
459 n_ns = 1;
462 void tr_rs(char **args)
464 n_ns = 0;
467 void tr_os(char **args)
469 if (n_sv)
470 down(n_sv);
471 n_sv = 0;
474 void tr_mk(char **args)
476 if (args[1])
477 num_set(map(args[1]), n_d);
478 else
479 n_mk = n_d;
482 void tr_rt(char **args)
484 int n = args[1] ? eval_re(args[1], n_d, 'v') : n_mk;
485 if (n >= 0 && n < n_d)
486 ren_sp(n - n_d, 0);
489 void tr_ne(char **args)
491 int n = args[1] ? eval(args[1], 'v') : n_v;
492 if (!ren_first())
493 return;
494 if (!ren_traps(n_d, n_d + n - 1, 1))
495 ren_pagelimit(n);
498 static void ren_ejectpage(int br)
500 ren_first();
501 bp_ejected = bp_count;
502 if (br)
503 ren_br();
504 while (bp_count == bp_ejected && !cdiv) {
505 if (detect_traps(n_d, n_p)) {
506 ren_traps(n_d, n_p, 1);
507 } else {
508 bp_ejected = 0;
509 ren_page(0);
514 void tr_bp(char **args)
516 if (!cdiv && (args[1] || !n_ns)) {
517 if (args[1])
518 bp_next = eval_re(args[1], n_pg, 0);
519 ren_ejectpage(args[0][0] == c_cc);
523 void tr_pn(char **args)
525 if (args[1])
526 bp_next = eval_re(args[1], n_pg, 0);
529 static void ren_ps(char *s)
531 int ps = !s || !*s || !strcmp("0", s) ?
532 n_s0 * SC_PT : eval_re(s, n_s * SC_PT, 'p');
533 n_s0 = n_s;
534 n_s = MAX(1, (ps + SC_PT / 2) / SC_PT);
537 void tr_ps(char **args)
539 ren_ps(args[1]);
542 void tr_ll(char **args)
544 int ll = args[1] ? eval_re(args[1], n_l, 'm') : n_l0;
545 n_l0 = n_l;
546 n_l = MAX(0, ll);
549 void tr_in(char **args)
551 int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0;
552 if (args[0][0] == c_cc)
553 ren_br();
554 n_i0 = n_i;
555 n_i = MAX(0, in);
556 n_ti = -1;
559 void tr_ti(char **args)
561 if (args[0][0] == c_cc)
562 ren_br();
563 if (args[1])
564 n_ti = eval_re(args[1], n_i, 'm');
567 void tr_l2r(char **args)
569 dir_do = 1;
570 if (args[0][0] == c_cc)
571 ren_br();
572 n_td = 0;
573 n_cd = 0;
576 void tr_r2l(char **args)
578 dir_do = 1;
579 if (args[0][0] == c_cc)
580 ren_br();
581 n_td = 1;
582 n_cd = 1;
585 void tr_in2(char **args)
587 int I = args[1] ? eval_re(args[1], n_I, 'm') : n_I0;
588 if (args[0][0] == c_cc)
589 ren_br();
590 n_I0 = n_I;
591 n_I = MAX(0, I);
592 n_tI = -1;
595 void tr_ti2(char **args)
597 if (args[0][0] == c_cc)
598 ren_br();
599 if (args[1])
600 n_tI = eval_re(args[1], n_I, 'm');
603 static void ren_ft(char *s)
605 int fn = !s || !*s || !strcmp("P", s) ? n_f0 : dev_pos(s);
606 if (fn < 0) {
607 errmsg("neatroff: failed to mount <%s>\n", s);
608 } else {
609 n_f0 = n_f;
610 n_f = fn;
614 void tr_ft(char **args)
616 ren_ft(args[1]);
619 void tr_fp(char **args)
621 int pos;
622 if (!args[2])
623 return;
624 pos = isdigit((unsigned char) args[1][0]) ? atoi(args[1]) : -1;
625 if (dev_mnt(pos, args[2], args[3] ? args[3] : args[2]) < 0)
626 errmsg("neatroff: failed to mount <%s>\n", args[2]);
629 void tr_nf(char **args)
631 if (args[0][0] == c_cc)
632 ren_br();
633 n_u = 0;
636 void tr_fi(char **args)
638 if (args[0][0] == c_cc)
639 ren_br();
640 n_u = 1;
643 void tr_ce(char **args)
645 if (args[0][0] == c_cc)
646 ren_br();
647 n_ce = args[1] ? atoi(args[1]) : 1;
650 void tr_fc(char **args)
652 char *fa = args[1];
653 char *fb = args[2];
654 if (fa && charread(&fa, c_fa) >= 0) {
655 if (!fb || charread(&fb, c_fb) < 0)
656 strcpy(c_fb, " ");
657 } else {
658 c_fa[0] = '\0';
659 c_fb[0] = '\0';
663 static void ren_cl(char *s)
665 int m = !s || !*s ? n_m0 : clr_get(s);
666 n_m0 = n_m;
667 n_m = m;
670 void tr_cl(char **args)
672 ren_cl(args[1]);
675 void tr_ab(char **args)
677 fprintf(stderr, "%s\n", args[1]);
678 ren_aborted = 1;
681 static void ren_cmd(struct wb *wb, int c, char *arg)
683 switch (c) {
684 case ' ':
685 wb_hmov(wb, font_swid(dev_font(n_f), n_s, n_ss));
686 break;
687 case 'b':
688 ren_bcmd(wb, arg);
689 break;
690 case 'c':
691 wb_setpart(wb);
692 break;
693 case 'D':
694 ren_dcmd(wb, arg);
695 break;
696 case 'd':
697 wb_vmov(wb, SC_EM / 2);
698 break;
699 case 'f':
700 ren_ft(arg);
701 break;
702 case 'h':
703 wb_hmov(wb, eval(arg, 'm'));
704 break;
705 case 'j':
706 wb_setcost(wb, eval(arg, 0));
707 break;
708 case 'k':
709 num_set(map(arg), wb == cwb ? f_hpos() - n_lb : wb_wid(wb));
710 break;
711 case 'L':
712 ren_vlcmd(wb, arg);
713 break;
714 case 'l':
715 ren_hlcmd(wb, arg);
716 break;
717 case 'm':
718 ren_cl(arg);
719 break;
720 case 'o':
721 ren_ocmd(wb, arg);
722 break;
723 case 'p':
724 if (wb == cwb)
725 while (fmt_fillreq(cfmt))
726 ren_fmtpop(cfmt);
727 break;
728 case 'r':
729 wb_vmov(wb, -SC_EM);
730 break;
731 case 's':
732 ren_ps(arg);
733 break;
734 case 'u':
735 wb_vmov(wb, -SC_EM / 2);
736 break;
737 case 'v':
738 wb_vmov(wb, eval(arg, 'v'));
739 break;
740 case 'X':
741 wb_etc(wb, arg);
742 break;
743 case 'x':
744 wb_els(wb, eval(arg, 'v'));
745 break;
746 case 'Z':
747 ren_zcmd(wb, arg);
748 break;
749 case '0':
750 wb_hmov(wb, zwid());
751 break;
752 case '|':
753 wb_hmov(wb, SC_EM / 6);
754 break;
755 case '&':
756 wb_hmov(wb, 0);
757 break;
758 case '^':
759 wb_hmov(wb, SC_EM / 12);
760 break;
761 case '/':
762 wb_italiccorrection(wb);
763 break;
764 case ',':
765 wb_italiccorrectionleft(wb);
766 break;
767 case '<':
768 case '>':
769 n_cd = c == '<';
770 wb_flushdir(wb);
771 break;
775 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int));
776 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int));
778 /* insert a character, escape sequence, field or etc into wb */
779 static void ren_put(struct wb *wb, char *c, int (*next)(void), void (*back)(int))
781 int w;
782 if (c[0] == ' ' || c[0] == '\n') {
783 wb_put(wb, c);
784 return;
786 if (c[0] == '\t' || c[0] == '\x01') {
787 ren_tab(wb, c[0] == '\t' ? c_tc : c_lc, next, back);
788 return;
790 if (c_fa[0] && !strcmp(c_fa, c)) {
791 ren_field(wb, next, back);
792 return;
794 if (c[0] == c_ec) {
795 if (c[1] == 'z') {
796 w = wb_wid(wb);
797 ren_char(wb, next, back);
798 wb_hmov(wb, w - wb_wid(wb));
799 return;
801 if (strchr(" bCcDdefHhjkLlmNoprSsuvXxZz0^|!{}&/,<>", c[1])) {
802 char *arg = NULL;
803 if (strchr(ESC_P, c[1]))
804 arg = unquotednext(c[1], next, back);
805 if (strchr(ESC_Q, c[1]))
806 arg = quotednext(next, back);
807 if (c[1] == 'e') {
808 snprintf(c, GNLEN, "%c%c", c_ec, c_ec);
809 } else if (c[1] == 'N') {
810 snprintf(c, GNLEN, "GID=%s", arg);
811 } else {
812 ren_cmd(wb, c[1], arg);
813 free(arg);
814 return;
816 free(arg);
819 if (ren_div) {
820 wb_putraw(wb, c);
821 return;
823 if (cdef_map(c, n_f)) /* .char characters */
824 wb_putexpand(wb, c);
825 else
826 wb_put(wb, c);
829 /* read one character and place it inside wb buffer */
830 int ren_char(struct wb *wb, int (*next)(void), void (*back)(int))
832 char c[GNLEN * 4];
833 if (charnext(c, next, back) < 0)
834 return -1;
835 ren_put(wb, c, next, back);
836 return 0;
839 /* like ren_char(); return 1 if d1 was read and 2 if d2 was read */
840 static int ren_chardel(struct wb *wb, int (*next)(void), void (*back)(int),
841 char *d1, char *d2)
843 char c[GNLEN * 4];
844 if (charnext(c, next, back) < 0)
845 return -1;
846 if (d1 && !strcmp(d1, c))
847 return 1;
848 if (d2 && !strcmp(d2, c))
849 return 2;
850 ren_put(wb, c, next, back);
851 return 0;
854 /* read the argument of \w and push its width */
855 int ren_wid(int (*next)(void), void (*back)(int))
857 char delim[GNLEN];
858 int c, n;
859 struct wb wb;
860 wb_init(&wb);
861 charnext(delim, next, back);
862 odiv_beg();
863 c = next();
864 while (c >= 0 && c != '\n') {
865 back(c);
866 if (ren_chardel(&wb, next, back, delim, NULL))
867 break;
868 c = next();
870 odiv_end();
871 n = wb_wid(&wb);
872 wb_wconf(&wb, &n_ct, &n_st, &n_sb, &n_llx, &n_lly, &n_urx, &n_ury);
873 wb_done(&wb);
874 return n;
877 /* return 1 if d1 was read and 2 if d2 was read */
878 static int ren_until(struct wb *wb, int (*next)(void), void (*back)(int),
879 char *d1, char *d2)
881 int c, ret;
882 c = next();
883 while (c >= 0 && c != '\n') {
884 back(c);
885 ret = ren_chardel(wb, next, back, d1, d2);
886 if (ret)
887 return ret;
888 c = next();
890 if (c == '\n')
891 back(c);
892 return 0;
895 /* like ren_until(); map src to dst */
896 static int ren_untilmap(struct wb *wb, int (*next)(void), void (*back)(int),
897 char *end, char *src, char *dst)
899 int ret;
900 while ((ret = ren_until(wb, next, back, src, end)) == 1) {
901 sstr_push(dst);
902 ren_until(wb, sstr_next, sstr_back, end, NULL);
903 sstr_pop();
905 return 0;
908 static void wb_cpy(struct wb *dst, struct wb *src, int left)
910 wb_hmov(dst, left - wb_wid(dst));
911 wb_cat(dst, src);
914 void ren_tl(int (*next)(void), void (*back)(int))
916 struct wb wb, wb2;
917 char *pgnum;
918 char delim[GNLEN];
919 ren_first();
920 pgnum = num_str(map("%"));
921 wb_init(&wb);
922 wb_init(&wb2);
923 charnext(delim, next, back);
924 if (!strcmp("\n", delim))
925 back('\n');
926 /* the left-adjusted string */
927 ren_untilmap(&wb2, next, back, delim, c_pc, pgnum);
928 wb_cpy(&wb, &wb2, 0);
929 /* the centered string */
930 ren_untilmap(&wb2, next, back, delim, c_pc, pgnum);
931 wb_cpy(&wb, &wb2, (n_lt - wb_wid(&wb2)) / 2);
932 /* the right-adjusted string */
933 ren_untilmap(&wb2, next, back, delim, c_pc, pgnum);
934 wb_cpy(&wb, &wb2, n_lt - wb_wid(&wb2));
935 /* flushing the line */
936 ren_line(wb_buf(&wb), wb_wid(&wb), AD_L, 0,
937 0, 0, n_lt, wb.els_neg, wb.els_pos);
938 wb_done(&wb2);
939 wb_done(&wb);
942 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int))
944 struct wb wbs[NFIELDS];
945 int i, n = 0;
946 int wid = 0;
947 int left, right, cur_left;
948 int pad, rem;
949 while (n < LEN(wbs)) {
950 wb_init(&wbs[n]);
951 if (ren_until(&wbs[n++], next, back, c_fb, c_fa) != 1)
952 break;
954 left = wb == cwb ? f_hpos() : wb_wid(wb);
955 right = tab_next(left);
956 for (i = 0; i < n; i++)
957 wid += wb_wid(&wbs[i]);
958 pad = (right - left - wid) / (n > 1 ? n - 1 : 1);
959 rem = (right - left - wid) % (n > 1 ? n - 1 : 1);
960 for (i = 0; i < n; i++) {
961 if (i == 0)
962 cur_left = left;
963 else if (i == n - 1)
964 cur_left = right - wb_wid(&wbs[i]);
965 else
966 cur_left = wb_wid(wb) + pad + (i + rem >= n);
967 wb_cpy(wb, &wbs[i], cur_left);
968 wb_done(&wbs[i]);
972 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int))
974 struct wb t;
975 int pos = wb == cwb ? f_hpos() : wb_wid(wb);
976 int ins = tab_next(pos); /* insertion position */
977 int typ = tab_type(pos); /* tab type */
978 int c;
979 wb_init(&t);
980 if (typ == 'R' || typ == 'C') {
981 c = next();
982 while (c >= 0 && c != '\n' && c != '\t' && c != '\x01') {
983 back(c);
984 ren_char(&t, next, back);
985 c = next();
987 back(c);
989 if (typ == 'C')
990 ins -= wb_wid(&t) / 2;
991 if (typ == 'R')
992 ins -= wb_wid(&t);
993 if (!tc[0] || ins <= pos)
994 wb_hmov(wb, ins - pos);
995 else
996 ren_hline(wb, ins - pos, tc);
997 wb_cat(wb, &t);
998 wb_done(&t);
1001 /* parse characters and troff requests of s and append them to wb */
1002 int ren_parse(struct wb *wb, char *s)
1004 int c;
1005 odiv_beg();
1006 sstr_push(s);
1007 c = sstr_next();
1008 while (c >= 0) {
1009 sstr_back(c);
1010 if (ren_char(wb, sstr_next, sstr_back))
1011 break;
1012 c = sstr_next();
1014 sstr_pop();
1015 odiv_end();
1016 return 0;
1019 /* cause nested render_rec() to exit */
1020 void tr_popren(char **args)
1022 ren_level = args[1] ? atoi(args[1]) : 0;
1025 #define FMT_PAR() (n_u && !n_na && !n_ce && (n_j & AD_P) == AD_P)
1027 /* read characters from tr.c and pass the rendered lines to out.c */
1028 static int render_rec(int level)
1030 int c;
1031 while (ren_level >= level) {
1032 while (!ren_un && !tr_nextreq())
1033 if (ren_level < level)
1034 break;
1035 if (ren_level < level)
1036 break;
1037 if (ren_aborted)
1038 return 1;
1039 c = ren_next();
1040 if (c < 0) {
1041 if (bp_final >= 2)
1042 break;
1043 if (bp_final == 0) {
1044 bp_final = 1;
1045 if (trap_em >= 0)
1046 trap_exec(trap_em);
1047 } else {
1048 bp_final = 2;
1049 ren_ejectpage(1);
1052 if (c >= 0)
1053 ren_partial = c != '\n';
1054 /* add cwb (the current word) to cfmt */
1055 if (c == ' ' || c == '\n') {
1056 if (!wb_part(cwb)) { /* not after a \c */
1057 ren_fmtword(cwb);
1058 if (c == '\n')
1059 while (fmt_newline(cfmt))
1060 ren_fmtpop(cfmt);
1061 if (!FMT_PAR())
1062 ren_fmtpopall(cfmt);
1063 if (c == ' ')
1064 fmt_space(cfmt);
1067 /* flush the line if necessary */
1068 if (c == ' ' || c == '\n' || c < 0)
1069 ren_fmtpop(cfmt);
1070 if (c == '\n' || ren_nl) /* end or start of input line */
1071 n_lb = f_hpos();
1072 if (c == '\n' && n_it && --n_itn == 0)
1073 trap_exec(n_it);
1074 if (c == '\n' && !wb_part(cwb))
1075 n_ce = MAX(0, n_ce - 1);
1076 if (c != ' ' && c >= 0) {
1077 ren_back(c);
1078 ren_char(cwb, ren_next, ren_back);
1080 if (c >= 0)
1081 ren_nl = c == '\n';
1083 return 0;
1086 /* render input words */
1087 int render(void)
1089 n_nl = -1;
1090 while (!tr_nextreq())
1092 ren_first(); /* transition to the first page */
1093 render_rec(0);
1094 bp_final = 3;
1095 if (fmt_morewords(cfmt))
1096 ren_page(1);
1097 ren_br();
1098 return 0;
1101 /* trap handling */
1103 #define tposval(i) (tpos[i] < 0 ? n_p + tpos[i] : tpos[i])
1105 static int tpos[NTRAPS]; /* trap positions */
1106 static int treg[NTRAPS]; /* trap registers */
1107 static int ntraps;
1109 static int trap_first(int pos)
1111 int best = -1;
1112 int i;
1113 for (i = 0; i < ntraps; i++)
1114 if (treg[i] >= 0 && tposval(i) > pos)
1115 if (best < 0 || tposval(i) < tposval(best))
1116 best = i;
1117 return best;
1120 static int trap_byreg(int reg)
1122 int i;
1123 for (i = 0; i < ntraps; i++)
1124 if (treg[i] == reg)
1125 return i;
1126 return -1;
1129 static int trap_bypos(int reg, int pos)
1131 int i;
1132 for (i = 0; i < ntraps; i++)
1133 if (treg[i] >= 0 && tposval(i) == pos)
1134 if (reg == -1 || treg[i] == reg)
1135 return i;
1136 return -1;
1139 void tr_wh(char **args)
1141 int reg, pos, id;
1142 if (!args[1])
1143 return;
1144 pos = eval(args[1], 'v');
1145 id = trap_bypos(-1, pos);
1146 if (!args[2]) {
1147 if (id >= 0)
1148 treg[id] = -1;
1149 return;
1151 reg = map(args[2]);
1152 if (id < 0) /* find an unused position in treg[] */
1153 id = trap_byreg(-1);
1154 if (id < 0)
1155 id = ntraps++;
1156 tpos[id] = pos;
1157 treg[id] = reg;
1160 void tr_ch(char **args)
1162 int reg;
1163 int id;
1164 if (!args[1])
1165 return;
1166 reg = map(args[1]);
1167 id = trap_byreg(reg);
1168 if (id >= 0) {
1169 if (args[2])
1170 tpos[id] = eval(args[2], 'v');
1171 else
1172 treg[id] = -1;
1176 void tr_dt(char **args)
1178 if (!cdiv)
1179 return;
1180 if (args[2]) {
1181 cdiv->tpos = eval(args[1], 'v');
1182 cdiv->treg = map(args[2]);
1183 } else {
1184 cdiv->treg = -1;
1188 void tr_em(char **args)
1190 trap_em = args[1] ? map(args[1]) : -1;
1193 static int trap_pos(int pos)
1195 int ret = trap_first(pos);
1196 if (bp_final >= 3)
1197 return -1;
1198 if (cdiv)
1199 return cdiv->treg && cdiv->tpos > pos ? cdiv->tpos : -1;
1200 return ret >= 0 ? tposval(ret) : -1;
1203 static int trap_reg(int pos)
1205 int ret = trap_first(pos);
1206 if (cdiv)
1207 return cdiv->treg && cdiv->tpos > pos ? cdiv->treg : -1;
1208 return ret >= 0 ? treg[ret] : -1;
1211 int f_nexttrap(void)
1213 int pos = trap_pos(n_d);
1214 if (cdiv)
1215 return pos >= 0 ? pos : 0x7fffffff;
1216 return (pos >= 0 && pos < n_p ? pos : n_p) - n_d;