reg: use snprintf for string values in num_str()
[neatroff.git] / ren.c
blob8a9a20406e3cc916d484dd90741fd8e9eb4980e2
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_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_printf(&cdiv->sbuf, "%c%s\n", c_cc, TR_DIVEND);
76 str_set(cdiv->reg, sbuf_buf(&cdiv->sbuf));
77 sbuf_done(&cdiv->sbuf);
78 n_dl = cdiv->dl;
79 n_dn = n_d;
80 n_d = cdiv->prev_d;
81 n_h = cdiv->prev_h;
82 n_mk = cdiv->prev_mk;
83 n_ns = cdiv->prev_ns;
84 cdiv = cdiv > divs ? cdiv - 1 : NULL;
88 int f_divreg(void)
90 return cdiv ? cdiv->reg : -1;
93 int f_hpos(void)
95 return fmt_wid(cfmt) + wb_wid(cwb);
98 void tr_divbeg(char **args)
100 odiv_beg();
101 ren_div++;
104 void tr_divend(char **args)
106 if (ren_div <= 0)
107 errdie("neatroff: diversion stack empty\n");
108 odiv_end();
109 ren_div--;
112 void tr_divvs(char **args)
114 ren_divvs = eval(args[1], 'u');
117 void tr_transparent(char **args)
119 if (cdiv)
120 sbuf_printf(&cdiv->sbuf, "%s\n", args[1]);
121 else
122 out("%s\n", args[1]);
125 static int trap_reg(int pos);
126 static int trap_pos(int pos);
127 static void trap_exec(int reg);
129 static void ren_page(int force)
131 if (!force && bp_final >= 2)
132 return;
133 n_nl = 0;
134 n_d = 0;
135 n_h = 0;
136 n_pg = bp_next != NOPAGE ? bp_next : n_pg + 1;
137 bp_next = NOPAGE;
138 n_PG += 1;
139 out("p%d\n", n_PG);
140 out("V%d\n", 0);
141 if (trap_pos(-1) == 0)
142 trap_exec(trap_reg(-1));
145 static int ren_first(void)
147 if (bp_first && !cdiv) {
148 bp_first = 0;
149 ren_page(1);
150 return 0;
152 return 1;
155 /* when nodiv, do not append .sp to diversions */
156 static void ren_sp(int n, int nodiv)
158 int linevs = !n; /* the vertical spacing before a line */
159 ren_first();
160 if (!n && ren_div && ren_divvs && !n_u)
161 n = ren_divvs; /* .v at the time of diversion */
162 ren_divvs = 0;
163 n_ns = 0;
164 n_d += n ? n : n_v;
165 if (n_d > n_h)
166 n_h = n_d;
167 if (cdiv && !nodiv) {
168 if (linevs)
169 sbuf_printf(&cdiv->sbuf, "%c%s %du\n", c_cc, TR_DIVVS, n_v);
170 else
171 sbuf_printf(&cdiv->sbuf, "%csp %du\n", c_cc, n ? n : n_v);
173 if (!cdiv)
174 n_nl = n_d;
177 static int render_rec(int level);
179 static void trap_exec(int reg)
181 char cmd[16];
182 int partial = ren_partial && (!ren_un || ren_unbuf[0] != '\n');
183 if (str_get(reg)) {
184 sprintf(cmd, "%c%s %d\n", c_cc, TR_POPREN, ren_level);
185 in_push(cmd, NULL);
186 in_push(str_get(reg), NULL);
187 if (partial)
188 in_push("\n", NULL);
189 render_rec(++ren_level);
190 /* executed the trap while in the middle of an input line */
191 if (partial)
192 fmt_suppressnl(cfmt);
196 static int detect_traps(int beg, int end)
198 int pos = trap_pos(beg);
199 return pos >= 0 && (cdiv || pos < n_p) && pos <= end;
202 /* return 1 if executed a trap */
203 static int ren_traps(int beg, int end, int dosp)
205 int pos = trap_pos(beg);
206 if (detect_traps(beg, end)) {
207 if (dosp && pos > beg)
208 ren_sp(pos - beg, 0);
209 trap_exec(trap_reg(beg));
210 return 1;
212 return 0;
215 static int detect_pagelimit(int ne)
217 return !cdiv && n_nl + ne >= n_p;
220 /* start a new page if needed */
221 static int ren_pagelimit(int ne)
223 if (detect_pagelimit(ne)) {
224 ren_page(0);
225 return 1;
227 return 0;
230 /* return 1 if triggered a trap */
231 static int down(int n)
233 if (ren_traps(n_d, n_d + (n ? n : n_v), 1))
234 return 1;
235 ren_sp(MAX(n, -n_d), 0);
236 return ren_pagelimit(0);
239 /* line adjustment */
240 static int ren_ljust(struct sbuf *spre, int w, int ad, int li, int lI, int ll)
242 int ljust = li;
243 int llen = ll - lI - li;
244 n_n = w;
245 if ((ad & AD_B) == AD_C)
246 ljust += llen > w ? (llen - w) / 2 : 0;
247 if ((ad & AD_B) == AD_R)
248 ljust += llen - w;
249 if (ljust)
250 sbuf_printf(spre, "%ch'%du'", c_ec, ljust);
251 if (cdiv && cdiv->dl < w + ljust)
252 cdiv->dl = w + ljust;
253 return ljust;
256 /* append the line to the current diversion or send it to out.c */
257 static void ren_out(char *beg, char *mid, char *end)
259 if (cdiv) {
260 sbuf_append(&cdiv->sbuf, beg);
261 sbuf_append(&cdiv->sbuf, mid);
262 sbuf_append(&cdiv->sbuf, end);
263 sbuf_append(&cdiv->sbuf, "\n");
264 } else {
265 out("H%d\n", n_o);
266 out("V%d\n", n_d);
267 out_line(beg);
268 out_line(mid);
269 out_line(end);
273 static void ren_dir(struct sbuf *sbuf)
275 struct sbuf fixed;
276 sbuf_init(&fixed);
277 dir_fix(&fixed, sbuf_buf(sbuf));
278 sbuf_done(sbuf);
279 sbuf_init(sbuf);
280 sbuf_append(sbuf, sbuf_buf(&fixed));
281 sbuf_done(&fixed);
284 static int zwid(void)
286 struct glyph *g = dev_glyph("0", n_f);
287 return g ? font_gwid(g->font, dev_font(n_f), n_s, g->wid) : 0;
290 /* append the line number to the output line */
291 static void ren_lnum(struct sbuf *spre)
293 char num[16] = "";
294 char dig[16] = "";
295 struct wb wb;
296 int i = 0;
297 wb_init(&wb);
298 if (n_nn <= 0 && (n_ln % n_nM) == 0)
299 sprintf(num, "%d", n_ln);
300 wb_hmov(&wb, n_nI * zwid());
301 if (strlen(num) < 3)
302 wb_hmov(&wb, (3 - strlen(num)) * zwid());
303 while (num[i]) {
304 dig[0] = num[i++];
305 wb_put(&wb, dig);
307 wb_hmov(&wb, n_nS * zwid());
308 sbuf_append(spre, wb_buf(&wb));
309 wb_done(&wb);
310 if (n_nn > 0)
311 n_nn--;
312 else
313 n_ln++;
316 /* append margin character */
317 static void ren_mc(struct sbuf *sbuf, int w, int ljust)
319 struct wb wb;
320 wb_init(&wb);
321 if (w + ljust < n_l + n_mcn)
322 wb_hmov(&wb, n_l + n_mcn - w - ljust);
323 wb_putexpand(&wb, c_mc);
324 sbuf_append(sbuf, wb_buf(&wb));
325 wb_done(&wb);
328 /* process a line and print it with ren_out() */
329 static int ren_line(char *line, int w, int ad, int body,
330 int li, int lI, int ll, int els_neg, int els_pos)
332 struct sbuf sbeg, send, sbuf;
333 int prev_d, lspc, ljust;
334 ren_first();
335 sbuf_init(&sbeg);
336 sbuf_init(&send);
337 sbuf_init(&sbuf);
338 sbuf_append(&sbuf, line);
339 lspc = MAX(1, n_L) * n_v; /* line space, ignoreing \x */
340 prev_d = n_d;
341 if (!n_ns || line[0] || els_neg || els_pos) {
342 if (els_neg)
343 ren_sp(-els_neg, 1);
344 ren_sp(0, 0);
345 if (line[0] && n_nm && body)
346 ren_lnum(&sbeg);
347 if (!ren_div && dir_do)
348 ren_dir(&sbuf);
349 ljust = ren_ljust(&sbeg, w, ad, li, lI, ll);
350 if (line[0] && body && n_mc)
351 ren_mc(&send, w, ljust);
352 ren_out(sbuf_buf(&sbeg), sbuf_buf(&sbuf), sbuf_buf(&send));
353 n_ns = 0;
354 if (els_pos)
355 ren_sp(els_pos, 1);
357 sbuf_done(&sbeg);
358 sbuf_done(&send);
359 sbuf_done(&sbuf);
360 n_a = els_pos;
361 if (detect_traps(prev_d, n_d) || detect_pagelimit(lspc - n_v)) {
362 if (!ren_pagelimit(lspc - n_v))
363 ren_traps(prev_d, n_d, 0);
364 return 1;
366 if (lspc - n_v && down(lspc - n_v))
367 return 1;
368 return 0;
371 /* read a line from fmt and send it to ren_line() */
372 static int ren_passline(struct fmt *fmt)
374 char *buf;
375 int ll, li, lI, els_neg, els_pos, w, ret;
376 int ad = n_j;
377 ren_first();
378 if (!fmt_morewords(fmt))
379 return 0;
380 buf = fmt_nextline(fmt, &w, &li, &lI, &ll, &els_neg, &els_pos);
381 if ((n_cp && !n_u) || n_na)
382 ad = AD_L;
383 else if ((ad & AD_B) == AD_B)
384 ad = n_td > 0 ? AD_R : AD_L;
385 if (n_ce)
386 ad = AD_C;
387 ret = ren_line(buf, w, ad, 1, li, lI, ll, els_neg, els_pos);
388 free(buf);
389 return ret;
392 /* output formatted lines in fmt */
393 static int ren_fmtpop(struct fmt *fmt)
395 int ret = 0;
396 while (fmt_morelines(fmt))
397 ret = ren_passline(fmt);
398 return ret;
401 /* format and output all lines in fmt */
402 static void ren_fmtpopall(struct fmt *fmt)
404 while (fmt_fill(fmt, 0))
405 ren_fmtpop(fmt);
406 ren_fmtpop(fmt);
409 /* pass the given word buffer to the current line buffer (cfmt) */
410 static void ren_fmtword(struct wb *wb)
412 while (fmt_word(cfmt, wb))
413 ren_fmtpop(cfmt);
414 wb_reset(wb);
417 /* output current line; returns 1 if triggered a trap */
418 static int ren_br(void)
420 ren_first();
421 ren_fmtword(cwb);
422 while (fmt_fill(cfmt, 1))
423 ren_fmtpop(cfmt);
424 return ren_fmtpop(cfmt);
427 void tr_br(char **args)
429 if (args[0][0] == c_cc)
430 ren_br();
431 else
432 ren_fmtpopall(cfmt); /* output the completed lines */
435 void tr_sp(char **args)
437 int traps = 0;
438 int n;
439 if (args[0][0] == c_cc)
440 traps = ren_br();
441 n = args[1] ? eval(args[1], 'v') : n_v;
442 if (n && (!n_ns || ren_div) && !traps)
443 down(n);
446 void tr_sv(char **args)
448 int n = eval(args[1], 'v');
449 n_sv = 0;
450 if (n_d + n < f_nexttrap())
451 down(n);
452 else
453 n_sv = n;
456 void tr_ns(char **args)
458 n_ns = 1;
461 void tr_rs(char **args)
463 n_ns = 0;
466 void tr_os(char **args)
468 if (n_sv)
469 down(n_sv);
470 n_sv = 0;
473 void tr_mk(char **args)
475 if (args[1])
476 num_set(map(args[1]), n_d);
477 else
478 n_mk = n_d;
481 void tr_rt(char **args)
483 int n = args[1] ? eval_re(args[1], n_d, 'v') : n_mk;
484 if (n >= 0 && n < n_d)
485 ren_sp(n - n_d, 0);
488 void tr_ne(char **args)
490 int n = args[1] ? eval(args[1], 'v') : n_v;
491 if (!ren_first())
492 return;
493 if (!ren_traps(n_d, n_d + n - 1, 1))
494 ren_pagelimit(n);
497 static void ren_ejectpage(int br)
499 ren_first();
500 bp_ejected = n_PG;
501 if (br)
502 ren_br();
503 while (n_PG == bp_ejected && !cdiv) {
504 if (detect_traps(n_d, n_p)) {
505 ren_traps(n_d, n_p, 1);
506 } else {
507 bp_ejected = 0;
508 ren_page(0);
513 void tr_bp(char **args)
515 if (!cdiv && (args[1] || !n_ns)) {
516 if (args[1])
517 bp_next = eval_re(args[1], n_pg, 0);
518 ren_ejectpage(args[0][0] == c_cc);
522 void tr_pn(char **args)
524 if (args[1])
525 bp_next = eval_re(args[1], n_pg, 0);
528 static void ren_ps(char *s)
530 int ps = !s || !*s || !strcmp("0", s) ?
531 n_s0 * SC_PT : eval_re(s, n_s * SC_PT, 'p');
532 n_s0 = n_s;
533 n_s = MAX(1, (ps + SC_PT / 2) / SC_PT);
536 void tr_ps(char **args)
538 ren_ps(args[1]);
541 void tr_ll(char **args)
543 int ll = args[1] ? eval_re(args[1], n_l, 'm') : n_l0;
544 n_l0 = n_l;
545 n_l = MAX(0, ll);
548 void tr_in(char **args)
550 int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0;
551 if (args[0][0] == c_cc)
552 ren_br();
553 n_i0 = n_i;
554 n_i = MAX(0, in);
555 n_ti = -1;
558 void tr_ti(char **args)
560 if (args[0][0] == c_cc)
561 ren_br();
562 if (args[1])
563 n_ti = eval_re(args[1], n_i, 'm');
566 void tr_l2r(char **args)
568 dir_do = 1;
569 if (args[0][0] == c_cc)
570 ren_br();
571 n_td = 0;
572 n_cd = 0;
575 void tr_r2l(char **args)
577 dir_do = 1;
578 if (args[0][0] == c_cc)
579 ren_br();
580 n_td = 1;
581 n_cd = 1;
584 void tr_in2(char **args)
586 int I = args[1] ? eval_re(args[1], n_I, 'm') : n_I0;
587 if (args[0][0] == c_cc)
588 ren_br();
589 n_I0 = n_I;
590 n_I = MAX(0, I);
591 n_tI = -1;
594 void tr_ti2(char **args)
596 if (args[0][0] == c_cc)
597 ren_br();
598 if (args[1])
599 n_tI = eval_re(args[1], n_I, 'm');
602 static void ren_ft(char *s)
604 int fn = !s || !*s || !strcmp("P", s) ? n_f0 : dev_pos(s);
605 if (fn < 0) {
606 errmsg("neatroff: failed to mount <%s>\n", s);
607 } else {
608 n_f0 = n_f;
609 n_f = fn;
613 void tr_ft(char **args)
615 ren_ft(args[1]);
618 void tr_fp(char **args)
620 int pos;
621 if (!args[2])
622 return;
623 pos = isdigit((unsigned char) args[1][0]) ? atoi(args[1]) : -1;
624 if (dev_mnt(pos, args[2], args[3] ? args[3] : args[2]) < 0)
625 errmsg("neatroff: failed to mount <%s>\n", args[2]);
628 void tr_nf(char **args)
630 if (args[0][0] == c_cc)
631 ren_br();
632 n_u = 0;
635 void tr_fi(char **args)
637 if (args[0][0] == c_cc)
638 ren_br();
639 n_u = 1;
642 void tr_ce(char **args)
644 if (args[0][0] == c_cc)
645 ren_br();
646 n_ce = args[1] ? atoi(args[1]) : 1;
649 void tr_fc(char **args)
651 char *fa = args[1];
652 char *fb = args[2];
653 if (fa && charread(&fa, c_fa) >= 0) {
654 if (!fb || charread(&fb, c_fb) < 0)
655 strcpy(c_fb, " ");
656 } else {
657 c_fa[0] = '\0';
658 c_fb[0] = '\0';
662 static void ren_cl(char *s)
664 int m = !s || !*s ? n_m0 : clr_get(s);
665 n_m0 = n_m;
666 n_m = m;
669 void tr_cl(char **args)
671 ren_cl(args[1]);
674 void tr_ab(char **args)
676 fprintf(stderr, "%s\n", args[1]);
677 ren_aborted = 1;
680 static void ren_cmd(struct wb *wb, int c, char *arg)
682 switch (c) {
683 case ' ':
684 wb_hmov(wb, font_swid(dev_font(n_f), n_s, n_ss));
685 break;
686 case 'b':
687 ren_bcmd(wb, arg);
688 break;
689 case 'c':
690 wb_setpart(wb);
691 break;
692 case 'D':
693 ren_dcmd(wb, arg);
694 break;
695 case 'd':
696 wb_vmov(wb, SC_EM / 2);
697 break;
698 case 'f':
699 ren_ft(arg);
700 break;
701 case 'h':
702 wb_hmov(wb, eval(arg, 'm'));
703 break;
704 case 'j':
705 wb_setcost(wb, eval(arg, 0));
706 break;
707 case 'k':
708 num_set(map(arg), wb == cwb ? f_hpos() - n_lb : wb_wid(wb));
709 break;
710 case 'L':
711 ren_vlcmd(wb, arg);
712 break;
713 case 'l':
714 ren_hlcmd(wb, arg);
715 break;
716 case 'm':
717 ren_cl(arg);
718 break;
719 case 'o':
720 ren_ocmd(wb, arg);
721 break;
722 case 'p':
723 if (wb == cwb)
724 while (fmt_fillreq(cfmt))
725 ren_fmtpop(cfmt);
726 break;
727 case 'r':
728 wb_vmov(wb, -SC_EM);
729 break;
730 case 's':
731 ren_ps(arg);
732 break;
733 case 'u':
734 wb_vmov(wb, -SC_EM / 2);
735 break;
736 case 'v':
737 wb_vmov(wb, eval(arg, 'v'));
738 break;
739 case 'X':
740 wb_etc(wb, arg);
741 break;
742 case 'x':
743 wb_els(wb, eval(arg, 'v'));
744 break;
745 case 'Z':
746 ren_zcmd(wb, arg);
747 break;
748 case '0':
749 wb_hmov(wb, zwid());
750 break;
751 case '|':
752 wb_hmov(wb, SC_EM / 6);
753 break;
754 case '&':
755 wb_hmov(wb, 0);
756 break;
757 case '^':
758 wb_hmov(wb, SC_EM / 12);
759 break;
760 case '/':
761 wb_italiccorrection(wb);
762 break;
763 case ',':
764 wb_italiccorrectionleft(wb);
765 break;
766 case '<':
767 case '>':
768 n_cd = c == '<';
769 wb_flushdir(wb);
770 break;
774 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int));
775 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int));
777 /* insert a character, escape sequence, field or etc into wb */
778 static void ren_put(struct wb *wb, char *c, int (*next)(void), void (*back)(int))
780 int w;
781 if (c[0] == ' ' || c[0] == '\n') {
782 wb_put(wb, c);
783 return;
785 if (c[0] == '\t' || c[0] == '\x01') {
786 ren_tab(wb, c[0] == '\t' ? c_tc : c_lc, next, back);
787 return;
789 if (c_fa[0] && !strcmp(c_fa, c)) {
790 ren_field(wb, next, back);
791 return;
793 if (c[0] == c_ec) {
794 if (c[1] == 'z') {
795 w = wb_wid(wb);
796 ren_char(wb, next, back);
797 wb_hmov(wb, w - wb_wid(wb));
798 return;
800 if (strchr(" bCcDdefHhjkLlmNoprSsuvXxZz0^|!{}&/,<>", c[1])) {
801 char *arg = NULL;
802 if (strchr(ESC_P, c[1]))
803 arg = unquotednext(c[1], next, back);
804 if (strchr(ESC_Q, c[1]))
805 arg = quotednext(next, back);
806 if (c[1] == 'e') {
807 snprintf(c, GNLEN, "%c%c", c_ec, c_ec);
808 } else if (c[1] == 'N') {
809 snprintf(c, GNLEN, "GID=%s", arg);
810 } else {
811 ren_cmd(wb, c[1], arg);
812 free(arg);
813 return;
815 free(arg);
818 if (ren_div) {
819 wb_putraw(wb, c);
820 return;
822 if (cdef_map(c, n_f)) /* .char characters */
823 wb_putexpand(wb, c);
824 else
825 wb_put(wb, c);
828 /* read one character and place it inside wb buffer */
829 int ren_char(struct wb *wb, int (*next)(void), void (*back)(int))
831 char c[GNLEN * 4];
832 if (charnext(c, next, back) < 0)
833 return -1;
834 ren_put(wb, c, next, back);
835 return 0;
838 /* like ren_char(); return 1 if d1 was read and 2 if d2 was read */
839 static int ren_chardel(struct wb *wb, int (*next)(void), void (*back)(int),
840 char *d1, char *d2)
842 char c[GNLEN * 4];
843 if (charnext(c, next, back) < 0)
844 return -1;
845 if (d1 && !strcmp(d1, c))
846 return 1;
847 if (d2 && !strcmp(d2, c))
848 return 2;
849 ren_put(wb, c, next, back);
850 return 0;
853 /* read the argument of \w and push its width */
854 int ren_wid(int (*next)(void), void (*back)(int))
856 char delim[GNLEN];
857 int c, n;
858 struct wb wb;
859 wb_init(&wb);
860 charnext(delim, next, back);
861 odiv_beg();
862 c = next();
863 while (c >= 0 && c != '\n') {
864 back(c);
865 if (ren_chardel(&wb, next, back, delim, NULL))
866 break;
867 c = next();
869 odiv_end();
870 n = wb_wid(&wb);
871 wb_wconf(&wb, &n_ct, &n_st, &n_sb, &n_llx, &n_lly, &n_urx, &n_ury);
872 wb_done(&wb);
873 return n;
876 /* return 1 if d1 was read and 2 if d2 was read */
877 static int ren_until(struct wb *wb, int (*next)(void), void (*back)(int),
878 char *d1, char *d2)
880 int c, ret;
881 c = next();
882 while (c >= 0 && c != '\n') {
883 back(c);
884 ret = ren_chardel(wb, next, back, d1, d2);
885 if (ret)
886 return ret;
887 c = next();
889 if (c == '\n')
890 back(c);
891 return 0;
894 /* like ren_until(); map src to dst */
895 static int ren_untilmap(struct wb *wb, int (*next)(void), void (*back)(int),
896 char *end, char *src, char *dst)
898 int ret;
899 while ((ret = ren_until(wb, next, back, src, end)) == 1) {
900 sstr_push(dst);
901 ren_until(wb, sstr_next, sstr_back, end, NULL);
902 sstr_pop();
904 return 0;
907 static void wb_cpy(struct wb *dst, struct wb *src, int left)
909 wb_hmov(dst, left - wb_wid(dst));
910 wb_cat(dst, src);
913 void ren_tl(int (*next)(void), void (*back)(int))
915 struct wb wb, wb2;
916 char *pgnum;
917 char delim[GNLEN];
918 ren_first();
919 pgnum = num_str(map("%"));
920 wb_init(&wb);
921 wb_init(&wb2);
922 charnext(delim, next, back);
923 if (!strcmp("\n", delim))
924 back('\n');
925 /* the left-adjusted string */
926 ren_untilmap(&wb2, next, back, delim, c_pc, pgnum);
927 wb_cpy(&wb, &wb2, 0);
928 /* the centered string */
929 ren_untilmap(&wb2, next, back, delim, c_pc, pgnum);
930 wb_cpy(&wb, &wb2, (n_lt - wb_wid(&wb2)) / 2);
931 /* the right-adjusted string */
932 ren_untilmap(&wb2, next, back, delim, c_pc, pgnum);
933 wb_cpy(&wb, &wb2, n_lt - wb_wid(&wb2));
934 /* flushing the line */
935 ren_line(wb_buf(&wb), wb_wid(&wb), AD_L, 0,
936 0, 0, n_lt, wb.els_neg, wb.els_pos);
937 wb_done(&wb2);
938 wb_done(&wb);
941 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int))
943 struct wb wbs[NFIELDS];
944 int i, n = 0;
945 int wid = 0;
946 int left, right, cur_left;
947 int pad, rem;
948 while (n < LEN(wbs)) {
949 wb_init(&wbs[n]);
950 if (ren_until(&wbs[n++], next, back, c_fb, c_fa) != 1)
951 break;
953 left = wb == cwb ? f_hpos() : wb_wid(wb);
954 right = tab_next(left);
955 for (i = 0; i < n; i++)
956 wid += wb_wid(&wbs[i]);
957 pad = (right - left - wid) / (n > 1 ? n - 1 : 1);
958 rem = (right - left - wid) % (n > 1 ? n - 1 : 1);
959 for (i = 0; i < n; i++) {
960 if (i == 0)
961 cur_left = left;
962 else if (i == n - 1)
963 cur_left = right - wb_wid(&wbs[i]);
964 else
965 cur_left = wb_wid(wb) + pad + (i + rem >= n);
966 wb_cpy(wb, &wbs[i], cur_left);
967 wb_done(&wbs[i]);
971 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int))
973 struct wb t;
974 int pos = wb == cwb ? f_hpos() : wb_wid(wb);
975 int ins = tab_next(pos); /* insertion position */
976 int typ = tab_type(pos); /* tab type */
977 int c;
978 wb_init(&t);
979 if (typ == 'R' || typ == 'C') {
980 c = next();
981 while (c >= 0 && c != '\n' && c != '\t' && c != '\x01') {
982 back(c);
983 ren_char(&t, next, back);
984 c = next();
986 back(c);
988 if (typ == 'C')
989 ins -= wb_wid(&t) / 2;
990 if (typ == 'R')
991 ins -= wb_wid(&t);
992 if (!tc[0] || ins <= pos)
993 wb_hmov(wb, ins - pos);
994 else
995 ren_hline(wb, ins - pos, tc);
996 wb_cat(wb, &t);
997 wb_done(&t);
1000 /* parse characters and troff requests of s and append them to wb */
1001 int ren_parse(struct wb *wb, char *s)
1003 int c;
1004 odiv_beg();
1005 sstr_push(s);
1006 c = sstr_next();
1007 while (c >= 0) {
1008 sstr_back(c);
1009 if (ren_char(wb, sstr_next, sstr_back))
1010 break;
1011 c = sstr_next();
1013 sstr_pop();
1014 odiv_end();
1015 return 0;
1018 /* cause nested render_rec() to exit */
1019 void tr_popren(char **args)
1021 ren_level = args[1] ? atoi(args[1]) : 0;
1024 #define FMT_PAR() (n_u && !n_na && !n_ce && (n_j & AD_P) == AD_P)
1026 /* read characters from tr.c and pass the rendered lines to out.c */
1027 static int render_rec(int level)
1029 int ren_div_saved = ren_div;
1030 int c;
1031 ren_div = 0;
1032 while (ren_level >= level) {
1033 while (!ren_un && !tr_nextreq())
1034 if (ren_level < level)
1035 break;
1036 if (ren_level < level)
1037 break;
1038 if (ren_aborted)
1039 return 1;
1040 c = ren_next();
1041 if (c < 0) {
1042 if (bp_final >= 2)
1043 break;
1044 if (bp_final == 0) {
1045 bp_final = 1;
1046 if (trap_em >= 0)
1047 trap_exec(trap_em);
1048 } else {
1049 bp_final = 2;
1050 ren_ejectpage(1);
1053 if (c >= 0)
1054 ren_partial = c != '\n';
1055 /* add cwb (the current word) to cfmt */
1056 if (c == ' ' || c == '\n') {
1057 if (!wb_part(cwb)) { /* not after a \c */
1058 ren_fmtword(cwb);
1059 if (c == '\n')
1060 while (fmt_newline(cfmt))
1061 ren_fmtpop(cfmt);
1062 if (!FMT_PAR())
1063 ren_fmtpopall(cfmt);
1064 if (c == ' ')
1065 fmt_space(cfmt);
1068 /* flush the line if necessary */
1069 if (c == ' ' || c == '\n' || c < 0)
1070 ren_fmtpop(cfmt);
1071 if (c == '\n' || ren_nl) /* end or start of input line */
1072 n_lb = f_hpos();
1073 if (c == '\n' && n_it && --n_itn == 0)
1074 trap_exec(n_it);
1075 if (c == '\n' && !wb_part(cwb))
1076 n_ce = MAX(0, n_ce - 1);
1077 if (c != ' ' && c >= 0) {
1078 ren_back(c);
1079 ren_char(cwb, ren_next, ren_back);
1081 if (c >= 0)
1082 ren_nl = c == '\n';
1084 /* restore ren_div after processing traps */
1085 ren_div = ren_div_saved;
1086 return 0;
1089 /* render input words */
1090 int render(void)
1092 n_nl = -1;
1093 while (!tr_nextreq())
1095 ren_first(); /* transition to the first page */
1096 render_rec(0);
1097 bp_final = 3;
1098 if (fmt_morewords(cfmt))
1099 ren_page(1);
1100 ren_br();
1101 return 0;
1104 /* trap handling */
1106 #define tposval(i) (tpos[i] < 0 ? n_p + tpos[i] : tpos[i])
1108 static int tpos[NTRAPS]; /* trap positions */
1109 static int treg[NTRAPS]; /* trap registers */
1110 static int ntraps;
1112 static int trap_first(int pos)
1114 int best = -1;
1115 int i;
1116 for (i = 0; i < ntraps; i++)
1117 if (treg[i] >= 0 && tposval(i) > pos)
1118 if (best < 0 || tposval(i) < tposval(best))
1119 best = i;
1120 return best;
1123 static int trap_byreg(int reg)
1125 int i;
1126 for (i = 0; i < ntraps; i++)
1127 if (treg[i] == reg)
1128 return i;
1129 return -1;
1132 static int trap_bypos(int reg, int pos)
1134 int i;
1135 for (i = 0; i < ntraps; i++)
1136 if (treg[i] >= 0 && tposval(i) == pos)
1137 if (reg == -1 || treg[i] == reg)
1138 return i;
1139 return -1;
1142 void tr_wh(char **args)
1144 int reg, pos, id;
1145 if (!args[1])
1146 return;
1147 pos = eval(args[1], 'v');
1148 id = trap_bypos(-1, pos);
1149 if (!args[2]) {
1150 if (id >= 0)
1151 treg[id] = -1;
1152 return;
1154 reg = map(args[2]);
1155 if (id < 0) /* find an unused position in treg[] */
1156 id = trap_byreg(-1);
1157 if (id < 0)
1158 id = ntraps++;
1159 tpos[id] = pos;
1160 treg[id] = reg;
1163 void tr_ch(char **args)
1165 int reg;
1166 int id;
1167 if (!args[1])
1168 return;
1169 reg = map(args[1]);
1170 id = trap_byreg(reg);
1171 if (id >= 0) {
1172 if (args[2])
1173 tpos[id] = eval(args[2], 'v');
1174 else
1175 treg[id] = -1;
1179 void tr_dt(char **args)
1181 if (!cdiv)
1182 return;
1183 if (args[2]) {
1184 cdiv->tpos = eval(args[1], 'v');
1185 cdiv->treg = map(args[2]);
1186 } else {
1187 cdiv->treg = -1;
1191 void tr_em(char **args)
1193 trap_em = args[1] ? map(args[1]) : -1;
1196 static int trap_pos(int pos)
1198 int ret = trap_first(pos);
1199 if (bp_final >= 3)
1200 return -1;
1201 if (cdiv)
1202 return cdiv->treg && cdiv->tpos > pos ? cdiv->tpos : -1;
1203 return ret >= 0 ? tposval(ret) : -1;
1206 static int trap_reg(int pos)
1208 int ret = trap_first(pos);
1209 if (cdiv)
1210 return cdiv->treg && cdiv->tpos > pos ? cdiv->treg : -1;
1211 return ret >= 0 ? treg[ret] : -1;
1214 int f_nexttrap(void)
1216 int pos = trap_pos(n_d);
1217 if (cdiv)
1218 return pos >= 0 ? pos : 0x7fffffff;
1219 return (pos >= 0 && pos < n_p ? pos : n_p) - n_d;