hyph: drop non-alpha characters for HY_FIRST2 and HY_FINAL2
[neatroff.git] / ren.c
blob9bfe547a1a079ba36bc2a920a4930a6ad1a65ae9
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 char c_fa[GNLEN]; /* 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 /* return 1 if triggered a trap */
216 static int down(int n)
218 if (ren_traps(n_d, n_d + (n ? n : n_v), 1))
219 return 1;
220 ren_sp(n, 0);
221 return ren_pagelimit(0);
224 /* line adjustment */
225 static int ren_ljust(struct sbuf *spre, int w, int ad, int ll, int li, int lt)
227 int ljust = lt >= 0 ? lt : li;
228 int llen = ll - ljust;
229 n_n = w;
230 if (ad == AD_C)
231 ljust += llen > w ? (llen - w) / 2 : 0;
232 if (ad == AD_R)
233 ljust += llen - w;
234 if (ljust)
235 sbuf_printf(spre, "%ch'%du'", c_ec, ljust);
236 if (cdiv && cdiv->dl < w + ljust)
237 cdiv->dl = w + ljust;
238 return ljust;
241 /* append the line to the current diversion or send it to out.c */
242 static void ren_line(struct sbuf *spre, struct sbuf *sbuf)
244 if (cdiv) {
245 if (!sbuf_empty(spre))
246 sbuf_append(&cdiv->sbuf, sbuf_buf(spre));
247 sbuf_append(&cdiv->sbuf, sbuf_buf(sbuf));
248 } else {
249 out("H%d\n", n_o);
250 out("V%d\n", n_d);
251 if (!sbuf_empty(spre))
252 out_line(sbuf_buf(spre));
253 out_line(sbuf_buf(sbuf));
257 static void ren_transparent(char *s)
259 if (cdiv)
260 sbuf_printf(&cdiv->sbuf, "%s\n", s);
261 else
262 out("%s\n", s);
265 static int zwid(void)
267 struct glyph *g = dev_glyph("0", n_f);
268 return charwid(n_f, n_s, g ? g->wid : SC_DW);
271 /* append the line number to the output line */
272 static void ren_lnum(struct sbuf *spre)
274 char num[16] = "";
275 char dig[16] = "";
276 struct wb wb;
277 int i = 0;
278 wb_init(&wb);
279 if (n_nn <= 0 && (n_ln % n_nM) == 0)
280 sprintf(num, "%d", n_ln);
281 wb_hmov(&wb, n_nI * zwid());
282 if (strlen(num) < 3)
283 wb_hmov(&wb, (3 - strlen(num)) * zwid());
284 while (num[i]) {
285 dig[0] = num[i++];
286 wb_put(&wb, dig);
288 wb_hmov(&wb, n_nS * zwid());
289 sbuf_append(spre, sbuf_buf(&wb.sbuf));
290 wb_done(&wb);
291 if (n_nn > 0)
292 n_nn--;
293 else
294 n_ln++;
297 /* append margin character */
298 static void ren_mc(struct sbuf *sbuf, int w, int ljust)
300 struct wb wb;
301 wb_init(&wb);
302 if (w + ljust < n_l + n_mcn)
303 wb_hmov(&wb, n_l + n_mcn - w - ljust);
304 wb_put(&wb, c_mc);
305 sbuf_append(sbuf, sbuf_buf(&wb.sbuf));
306 wb_done(&wb);
309 /* return 1 if triggered a trap */
310 static int ren_bradj(struct adj *adj, int fill, int ad, int body)
312 char cmd[16];
313 struct sbuf sbuf, spre;
314 int ll, li, lt, els_neg, els_pos;
315 int w, hyph, prev_d, lspc, ljust;
316 ren_first();
317 if (!adj_empty(adj, fill)) {
318 sbuf_init(&sbuf);
319 sbuf_init(&spre);
320 hyph = n_hy;
321 lspc = MAX(1, n_L) * n_v; /* line space, ignoreing \x */
322 if (n_hy & HY_LAST && (detect_traps(n_d, n_d + lspc) ||
323 detect_pagelimit(lspc)))
324 hyph = 0; /* disable for last lines */
325 w = adj_fill(adj, ad == AD_B, fill, hyph, &sbuf,
326 &ll, &li, &lt, &els_neg, &els_pos);
327 prev_d = n_d;
328 if (els_neg)
329 ren_sp(-els_neg, 1);
330 if (!n_ns || !sbuf_empty(&sbuf) || els_neg || els_pos) {
331 ren_sp(0, 0);
332 if (!sbuf_empty(&sbuf) && n_nm && body)
333 ren_lnum(&spre);
334 ljust = ren_ljust(&spre, w, ad, ll, li, lt);
335 if (!sbuf_empty(&sbuf) && body && n_mc)
336 ren_mc(&sbuf, w, ljust);
337 ren_line(&spre, &sbuf);
338 n_ns = 0;
340 sbuf_done(&spre);
341 sbuf_done(&sbuf);
342 if (els_pos)
343 ren_sp(els_pos, 1);
344 n_a = els_pos;
345 if (detect_traps(prev_d, n_d) || detect_pagelimit(lspc - n_v)) {
346 sprintf(cmd, "%c&", c_ec);
347 if (!ren_cnl) /* prevent unwanted newlines */
348 in_push(cmd, NULL);
349 if (!ren_traps(prev_d, n_d, 0))
350 ren_pagelimit(lspc - n_v);
351 return 1;
353 if (lspc - n_v && down(lspc - n_v))
354 return 1;
356 return 0;
359 /* return 1 if triggered a trap */
360 static int ren_br(int force)
362 int ad = n_j;
363 if (!n_u || n_na || (n_j == AD_B && force))
364 ad = AD_L;
365 if (n_ce)
366 ad = AD_C;
367 return ren_bradj(cadj, !force && !n_ce && n_u, ad, 1);
370 void tr_br(char **args)
372 if (args[0][0] == c_cc)
373 ren_br(1);
376 void tr_sp(char **args)
378 int traps = 0;
379 int n = args[1] ? eval(args[1], 'v') : n_v;
380 if (args[0][0] == c_cc)
381 traps = ren_br(1);
382 if (n && !n_ns && !traps)
383 down(n);
386 void tr_sv(char **args)
388 int n = eval(args[1], 'v');
389 n_sv = 0;
390 if (n_d + n < f_nexttrap())
391 down(n);
392 else
393 n_sv = n;
396 void tr_ns(char **args)
398 n_ns = 1;
401 void tr_rs(char **args)
403 n_ns = 0;
406 void tr_os(char **args)
408 if (n_sv)
409 down(n_sv);
410 n_sv = 0;
413 void tr_mk(char **args)
415 if (args[1])
416 num_set(map(args[1]), n_d);
417 else
418 n_mk = n_d;
421 void tr_rt(char **args)
423 int n = args[1] ? eval_re(args[1], n_d, 'v') : n_mk;
424 if (n >= 0 && n < n_d)
425 ren_sp(n - n_d, 0);
428 void tr_ne(char **args)
430 int n = args[1] ? eval(args[1], 'v') : n_v;
431 if (!ren_traps(n_d, n_d + n - 1, 1))
432 ren_pagelimit(n);
435 static void push_eject(void)
437 char buf[32];
438 bp_ejected = bp_count;
439 sprintf(buf, "%c%s %d\n", c_cc, TR_EJECT, bp_ejected);
440 in_pushnl(buf, NULL);
443 static void push_br(void)
445 char br[8] = {c_cc, 'b', 'r', '\n'};
446 in_pushnl(br, NULL);
449 static void ren_eject(int id)
451 if (id == bp_ejected && id == bp_count && !cdiv) {
452 if (detect_traps(n_d, n_p)) {
453 push_eject();
454 ren_traps(n_d, n_p, 1);
455 } else {
456 bp_ejected = 0;
457 ren_page(bp_next, 0);
462 void tr_eject(char **args)
464 ren_eject(atoi(args[1]));
467 void tr_bp(char **args)
469 if (!cdiv && (args[1] || !n_ns)) {
470 if (bp_ejected != bp_count)
471 push_eject();
472 if (args[0][0] == c_cc)
473 push_br();
474 if (args[1])
475 bp_next = eval_re(args[1], n_pg, 0);
479 void tr_pn(char **args)
481 if (args[1])
482 bp_next = eval_re(args[1], n_pg, 0);
485 static void ren_ps(char *s)
487 int ps = !s || !*s || !strcmp("0", s) ? n_s0 : eval_re(s, n_s, 0);
488 n_s0 = n_s;
489 n_s = MAX(1, ps);
492 void tr_ps(char **args)
494 ren_ps(args[1]);
497 void tr_ll(char **args)
499 int ll = args[1] ? eval_re(args[1], n_l, 'm') : n_l0;
500 n_l0 = n_l;
501 n_l = MAX(0, ll);
502 adj_ll(cadj, n_l);
505 void tr_in(char **args)
507 int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0;
508 if (args[0][0] == c_cc)
509 ren_br(1);
510 n_i0 = n_i;
511 n_i = MAX(0, in);
512 adj_in(cadj, n_i);
513 adj_ti(cadj, -1);
516 void tr_ti(char **args)
518 if (args[0][0] == c_cc)
519 ren_br(1);
520 if (args[1])
521 adj_ti(cadj, eval_re(args[1], n_i, 'm'));
524 static void ren_ft(char *s)
526 int fn = !s || !*s || !strcmp("P", s) ? n_f0 : dev_pos(s);
527 if (fn < 0) {
528 errmsg("neatroff: failed to mount <%s>\n", s);
529 } else {
530 n_f0 = n_f;
531 n_f = fn;
535 void tr_ft(char **args)
537 ren_ft(args[1]);
540 void tr_fp(char **args)
542 if (!args[2])
543 return;
544 if (dev_mnt(atoi(args[1]), args[2], args[3] ? args[3] : args[2]) < 0)
545 errmsg("neatroff: failed to mount <%s>\n", args[2]);
548 void tr_nf(char **args)
550 if (args[0][0] == c_cc)
551 ren_br(1);
552 n_u = 0;
555 void tr_fi(char **args)
557 if (args[0][0] == c_cc)
558 ren_br(1);
559 n_u = 1;
562 void tr_ce(char **args)
564 if (args[0][0] == c_cc)
565 ren_br(1);
566 n_ce = args[1] ? atoi(args[1]) : 1;
569 void tr_fc(char **args)
571 char *fa = args[1];
572 char *fb = args[2];
573 if (fa && charread(&fa, c_fa) >= 0) {
574 if (!fb || charread(&fb, c_fb) < 0)
575 strcpy(c_fb, " ");
576 } else {
577 c_fa[0] = '\0';
578 c_fb[0] = '\0';
582 static void ren_m(char *s)
584 int m = !s || !*s ? n_m0 : clr_get(s);
585 n_m0 = n_m;
586 n_m = m;
589 void tr_ab(char **args)
591 fprintf(stderr, "%s\n", args[1]);
592 ren_aborted = 1;
595 static void ren_cmd(struct wb *wb, int c, char *arg)
597 switch (c) {
598 case ' ':
599 wb_hmov(wb, spacewid(n_f, n_s));
600 break;
601 case 'b':
602 ren_bcmd(wb, arg);
603 break;
604 case 'c':
605 wb_setpart(wb);
606 break;
607 case 'D':
608 ren_dcmd(wb, arg);
609 break;
610 case 'd':
611 wb_vmov(wb, SC_EM / 2);
612 break;
613 case 'f':
614 ren_ft(arg);
615 break;
616 case 'h':
617 wb_hmov(wb, eval(arg, 'm'));
618 break;
619 case 'k':
620 num_set(map(arg), RENWB(wb) ? f_hpos() - n_lb : wb_wid(wb));
621 break;
622 case 'L':
623 ren_vlcmd(wb, arg);
624 break;
625 case 'l':
626 ren_hlcmd(wb, arg);
627 break;
628 case 'm':
629 ren_m(arg);
630 break;
631 case 'o':
632 ren_ocmd(wb, arg);
633 break;
634 case 'p':
635 if (RENWB(wb))
636 ren_fillreq = 1;
637 break;
638 case 'r':
639 wb_vmov(wb, -SC_EM);
640 break;
641 case 's':
642 ren_ps(arg);
643 break;
644 case 'u':
645 wb_vmov(wb, -SC_EM / 2);
646 break;
647 case 'v':
648 wb_vmov(wb, eval(arg, 'v'));
649 break;
650 case 'X':
651 wb_etc(wb, arg);
652 break;
653 case 'x':
654 wb_els(wb, eval(arg, 'v'));
655 break;
656 case '0':
657 wb_hmov(wb, zwid());
658 break;
659 case '|':
660 wb_hmov(wb, SC_EM / 6);
661 break;
662 case '&':
663 wb_hmov(wb, 0);
664 break;
665 case '^':
666 wb_hmov(wb, SC_EM / 12);
667 break;
668 case '{':
669 case '}':
670 break;
674 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int));
675 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int));
677 /* insert a character, escape sequence, field or etc into wb */
678 static void ren_put(struct wb *wb, char *c, int (*next)(void), void (*back)(int))
680 char arg[ILNLEN];
681 struct glyph *g;
682 char *s;
683 int w, n;
684 if (c[0] == ' ' || c[0] == '\n') {
685 wb_put(wb, c);
686 return;
688 if (c[0] == '\t' || c[0] == '\x01') {
689 ren_tab(wb, c[0] == '\t' ? c_tc : c_lc, next, back);
690 return;
692 if (c_fa[0] && !strcmp(c_fa, c)) {
693 ren_field(wb, next, back);
694 return;
696 if (c[0] == c_ec) {
697 if (c[1] == 'z') {
698 w = wb_wid(wb);
699 ren_char(wb, next, back);
700 wb_hmov(wb, w - wb_wid(wb));
701 return;
703 if (c[1] == '!') {
704 if (ren_nl && next == ren_next) {
705 s = arg;
706 n = next();
707 while (n >= 0 && n != '\n') {
708 *s++ = n;
709 n = next();
711 *s = '\0';
712 ren_transparent(arg);
714 return;
716 if (strchr(" bCcDdfHhkLlmNoprSsuvXxz0^|{}&", c[1])) {
717 argnext(arg, c[1], next, back);
718 if (c[1] == 'S' || c[1] == 'H')
719 return; /* not implemented */
720 if (c[1] != 'N') {
721 ren_cmd(wb, c[1], arg);
722 return;
724 g = dev_glyph_byid(arg, n_f);
725 strcpy(c, g ? g->name : "cnull");
728 if (!n_lg || ren_div || wb_lig(wb, c)) {
729 if (n_kn && !ren_div)
730 wb_kern(wb, c);
731 wb_put(wb, c);
735 /* read one character and place it inside wb buffer */
736 int ren_char(struct wb *wb, int (*next)(void), void (*back)(int))
738 char c[GNLEN * 4];
739 if (charnext(c, next, back) < 0)
740 return -1;
741 ren_put(wb, c, next, back);
742 return 0;
745 /* like ren_char(); return 1 if d1 was read and d2 if d2 was read */
746 static int ren_chardel(struct wb *wb, int (*next)(void), void (*back)(int),
747 char *d1, char *d2)
749 char c[GNLEN * 4];
750 if (charnext(c, next, back) < 0)
751 return -1;
752 if (d1 && !strcmp(d1, c))
753 return 1;
754 if (d2 && !strcmp(d2, c))
755 return 2;
756 ren_put(wb, c, next, back);
757 return 0;
760 /* read the argument of \w and push its width */
761 int ren_wid(int (*next)(void), void (*back)(int))
763 char delim[GNLEN];
764 int c, n;
765 struct wb wb;
766 wb_init(&wb);
767 charnext(delim, next, back);
768 odiv_beg();
769 c = next();
770 while (c >= 0 && c != '\n') {
771 back(c);
772 if (ren_chardel(&wb, next, back, delim, NULL))
773 break;
774 c = next();
776 odiv_end();
777 n = wb_wid(&wb);
778 wb_wconf(&wb, &n_ct, &n_st, &n_sb);
779 wb_done(&wb);
780 return n;
783 /* return 1 if d1 was read and 2 if d2 was read */
784 static int ren_until(struct wb *wb, char *d1, char *d2,
785 int (*next)(void), void (*back)(int))
787 int c, ret;
788 c = next();
789 while (c >= 0 && c != '\n') {
790 back(c);
791 ret = ren_chardel(wb, next, back, d1, d2);
792 if (ret)
793 return ret;
794 c = next();
796 if (c == '\n')
797 back(c);
798 return 0;
801 static void wb_cpy(struct wb *dst, struct wb *src, int left)
803 wb_hmov(dst, left - wb_wid(dst));
804 wb_cat(dst, src);
807 void ren_tl(int (*next)(void), void (*back)(int))
809 struct adj *adj;
810 struct wb wb, wb2;
811 char delim[GNLEN];
812 adj = adj_alloc();
813 wb_init(&wb);
814 wb_init(&wb2);
815 charnext(delim, next, back);
816 /* the left-adjusted string */
817 ren_until(&wb2, delim, NULL, next, back);
818 wb_cpy(&wb, &wb2, 0);
819 /* the centered string */
820 ren_until(&wb2, delim, NULL, next, back);
821 wb_cpy(&wb, &wb2, (n_lt - wb_wid(&wb2)) / 2);
822 /* the right-adjusted string */
823 ren_until(&wb2, delim, NULL, next, back);
824 wb_cpy(&wb, &wb2, n_lt - wb_wid(&wb2));
825 /* flushing the line */
826 adj_ll(adj, n_lt);
827 adj_wb(adj, &wb);
828 adj_nl(adj);
829 ren_bradj(adj, 0, AD_L, 0);
830 adj_free(adj);
831 wb_done(&wb2);
832 wb_done(&wb);
835 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int))
837 struct wb wbs[NFIELDS];
838 int i, n = 0;
839 int wid = 0;
840 int left, right, cur_left;
841 int pad, rem;
842 while (n < LEN(wbs)) {
843 wb_init(&wbs[n]);
844 if (ren_until(&wbs[n++], c_fb, c_fa, next, back) != 1)
845 break;
847 left = RENWB(wb) ? f_hpos() : wb_wid(wb);
848 right = tab_next(left);
849 for (i = 0; i < n; i++)
850 wid += wb_wid(&wbs[i]);
851 pad = (right - left - wid) / (n > 1 ? n - 1 : 1);
852 rem = (right - left - wid) % (n > 1 ? n - 1 : 1);
853 for (i = 0; i < n; i++) {
854 if (i == 0)
855 cur_left = left;
856 else if (i == n - 1)
857 cur_left = right - wb_wid(&wbs[i]);
858 else
859 cur_left = wb_wid(wb) + pad + (i + rem >= n);
860 wb_cpy(wb, &wbs[i], cur_left);
861 wb_done(&wbs[i]);
865 static void ren_tab(struct wb *wb, char *tc, int (*next)(void), void (*back)(int))
867 struct wb t;
868 int pos = RENWB(wb) ? f_hpos() : wb_wid(wb);
869 int ins = tab_next(pos); /* insertion position */
870 int typ = tab_type(pos); /* tab type */
871 int c;
872 wb_init(&t);
873 if (typ == 'R' || typ == 'C') {
874 c = next();
875 while (c >= 0 && c != '\n' && c != '\t' && c != '\x01') {
876 back(c);
877 ren_char(&t, next, back);
878 c = next();
880 back(c);
882 if (typ == 'C')
883 ins -= wb_wid(&t) / 2;
884 if (typ == 'R')
885 ins -= wb_wid(&t);
886 if (!tc[0] || ins <= pos)
887 wb_hmov(wb, ins - pos);
888 else
889 ren_hline(wb, ins - pos, tc);
890 wb_cat(wb, &t);
891 wb_done(&t);
894 /* read characters from in.c and pass rendered lines to out.c */
895 int render(void)
897 struct wb *wb = &ren_wb;
898 int fillreq;
899 int c;
900 n_nl = -1;
901 wb_init(wb);
902 tr_first();
903 ren_first(); /* transition to the first page */
904 c = ren_next();
905 while (1) {
906 if (ren_aborted)
907 return 1;
908 if (c < 0) {
909 if (bp_final >= 2)
910 break;
911 if (bp_final == 0 && trap_em >= 0) {
912 trap_exec(trap_em);
913 bp_final = 1;
914 } else {
915 bp_final = 2;
916 push_eject();
917 push_br();
919 c = ren_next();
920 continue;
922 ren_cnl = c == '\n';
923 fillreq = 0;
924 /* add wb (the current word) to cadj */
925 if (c == ' ' || c == '\n') {
926 adj_swid(cadj, spacewid(n_f, n_s));
927 if (!wb_part(wb)) { /* not after a \c */
928 adj_wb(cadj, wb);
929 fillreq = ren_fillreq;
930 ren_fillreq = 0;
931 if (c == '\n')
932 adj_nl(cadj);
933 else
934 adj_sp(cadj);
937 while ((fillreq && !n_ce && n_u) || adj_full(cadj, !n_ce && n_u)) {
938 ren_br(0);
939 fillreq = 0;
941 if (c == '\n' || ren_nl) /* end or start of input line */
942 n_lb = f_hpos();
943 if (c == '\n' && n_it && --n_itn == 0)
944 trap_exec(n_it);
945 if (c == '\n' && !wb_part(wb))
946 n_ce = MAX(0, n_ce - 1);
947 if (c != ' ') {
948 ren_back(c);
949 ren_char(wb, ren_next, ren_back);
950 if (c != '\n' && wb_empty(wb))
951 adj_nonl(cadj);
953 ren_nl = c == '\n';
954 c = ren_next();
956 bp_final = 3;
957 if (!adj_empty(cadj, 0))
958 ren_page(bp_next, 1);
959 ren_br(1);
960 wb_done(wb);
961 return 0;
964 /* trap handling */
966 #define tposval(i) (tpos[i] < 0 ? n_p + tpos[i] : tpos[i])
968 static int tpos[NTRAPS]; /* trap positions */
969 static int treg[NTRAPS]; /* trap registers */
970 static int ntraps;
972 static int trap_first(int pos)
974 int best = -1;
975 int i;
976 for (i = 0; i < ntraps; i++)
977 if (treg[i] >= 0 && tposval(i) > pos)
978 if (best < 0 || tposval(i) < tposval(best))
979 best = i;
980 return best;
983 static int trap_byreg(int reg)
985 int i;
986 for (i = 0; i < ntraps; i++)
987 if (treg[i] == reg)
988 return i;
989 return -1;
992 static int trap_bypos(int reg, int pos)
994 int i;
995 for (i = 0; i < ntraps; i++)
996 if (treg[i] >= 0 && tposval(i) == pos)
997 if (reg == -1 || treg[i] == reg)
998 return i;
999 return -1;
1002 void tr_wh(char **args)
1004 int reg, pos, id;
1005 if (!args[1])
1006 return;
1007 pos = eval(args[1], 'v');
1008 id = trap_bypos(-1, pos);
1009 if (!args[2]) {
1010 if (id >= 0)
1011 treg[id] = -1;
1012 return;
1014 reg = map(args[2]);
1015 if (id < 0)
1016 id = trap_byreg(-1);
1017 if (id < 0)
1018 id = ntraps++;
1019 tpos[id] = pos;
1020 treg[id] = reg;
1023 void tr_ch(char **args)
1025 int reg;
1026 int id;
1027 if (!args[1])
1028 return;
1029 reg = map(args[1]);
1030 id = trap_byreg(reg);
1031 if (id >= 0)
1032 tpos[id] = args[2] ? eval(args[2], 'v') : -1;
1035 void tr_dt(char **args)
1037 if (!cdiv)
1038 return;
1039 if (args[2]) {
1040 cdiv->tpos = eval(args[1], 'v');
1041 cdiv->treg = map(args[2]);
1042 } else {
1043 cdiv->treg = -1;
1047 void tr_em(char **args)
1049 trap_em = args[1] ? map(args[1]) : -1;
1052 static int trap_pos(int pos)
1054 int ret = trap_first(pos);
1055 if (bp_final >= 3)
1056 return -1;
1057 if (cdiv)
1058 return cdiv->treg && cdiv->tpos > pos ? cdiv->tpos : -1;
1059 return ret >= 0 ? tposval(ret) : -1;
1062 static int trap_reg(int pos)
1064 int ret = trap_first(pos);
1065 if (cdiv)
1066 return cdiv->treg && cdiv->tpos > pos ? cdiv->treg : -1;
1067 return ret >= 0 ? treg[ret] : -1;
1070 int f_nexttrap(void)
1072 int pos = trap_pos(n_d);
1073 if (cdiv)
1074 return pos >= 0 ? pos : 0x7fffffff;
1075 return (pos >= 0 && pos < n_p ? pos : n_p) - n_d;