ren: add .nm and .nn
[neatroff.git] / ren.c
blobf06ba1c0a742aae16f2754f20b1db197f096ea71
1 #include <ctype.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include "roff.h"
7 #define cadj env_adj() /* line buffer */
8 #define RENWB(wb) ((wb) == &ren_wb) /* is ren_wb */
10 /* diversions */
11 struct div {
12 struct sbuf sbuf; /* diversion output */
13 int reg; /* diversion register */
14 int tpos; /* diversion trap position */
15 int treg; /* diversion trap register */
16 int dl; /* diversion width */
17 int prev_d; /* previous \n(.d value */
18 int prev_h; /* previous \n(.h value */
19 int prev_mk; /* previous .mk internal register */
20 int prev_ns; /* previous .ns value */
22 static struct div divs[NPREV]; /* diversion stack */
23 static struct div *cdiv; /* current diversion */
24 static int ren_div; /* rendering a diversion */
25 static int trap_em = -1; /* end macro */
27 static struct wb ren_wb; /* the main ren.c word buffer */
28 static int ren_nl; /* just after newline */
29 static int ren_cnl; /* current char is a newline */
30 static int ren_unbuf[8]; /* ren_back() buffer */
31 static int ren_un;
32 static int ren_fillreq; /* \p request */
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 */
41 static int c_fa; /* field delimiter */
42 static char c_fb[GNLEN]; /* field padding */
44 static int ren_next(void)
46 return ren_un > 0 ? ren_unbuf[--ren_un] : tr_next();
49 static void ren_back(int c)
51 ren_unbuf[ren_un++] = c;
54 void tr_di(char **args)
56 if (args[1]) {
57 cdiv = cdiv ? cdiv + 1 : divs;
58 memset(cdiv, 0, sizeof(*cdiv));
59 sbuf_init(&cdiv->sbuf);
60 cdiv->reg = map(args[1]);
61 cdiv->treg = -1;
62 if (args[0][2] == 'a' && str_get(cdiv->reg)) /* .da */
63 sbuf_append(&cdiv->sbuf, str_get(cdiv->reg));
64 sbuf_printf(&cdiv->sbuf, "%c%s\n", c_cc, TR_DIVBEG);
65 cdiv->prev_d = n_d;
66 cdiv->prev_h = n_h;
67 cdiv->prev_mk = n_mk;
68 cdiv->prev_ns = n_ns;
69 n_d = 0;
70 n_h = 0;
71 n_mk = 0;
72 n_ns = 0;
73 } else if (cdiv) {
74 sbuf_putnl(&cdiv->sbuf);
75 sbuf_printf(&cdiv->sbuf, "%c%s\n", c_cc, TR_DIVEND);
76 str_set(cdiv->reg, sbuf_buf(&cdiv->sbuf));
77 sbuf_done(&cdiv->sbuf);
78 n_dl = cdiv->dl;
79 n_dn = n_d;
80 n_d = cdiv->prev_d;
81 n_h = cdiv->prev_h;
82 n_mk = cdiv->prev_mk;
83 n_ns = cdiv->prev_ns;
84 cdiv = cdiv > divs ? cdiv - 1 : NULL;
88 int charwid_base(int fn, int sz, int wid)
90 /* the original troff rounds the widths up */
91 return (wid * sz + dev_uwid / 2) / dev_uwid;
94 int charwid(int fn, int sz, int wid)
96 if (dev_getcs(fn))
97 return dev_getcs(n_f) * SC_EM / 36;
98 return charwid_base(fn, sz, wid) +
99 (dev_getbd(fn) ? dev_getbd(fn) - 1 : 0);
102 int spacewid(int fn, int sz)
104 return charwid(fn, sz, (dev_font(fn)->spacewid * n_ss + 6) / 12);
107 int f_divreg(void)
109 return cdiv ? cdiv->reg : -1;
112 int f_hpos(void)
114 return adj_wid(cadj) + wb_wid(&ren_wb);
117 void tr_divbeg(char **args)
119 odiv_beg();
120 ren_div++;
123 void tr_divend(char **args)
125 odiv_end();
126 ren_div--;
129 static int trap_reg(int pos);
130 static int trap_pos(int pos);
131 static void trap_exec(int reg);
133 static void ren_page(int pg, int force)
135 if (!force && bp_final >= 2)
136 return;
137 n_nl = 0;
138 n_d = 0;
139 n_h = 0;
140 n_pg = pg;
141 bp_next = n_pg + 1;
142 bp_count++;
143 out("p%d\n", pg);
144 out("V%d\n", 0);
145 if (trap_pos(-1) == 0)
146 trap_exec(trap_reg(-1));
149 static void ren_first(void)
151 if (bp_first && !cdiv) {
152 bp_first = 0;
153 ren_page(bp_next, 1);
157 /* when nodiv, do not append .sp to diversions */
158 static void ren_sp(int n, int nodiv)
160 ren_first();
161 /* ignore .sp without arguments when reading diversions */
162 if (!n && ren_div && !n_u)
163 return;
164 n_d += n ? n : n_v;
165 if (n_d > n_h)
166 n_h = n_d;
167 if (cdiv && !nodiv) {
168 sbuf_putnl(&cdiv->sbuf);
169 sbuf_printf(&cdiv->sbuf, "%csp %du\n", c_cc, n ? n : n_v);
170 } else {
171 n_nl = n_d;
175 static void trap_exec(int reg)
177 if (str_get(reg))
178 in_pushnl(str_get(reg), NULL);
181 static int detect_traps(int beg, int end)
183 int pos = trap_pos(beg);
184 return pos >= 0 && (cdiv || pos < n_p) && pos <= end;
187 /* return 1 if executed a trap */
188 static int ren_traps(int beg, int end, int dosp)
190 int pos = trap_pos(beg);
191 if (detect_traps(beg, end)) {
192 if (dosp && pos > beg)
193 ren_sp(pos - beg, 0);
194 trap_exec(trap_reg(beg));
195 return 1;
197 return 0;
200 static int detect_pagelimit(int ne)
202 return !cdiv && n_nl + ne >= n_p;
205 /* start a new page if needed */
206 static int ren_pagelimit(int ne)
208 if (detect_pagelimit(ne)) {
209 ren_page(bp_next, 0);
210 return 1;
212 return 0;
215 static void down(int n)
217 if (!ren_traps(n_d, n_d + (n ? n : n_v), 1)) {
218 ren_sp(n, 0);
219 ren_pagelimit(0);
223 /* line adjustment */
224 static void ren_ljust(struct sbuf *spre, int w, int ad, int ll, int li, int lt)
226 int ljust = lt >= 0 ? lt : li;
227 int llen = ll - ljust;
228 n_n = w;
229 if (ad == AD_C)
230 ljust += llen > w ? (llen - w) / 2 : 0;
231 if (ad == AD_R)
232 ljust += llen - w;
233 if (ljust)
234 sbuf_printf(spre, "%ch'%du'", c_ec, ljust);
235 if (cdiv && cdiv->dl < w)
236 cdiv->dl = w;
239 /* append the line to the current diversion or send it to out.c */
240 static void ren_line(struct sbuf *spre, struct sbuf *sbuf)
242 if (cdiv) {
243 if (!sbuf_empty(spre))
244 sbuf_append(&cdiv->sbuf, sbuf_buf(spre));
245 sbuf_append(&cdiv->sbuf, sbuf_buf(sbuf));
246 } else {
247 out("H%d\n", n_o);
248 out("V%d\n", n_d);
249 if (!sbuf_empty(spre))
250 out_line(sbuf_buf(spre));
251 out_line(sbuf_buf(sbuf));
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 charwid(n_f, n_s, g ? g->wid : SC_DW);
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, sbuf_buf(&wb.sbuf));
288 wb_done(&wb);
289 if (n_nn > 0)
290 n_nn--;
291 else
292 n_ln++;
295 /* return 1 if triggered a trap */
296 static int ren_bradj(struct adj *adj, int fill, int ad, int body)
298 char cmd[16];
299 struct sbuf sbuf, spre;
300 int ll, li, lt, els_neg, els_pos;
301 int w, prev_d, lspc;
302 ren_first();
303 if (!adj_empty(adj, fill)) {
304 sbuf_init(&sbuf);
305 sbuf_init(&spre);
306 w = adj_fill(adj, ad == AD_B, fill, n_hy, &sbuf,
307 &ll, &li, &lt, &els_neg, &els_pos);
308 prev_d = n_d;
309 if (els_neg)
310 ren_sp(-els_neg, 1);
311 if (!n_ns || !sbuf_empty(&sbuf) || els_neg || els_pos) {
312 ren_sp(0, 0);
313 if (!sbuf_empty(&sbuf) && n_nm && body)
314 ren_lnum(&spre);
315 ren_ljust(&spre, w, ad, ll, li, lt);
316 ren_line(&spre, &sbuf);
317 n_ns = 0;
319 sbuf_done(&spre);
320 sbuf_done(&sbuf);
321 if (els_pos)
322 ren_sp(els_pos, 1);
323 n_a = els_pos;
324 lspc = MAX(1, n_L) * n_v - n_v;
325 if (detect_traps(prev_d, n_d) || detect_pagelimit(lspc)) {
326 sprintf(cmd, "%c&", c_ec);
327 if (!ren_cnl) /* prevent unwanted newlines */
328 in_push(cmd, NULL);
329 if (!ren_traps(prev_d, n_d, 0))
330 ren_pagelimit(lspc);
331 return 1;
333 if (lspc)
334 down(lspc);
336 return 0;
339 /* return 1 if triggered a trap */
340 static int ren_br(int force)
342 int ad = n_j;
343 if (!n_u || n_na || (n_j == AD_B && force))
344 ad = AD_L;
345 if (n_ce)
346 ad = AD_C;
347 return ren_bradj(cadj, !force && !n_ce && n_u, ad, 1);
350 void tr_br(char **args)
352 if (args[0][0] == c_cc)
353 ren_br(1);
356 void tr_sp(char **args)
358 int traps = 0;
359 int n = args[1] ? eval(args[1], 'v') : n_v;
360 if (args[0][0] == c_cc)
361 traps = ren_br(1);
362 if (n && !n_ns && !traps)
363 down(n);
366 void tr_sv(char **args)
368 int n = eval(args[1], 'v');
369 n_sv = 0;
370 if (n_d + n < f_nexttrap())
371 down(n);
372 else
373 n_sv = n;
376 void tr_ns(char **args)
378 n_ns = 1;
381 void tr_rs(char **args)
383 n_ns = 0;
386 void tr_os(char **args)
388 if (n_sv)
389 down(n_sv);
390 n_sv = 0;
393 void tr_mk(char **args)
395 if (args[1])
396 num_set(map(args[1]), n_d);
397 else
398 n_mk = n_d;
401 void tr_rt(char **args)
403 int n = args[1] ? eval_re(args[1], n_d, 'v') : n_mk;
404 if (n >= 0 && n < n_d)
405 ren_sp(n - n_d, 0);
408 void tr_ne(char **args)
410 int n = args[1] ? eval(args[1], 'v') : n_v;
411 if (!ren_traps(n_d, n_d + n - 1, 1))
412 ren_pagelimit(n);
415 static void push_eject(void)
417 char buf[32];
418 bp_ejected = bp_count;
419 sprintf(buf, "%c%s %d\n", c_cc, TR_EJECT, bp_ejected);
420 in_pushnl(buf, NULL);
423 static void push_br(void)
425 char br[8] = {c_cc, 'b', 'r', '\n'};
426 in_pushnl(br, NULL);
429 static void ren_eject(int id)
431 if (id == bp_ejected && id == bp_count && !cdiv) {
432 if (detect_traps(n_d, n_p)) {
433 push_eject();
434 ren_traps(n_d, n_p, 1);
435 } else {
436 bp_ejected = 0;
437 ren_page(bp_next, 0);
442 void tr_eject(char **args)
444 ren_eject(atoi(args[1]));
447 void tr_bp(char **args)
449 if (!cdiv && (args[1] || !n_ns)) {
450 if (bp_ejected != bp_count)
451 push_eject();
452 if (args[0][0] == c_cc)
453 push_br();
454 if (args[1])
455 bp_next = eval_re(args[1], n_pg, 0);
459 void tr_pn(char **args)
461 if (args[1])
462 bp_next = eval_re(args[1], n_pg, 0);
465 static void ren_ps(char *s)
467 int ps = !s || !*s || !strcmp("0", s) ? n_s0 : eval_re(s, n_s, 0);
468 n_s0 = n_s;
469 n_s = MAX(1, ps);
472 void tr_ps(char **args)
474 ren_ps(args[1]);
477 void tr_ll(char **args)
479 int ll = args[1] ? eval_re(args[1], n_l, 'm') : n_l0;
480 n_l0 = n_l;
481 n_l = MAX(0, ll);
482 adj_ll(cadj, n_l);
485 void tr_in(char **args)
487 int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0;
488 if (args[0][0] == c_cc)
489 ren_br(1);
490 n_i0 = n_i;
491 n_i = MAX(0, in);
492 adj_in(cadj, n_i);
493 adj_ti(cadj, -1);
496 void tr_ti(char **args)
498 if (args[0][0] == c_cc)
499 ren_br(1);
500 if (args[1])
501 adj_ti(cadj, eval_re(args[1], n_i, 'm'));
504 static void ren_ft(char *s)
506 int fn = !s || !*s || !strcmp("P", s) ? n_f0 : dev_pos(s);
507 if (fn >= 0) {
508 n_f0 = n_f;
509 n_f = fn;
513 void tr_ft(char **args)
515 ren_ft(args[1]);
518 void tr_fp(char **args)
520 if (!args[2])
521 return;
522 if (dev_mnt(atoi(args[1]), args[2], args[3] ? args[3] : args[2]) < 0)
523 errmsg("troff: failed to mount %s\n", args[2]);
526 void tr_nf(char **args)
528 if (args[0][0] == c_cc)
529 ren_br(1);
530 n_u = 0;
533 void tr_fi(char **args)
535 if (args[0][0] == c_cc)
536 ren_br(1);
537 n_u = 1;
540 void tr_ce(char **args)
542 if (args[0][0] == c_cc)
543 ren_br(1);
544 n_ce = args[1] ? atoi(args[1]) : 1;
547 void tr_fc(char **args)
549 if (args[1]) {
550 c_fa = args[1][0];
551 strcpy(c_fb, args[2] ? args[2] : " ");
552 } else {
553 c_fa = -1;
554 c_fb[0] = '\0';
558 static void ren_m(char *s)
560 int m = !s || !*s ? n_m0 : clr_get(s);
561 n_m0 = n_m;
562 n_m = m;
565 void tr_ab(char **args)
567 fprintf(stderr, "%s\n", args[1]);
568 ren_aborted = 1;
571 static void escarg_ren(char *d, int cmd, int (*next)(void), void (*back)(int))
573 char delim[GNLEN];
574 int c;
575 if (strchr(ESC_P, cmd)) {
576 c = next();
577 if (cmd == 's' && (c == '-' || c == '+')) {
578 *d++ = c;
579 c = next();
581 if (c == '(') {
582 *d++ = next();
583 *d++ = next();
584 } else if (!n_cp && c == '[') {
585 c = next();
586 while (c > 0 && c != '\n' && c != ']') {
587 *d++ = c;
588 c = next();
590 } else {
591 *d++ = c;
592 if (cmd == 's' && c >= '1' && c <= '3') {
593 c = next();
594 if (isdigit(c))
595 *d++ = c;
596 else
597 back(c);
601 if (strchr(ESC_Q, cmd)) {
602 schar_read(delim, next);
603 while (schar_jump(delim, next, back)) {
604 if ((c = next()) < 0)
605 break;
606 *d++ = c;
609 *d = '\0';
612 static int nextchar(char *s, int (*next)(void))
614 int c = next();
615 int l = utf8len(c);
616 int i;
617 if (c < 0)
618 return 0;
619 s[0] = c;
620 for (i = 1; i < l; i++)
621 s[i] = next();
622 s[l] = '\0';
623 return l;
626 static void ren_cmd(struct wb *wb, int c, char *arg)
628 switch (c) {
629 case ' ':
630 wb_hmov(wb, spacewid(n_f, n_s));
631 break;
632 case 'b':
633 ren_bracket(wb, arg);
634 break;
635 case 'c':
636 wb_setpart(wb);
637 break;
638 case 'D':
639 ren_draw(wb, arg);
640 break;
641 case 'd':
642 wb_vmov(wb, SC_EM / 2);
643 break;
644 case 'f':
645 ren_ft(arg);
646 break;
647 case 'h':
648 wb_hmov(wb, eval(arg, 'm'));
649 break;
650 case 'k':
651 num_set(map(arg), RENWB(wb) ? f_hpos() - n_lb : wb_wid(wb));
652 break;
653 case 'L':
654 ren_vline(wb, arg);
655 break;
656 case 'l':
657 ren_hline(wb, arg);
658 break;
659 case 'm':
660 ren_m(arg);
661 break;
662 case 'o':
663 ren_over(wb, arg);
664 break;
665 case 'p':
666 if (RENWB(wb))
667 ren_fillreq = 1;
668 break;
669 case 'r':
670 wb_vmov(wb, -SC_EM);
671 break;
672 case 's':
673 ren_ps(arg);
674 break;
675 case 'u':
676 wb_vmov(wb, -SC_EM / 2);
677 break;
678 case 'v':
679 wb_vmov(wb, eval(arg, 'v'));
680 break;
681 case 'X':
682 wb_etc(wb, arg);
683 break;
684 case 'x':
685 wb_els(wb, eval(arg, 'v'));
686 break;
687 case '0':
688 wb_hmov(wb, zwid());
689 break;
690 case '|':
691 wb_hmov(wb, SC_EM / 6);
692 break;
693 case '&':
694 wb_hmov(wb, 0);
695 break;
696 case '^':
697 wb_hmov(wb, SC_EM / 12);
698 break;
699 case '{':
700 case '}':
701 break;
705 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int));
707 /* read one character and place it inside wb buffer */
708 void ren_char(struct wb *wb, int (*next)(void), void (*back)(int))
710 char c[GNLEN * 4];
711 char arg[ILNLEN];
712 struct glyph *g;
713 char *s;
714 int w, n, l;
715 nextchar(c, next);
716 if (c[0] == ' ' || c[0] == '\n') {
717 wb_put(wb, c);
718 return;
720 if (c[0] == '\t' || c[0] == '\x01') {
721 n = RENWB(wb) ? f_hpos() : wb_wid(wb);
722 wb_hmov(wb, tab_next(n) - n);
723 return;
725 if (c[0] == c_fa) {
726 ren_field(wb, next, back);
727 return;
729 if (c[0] == c_ni)
730 nextchar(c + 1, next);
731 if (c[0] == c_ec) {
732 nextchar(c + 1, next);
733 if (c[1] == '(') {
734 l = nextchar(c + 2, next);
735 l += nextchar(c + 2 + l, next);
736 c[2 + l] = '\0';
737 } else if (!n_cp && c[1] == '[') {
738 l = 0;
739 n = next();
740 while (n >= 0 && n != '\n' && n != ']' && l < GNLEN - 1) {
741 c[l++] = n;
742 n = next();
744 c[l] = '\0';
745 } else if (c[1] == 'z') {
746 w = wb_wid(wb);
747 ren_char(wb, next, back);
748 wb_hmov(wb, w - wb_wid(wb));
749 return;
750 } else if (c[1] == '!') {
751 if (ren_nl && next == ren_next) {
752 s = arg;
753 n = next();
754 while (n >= 0 && n != '\n') {
755 *s++ = n;
756 n = next();
758 *s = '\0';
759 ren_transparent(arg);
761 return;
762 } else if (strchr(" bCcDdfhkLlmNoprsuvXxz0^|{}&", c[1])) {
763 escarg_ren(arg, c[1], next, back);
764 if (c[1] == 'N') {
765 g = dev_glyph_byid(arg, n_f);
766 c[1] = 'C';
767 strcpy(arg, g ? g->name : "cnull");
769 if (c[1] != 'C') {
770 ren_cmd(wb, c[1], arg);
771 return;
773 strcpy(c, arg);
776 if (!n_lg || wb_lig(wb, c)) {
777 if (n_kn)
778 wb_kern(wb, c);
779 wb_put(wb, c);
783 /* read the argument of \w and push its width */
784 int ren_wid(int (*next)(void), void (*back)(int))
786 char delim[GNLEN];
787 int c, n;
788 struct wb wb;
789 wb_init(&wb);
790 schar_read(delim, next);
791 odiv_beg();
792 c = next();
793 while (c >= 0 && c != '\n') {
794 back(c);
795 if (!schar_jump(delim, next, back))
796 break;
797 ren_char(&wb, next, back);
798 c = next();
800 odiv_end();
801 n = wb_wid(&wb);
802 wb_wconf(&wb, &n_ct, &n_st, &n_sb);
803 wb_done(&wb);
804 return n;
807 /* return 1 if the ending character (ec) was read */
808 static int ren_until(struct wb *wb, char *delim, int ec,
809 int (*next)(void), void (*back)(int))
811 int c;
812 c = next();
813 while (c >= 0 && c != '\n' && c != ec) {
814 back(c);
815 if (!schar_jump(delim, next, back))
816 break;
817 ren_char(wb, next, back);
818 c = next();
820 if (c == '\n')
821 back(c);
822 return c == ec;
825 static void wb_cpy(struct wb *dst, struct wb *src, int left)
827 wb_hmov(dst, left - wb_wid(dst));
828 wb_cat(dst, src);
831 void ren_tl(int (*next)(void), void (*back)(int))
833 struct adj *adj;
834 struct wb wb, wb2;
835 char delim[GNLEN];
836 adj = adj_alloc();
837 wb_init(&wb);
838 wb_init(&wb2);
839 schar_read(delim, next);
840 /* the left-adjusted string */
841 ren_until(&wb2, delim, '\n', next, back);
842 wb_cpy(&wb, &wb2, 0);
843 /* the centered string */
844 ren_until(&wb2, delim, '\n', next, back);
845 wb_cpy(&wb, &wb2, (n_lt - wb_wid(&wb2)) / 2);
846 /* the right-adjusted string */
847 ren_until(&wb2, delim, '\n', next, back);
848 wb_cpy(&wb, &wb2, n_lt - wb_wid(&wb2));
849 /* flushing the line */
850 adj_ll(adj, n_lt);
851 adj_wb(adj, &wb);
852 adj_nl(adj);
853 ren_bradj(adj, 0, AD_L, 0);
854 adj_free(adj);
855 wb_done(&wb2);
856 wb_done(&wb);
859 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int))
861 struct wb wbs[NFIELDS];
862 int i, n = 0;
863 int wid = 0;
864 int left, right, cur_left;
865 int pad, rem;
866 while (n < LEN(wbs)) {
867 wb_init(&wbs[n]);
868 if (ren_until(&wbs[n++], c_fb, c_fa, next, back))
869 break;
871 left = RENWB(wb) ? f_hpos() : wb_wid(wb);
872 right = tab_next(left);
873 for (i = 0; i < n; i++)
874 wid += wb_wid(&wbs[i]);
875 pad = (right - left - wid) / (n > 1 ? n - 1 : 1);
876 rem = (right - left - wid) % (n > 1 ? n - 1 : 1);
877 for (i = 0; i < n; i++) {
878 if (i == 0)
879 cur_left = left;
880 else if (i == n - 1)
881 cur_left = right - wb_wid(&wbs[i]);
882 else
883 cur_left = wb_wid(wb) + pad + (i + rem >= n);
884 wb_cpy(wb, &wbs[i], cur_left);
885 wb_done(&wbs[i]);
889 /* read characters from in.c and pass rendered lines to out.c */
890 int render(void)
892 struct wb *wb = &ren_wb;
893 int fillreq;
894 int c;
895 n_nl = -1;
896 wb_init(wb);
897 tr_first();
898 ren_first(); /* transition to the first page */
899 c = ren_next();
900 while (1) {
901 if (ren_aborted)
902 return 1;
903 if (c < 0) {
904 if (bp_final >= 2)
905 break;
906 if (bp_final == 0 && trap_em >= 0) {
907 trap_exec(trap_em);
908 bp_final = 1;
909 } else {
910 bp_final = 2;
911 push_eject();
912 push_br();
914 c = ren_next();
915 continue;
917 ren_cnl = c == '\n';
918 fillreq = 0;
919 /* add wb (the current word) to cadj */
920 if (c == ' ' || c == '\n') {
921 adj_swid(cadj, spacewid(n_f, n_s));
922 if (!wb_part(wb)) { /* not after a \c */
923 adj_wb(cadj, wb);
924 fillreq = ren_fillreq;
925 ren_fillreq = 0;
926 if (c == '\n')
927 adj_nl(cadj);
928 else
929 adj_sp(cadj);
932 while ((fillreq && !n_ce && n_u) || adj_full(cadj, !n_ce && n_u)) {
933 ren_br(0);
934 fillreq = 0;
936 if (c == '\n' || ren_nl) /* end or start of input line */
937 n_lb = f_hpos();
938 if (c == '\n' && !wb_part(wb))
939 n_ce = MAX(0, n_ce - 1);
940 if (c != ' ') {
941 ren_back(c);
942 ren_char(wb, ren_next, ren_back);
943 if (c != '\n' && wb_empty(wb))
944 adj_nonl(cadj);
946 ren_nl = c == '\n';
947 c = ren_next();
949 bp_final = 3;
950 if (!adj_empty(cadj, 0))
951 ren_page(bp_next, 1);
952 ren_br(1);
953 wb_done(wb);
954 return 0;
957 /* trap handling */
959 #define tposval(i) (tpos[i] < 0 ? n_p + tpos[i] : tpos[i])
961 static int tpos[NTRAPS]; /* trap positions */
962 static int treg[NTRAPS]; /* trap registers */
963 static int ntraps;
965 static int trap_first(int pos)
967 int best = -1;
968 int i;
969 for (i = 0; i < ntraps; i++)
970 if (treg[i] >= 0 && tposval(i) > pos)
971 if (best < 0 || tposval(i) < tposval(best))
972 best = i;
973 return best;
976 static int trap_byreg(int reg)
978 int i;
979 for (i = 0; i < ntraps; i++)
980 if (treg[i] == reg)
981 return i;
982 return -1;
985 static int trap_bypos(int reg, int pos)
987 int i;
988 for (i = 0; i < ntraps; i++)
989 if (treg[i] >= 0 && tposval(i) == pos)
990 if (reg == -1 || treg[i] == reg)
991 return i;
992 return -1;
995 void tr_wh(char **args)
997 int reg, pos, id;
998 if (!args[1])
999 return;
1000 pos = eval(args[1], 'v');
1001 id = trap_bypos(-1, pos);
1002 if (!args[2]) {
1003 if (id >= 0)
1004 treg[id] = -1;
1005 return;
1007 reg = map(args[2]);
1008 if (id < 0)
1009 id = trap_byreg(-1);
1010 if (id < 0)
1011 id = ntraps++;
1012 tpos[id] = pos;
1013 treg[id] = reg;
1016 void tr_ch(char **args)
1018 int reg;
1019 int id;
1020 if (!args[1])
1021 return;
1022 reg = map(args[1]);
1023 id = trap_byreg(reg);
1024 if (id >= 0)
1025 tpos[id] = args[2] ? eval(args[2], 'v') : -1;
1028 void tr_dt(char **args)
1030 if (!cdiv)
1031 return;
1032 if (args[2]) {
1033 cdiv->tpos = eval(args[1], 'v');
1034 cdiv->treg = map(args[2]);
1035 } else {
1036 cdiv->treg = -1;
1040 void tr_em(char **args)
1042 trap_em = args[1] ? map(args[1]) : -1;
1045 static int trap_pos(int pos)
1047 int ret = trap_first(pos);
1048 if (bp_final >= 3)
1049 return -1;
1050 if (cdiv)
1051 return cdiv->treg && cdiv->tpos > pos ? cdiv->tpos : -1;
1052 return ret >= 0 ? tposval(ret) : -1;
1055 static int trap_reg(int pos)
1057 int ret = trap_first(pos);
1058 if (cdiv)
1059 return cdiv->treg && cdiv->tpos > pos ? cdiv->treg : -1;
1060 return ret >= 0 ? treg[ret] : -1;
1063 int f_nexttrap(void)
1065 int pos = trap_pos(n_d);
1066 if (cdiv)
1067 return pos >= 0 ? pos : 0x7fffffff;
1068 return (pos >= 0 && pos < n_p ? pos : n_p) - n_d;