ren: support fields
[neatroff.git] / ren.c
bloba186aea9ce5125e5bc6069f71661d06b550db81d
1 #include <ctype.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include "xroff.h"
7 #define cadj env_adj() /* line buffer */
9 /* diversions */
10 struct div {
11 struct sbuf sbuf; /* diversion output */
12 int reg; /* diversion register */
13 int tpos; /* diversion trap position */
14 int treg; /* diversion trap register */
15 int dl; /* diversion width */
16 int prev_d; /* previous \(.d value */
17 int prev_h; /* previous \(.h value */
18 int prev_mk; /* previous .mk internal register */
19 int prev_ns; /* previous .ns value */
21 static struct div divs[NPREV]; /* diversion stack */
22 static struct div *cdiv; /* current diversion */
23 static int ren_div; /* rendering a diversion */
25 static struct wb ren_wb; /* the main ren.c word buffer */
26 static int ren_nl; /* just after newline */
27 static int ren_unbuf[8]; /* ren_back() buffer */
28 static int ren_un;
30 static int bp_first = 1; /* prior to the first page */
31 static int bp_next = 1; /* next page number */
32 static int bp_force; /* execute the traps until the next page */
34 static int c_fa; /* field delimiter */
35 static char c_fb[GNLEN]; /* field padding */
37 static int ren_next(void)
39 return ren_un > 0 ? ren_unbuf[--ren_un] : tr_next();
42 static void ren_back(int c)
44 ren_unbuf[ren_un++] = c;
47 void tr_di(char **args)
49 if (args[1]) {
50 cdiv = cdiv ? cdiv + 1 : divs;
51 memset(cdiv, 0, sizeof(*cdiv));
52 sbuf_init(&cdiv->sbuf);
53 cdiv->reg = REG(args[1][0], args[1][1]);
54 cdiv->treg = -1;
55 if (args[0][2] == 'a' && str_get(cdiv->reg)) /* .da */
56 sbuf_append(&cdiv->sbuf, str_get(cdiv->reg));
57 sbuf_printf(&cdiv->sbuf, "%c%s\n", c_cc, DIV_BEG);
58 cdiv->prev_d = n_d;
59 cdiv->prev_h = n_h;
60 cdiv->prev_mk = n_mk;
61 cdiv->prev_ns = n_ns;
62 n_d = 0;
63 n_h = 0;
64 n_mk = 0;
65 n_ns = 0;
66 } else if (cdiv) {
67 sbuf_putnl(&cdiv->sbuf);
68 sbuf_printf(&cdiv->sbuf, "%c%s\n", c_cc, DIV_END);
69 str_set(cdiv->reg, sbuf_buf(&cdiv->sbuf));
70 sbuf_done(&cdiv->sbuf);
71 n_dl = cdiv->dl;
72 n_dn = n_d;
73 n_d = cdiv->prev_d;
74 n_h = cdiv->prev_h;
75 n_mk = cdiv->prev_mk;
76 n_ns = cdiv->prev_ns;
77 cdiv = cdiv > divs ? cdiv - 1 : NULL;
81 int f_divreg(void)
83 return cdiv ? cdiv->reg : -1;
86 int f_hpos(void)
88 return adj_wid(cadj) + wb_wid(&ren_wb);
91 void tr_divbeg(char **args)
93 odiv_beg();
94 ren_div++;
97 void tr_divend(char **args)
99 odiv_end();
100 ren_div--;
103 static int trap_reg(int pos);
104 static int trap_pos(int pos);
105 static void trap_exec(int reg);
107 static void ren_page(int pg)
109 n_nl = 0;
110 n_d = 0;
111 n_h = 0;
112 n_pg = pg;
113 bp_next = n_pg + 1;
114 out("p%d\n", pg);
115 out("V%d\n", 0);
116 if (trap_pos(-1) == 0)
117 trap_exec(trap_reg(-1));
120 static void ren_first(void)
122 if (bp_first && !cdiv) {
123 bp_first = 0;
124 ren_page(bp_next);
128 static void ren_sp(int n)
130 char cmd[32];
131 ren_first();
132 if (!n && ren_div && !n_u)
133 return;
134 n_d += n ? n : n_v;
135 if (n_d > n_h)
136 n_h = n_d;
137 if (cdiv) {
138 sbuf_putnl(&cdiv->sbuf);
139 sprintf(cmd, "'sp %du\n", n ? n : n_v);
140 sbuf_append(&cdiv->sbuf, cmd);
141 } else {
142 n_nl = n_d;
146 static void push_ne(void)
148 char buf[32];
149 sprintf(buf, "%cne %du\n", c_cc, n_p);
150 in_pushnl(buf, NULL);
153 static void trap_exec(int reg)
155 if (bp_force)
156 push_ne();
157 if (str_get(reg))
158 in_pushnl(str_get(reg), NULL);
161 /* return 1 if executed a trap */
162 static int ren_traps(int beg, int end, int dosp)
164 int pos = trap_pos(beg);
165 if (pos >= 0 && (cdiv || pos < n_p) && pos <= end) {
166 if (dosp && pos > beg)
167 ren_sp(pos - beg);
168 trap_exec(trap_reg(beg));
169 return 1;
171 return 0;
174 /* start a new page if needed */
175 static int ren_pagelimit(int ne)
177 if (n_nl + ne >= n_p && !cdiv) {
178 bp_force = 0;
179 ren_page(bp_next);
180 return 1;
182 return 0;
185 static void down(int n)
187 if (!ren_traps(n_d, n_d + (n ? n : n_v), 1)) {
188 ren_sp(n);
189 ren_pagelimit(0);
193 /* flush the given line and send it to out.c */
194 static void ren_line(char *s, int w, int ad, int ll, int li, int lt)
196 int ljust = 0;
197 char cmd[32];
198 int llen = ll - (lt >= 0 ? lt : li);
199 n_n = w;
200 if (ad == AD_C)
201 ljust = llen > w ? (llen - w) / 2 : 0;
202 if (ad == AD_R)
203 ljust = llen - w;
204 if (cdiv) {
205 if (cdiv->dl < w)
206 cdiv->dl = w;
207 ljust += lt >= 0 ? lt : li;
208 if (ljust) {
209 sprintf(cmd, "%ch'%du'", c_ec, ljust);
210 sbuf_append(&cdiv->sbuf, cmd);
212 sbuf_append(&cdiv->sbuf, s);
213 } else {
214 out("H%d\n", n_o + (lt >= 0 ? lt : li) + ljust);
215 out("V%d\n", n_d);
216 out_line(s);
220 static void ren_transparent(char *s)
222 if (cdiv)
223 sbuf_printf(&cdiv->sbuf, "%s\n", s);
224 else
225 out("%s\n", s);
228 /* return 1 if triggered a trap */
229 static int ren_bradj(struct adj *adj, int fill, int ad)
231 struct sbuf sbuf;
232 int ll, li, lt, els_neg, els_pos;
233 int w, prev_d;
234 ren_first();
235 if (!adj_empty(adj, fill)) {
236 sbuf_init(&sbuf);
237 w = adj_fill(adj, ad == AD_B, fill, &sbuf,
238 &ll, &li, &lt, &els_neg, &els_pos);
239 prev_d = n_d;
240 if (els_neg)
241 ren_sp(-els_neg);
242 if (!n_ns || w || els_neg || els_pos) {
243 ren_sp(0);
244 ren_line(sbuf_buf(&sbuf), w, ad, ll, li, lt);
245 n_ns = 0;
247 sbuf_done(&sbuf);
248 if (els_pos)
249 ren_sp(els_pos);
250 n_a = els_pos;
251 if (!ren_traps(prev_d, n_d, 0)) {
252 if (n_L > 1 && (cdiv || n_d < n_p)) {
253 down(n_L * n_v - n_v);
254 } else {
255 if (ren_pagelimit(0))
256 return 1;
258 return 0;
260 return 1;
262 return 0;
265 /* return 1 if triggered a trap */
266 static int ren_br(int force)
268 return ren_bradj(cadj, !force && !n_ce && n_u,
269 n_ce ? AD_C : (n_u && !n_na && (n_j != AD_B || !force) ? n_j : AD_L));
272 void tr_br(char **args)
274 if (args[0][0] == c_cc)
275 ren_br(1);
278 void tr_sp(char **args)
280 int traps = 0;
281 int n = args[1] ? eval(args[1], 'v') : n_v;
282 if (args[0][0] == c_cc)
283 traps = ren_br(1);
284 if (n && !n_ns && !traps)
285 down(n);
288 void tr_sv(char **args)
290 int n = eval(args[1], 'v');
291 n_sv = 0;
292 if (n_d + n < f_nexttrap())
293 down(n);
294 else
295 n_sv = n;
298 void tr_ns(char **args)
300 n_ns = 1;
303 void tr_rs(char **args)
305 n_ns = 0;
308 void tr_os(char **args)
310 if (n_sv)
311 down(n_sv);
312 n_sv = 0;
315 void tr_mk(char **args)
317 if (args[1])
318 num_set(REG(args[1][0], args[1][1]), n_d);
319 else
320 n_mk = n_d;
323 void tr_rt(char **args)
325 int n = args[1] ? eval_re(args[1], n_d, 'v') : n_mk;
326 if (n >= 0 && n < n_d)
327 ren_sp(n - n_d);
330 void tr_ne(char **args)
332 int n = args[1] ? eval(args[1], 'v') : n_v;
333 if (!ren_traps(n_d, n_d + n - 1, 1))
334 ren_pagelimit(n);
337 void tr_bp(char **args)
339 char br[] = {c_cc, 'b', 'r', '\n'};
340 if (!cdiv && (args[1] || !n_ns)) {
341 if (!bp_force)
342 push_ne();
343 if (args[0][0] == c_cc)
344 in_pushnl(br, NULL);
345 bp_force = 1;
346 if (args[1])
347 bp_next = eval_re(args[1], n_pg, 0);
351 void tr_pn(char **args)
353 if (args[1])
354 bp_next = eval_re(args[1], n_pg, 0);
357 static void ren_ps(char *s)
359 int ps = !s || !*s || !strcmp("0", s) ? n_s0 : eval_re(s, n_s, 0);
360 n_s0 = n_s;
361 n_s = MAX(1, ps);
364 void tr_ps(char **args)
366 ren_ps(args[1]);
369 void tr_ll(char **args)
371 int ll = args[1] ? eval_re(args[1], n_l, 'm') : n_l0;
372 n_l0 = n_l;
373 n_l = MAX(0, ll);
374 adj_ll(cadj, n_l);
377 void tr_in(char **args)
379 int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0;
380 if (args[0][0] == c_cc)
381 ren_br(1);
382 n_i0 = n_i;
383 n_i = MAX(0, in);
384 adj_in(cadj, n_i);
385 adj_ti(cadj, -1);
388 void tr_ti(char **args)
390 if (args[0][0] == c_cc)
391 ren_br(1);
392 if (args[1])
393 adj_ti(cadj, eval_re(args[1], n_i, 'm'));
396 static void ren_ft(char *s)
398 int fn = !*s || !strcmp("P", s) ? n_f0 : dev_font(s);
399 if (fn >= 0) {
400 n_f0 = n_f;
401 n_f = fn;
405 void tr_ft(char **args)
407 if (args[1])
408 ren_ft(args[1]);
411 void tr_fp(char **args)
413 if (!args[2])
414 return;
415 if (dev_mnt(atoi(args[1]), args[2], args[3] ? args[3] : args[2]) < 0)
416 errmsg("troff: failed to mount %s\n", args[2]);
419 void tr_nf(char **args)
421 if (args[0][0] == c_cc)
422 ren_br(1);
423 n_u = 0;
426 void tr_fi(char **args)
428 if (args[0][0] == c_cc)
429 ren_br(1);
430 n_u = 1;
433 void tr_ce(char **args)
435 if (args[0][0] == c_cc)
436 ren_br(1);
437 n_ce = args[1] ? atoi(args[1]) : 1;
440 void tr_fc(char **args)
442 if (args[1]) {
443 c_fa = args[1][0];
444 strcpy(c_fb, args[2] ? args[2] : " ");
445 } else {
446 c_fa = -1;
447 c_fb[0] = '\0';
451 static void escarg_ren(char *d, int cmd, int (*next)(void), void (*back)(int))
453 char delim[GNLEN];
454 int c;
455 if (strchr(ESC_P, cmd)) {
456 c = next();
457 if (cmd == 's' && (c == '-' || c == '+')) {
458 *d++ = c;
459 c = next();
461 if (c == '(') {
462 *d++ = next();
463 *d++ = next();
464 } else {
465 *d++ = c;
466 if (cmd == 's' && c >= '1' && c <= '3') {
467 c = next();
468 if (isdigit(c))
469 *d++ = c;
470 else
471 back(c);
475 if (strchr(ESC_Q, cmd)) {
476 schar_read(delim, next);
477 while (schar_jump(delim, next, back)) {
478 if ((c = next()) < 0)
479 break;
480 *d++ = c;
483 *d = '\0';
486 static int nextchar(char *s, int (*next)(void))
488 int c = next();
489 int l = utf8len(c);
490 int i;
491 if (c < 0)
492 return 0;
493 s[0] = c;
494 for (i = 1; i < l; i++)
495 s[i] = next();
496 s[l] = '\0';
497 return l;
500 static void ren_cmd(struct wb *wb, int c, char *arg)
502 struct glyph *g;
503 switch (c) {
504 case ' ':
505 wb_hmov(wb, charwid(dev_spacewid(), n_s));
506 break;
507 case 'b':
508 ren_bracket(wb, arg);
509 break;
510 case 'c':
511 wb_setpart(wb);
512 break;
513 case 'D':
514 ren_draw(wb, arg);
515 break;
516 case 'd':
517 wb_vmov(wb, SC_EM / 2);
518 break;
519 case 'f':
520 ren_ft(arg);
521 break;
522 case 'h':
523 wb_hmov(wb, eval(arg, 'm'));
524 break;
525 case 'k':
526 num_set(REG(arg[0], arg[1]),
527 wb == &ren_wb ? f_hpos() - n_lb : wb_wid(wb));
528 break;
529 case 'L':
530 ren_vline(wb, arg);
531 break;
532 case 'l':
533 ren_hline(wb, arg);
534 break;
535 case 'o':
536 ren_over(wb, arg);
537 break;
538 case 'r':
539 wb_vmov(wb, -SC_EM);
540 break;
541 case 's':
542 ren_ps(arg);
543 break;
544 case 'u':
545 wb_vmov(wb, -SC_EM / 2);
546 break;
547 case 'v':
548 wb_vmov(wb, eval(arg, 'v'));
549 break;
550 case 'X':
551 wb_etc(wb, arg);
552 break;
553 case 'x':
554 wb_els(wb, eval(arg, 'v'));
555 break;
556 case '0':
557 g = dev_glyph("0", n_f);
558 wb_hmov(wb, charwid(g ? g->wid : SC_DW, n_s));
559 break;
560 case '|':
561 wb_hmov(wb, SC_EM / 6);
562 break;
563 case '^':
564 wb_hmov(wb, SC_EM / 12);
565 break;
566 case '{':
567 case '}':
568 break;
572 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int));
574 /* read one character and place it inside wb buffer */
575 void ren_char(struct wb *wb, int (*next)(void), void (*back)(int))
577 char c[GNLEN * 4];
578 char arg[ILNLEN];
579 char *s;
580 int w, n;
581 nextchar(c, next);
582 if (c[0] == ' ' || c[0] == '\n') {
583 wb_put(wb, c);
584 return;
586 if (c[0] == '\t' || c[0] == '\x01') {
587 n = wb == &ren_wb ? f_hpos() : wb_wid(wb);
588 wb_hmov(wb, tab_next(n) - n);
589 return;
591 if (c[0] == c_fa) {
592 ren_field(wb, next, back);
593 return;
595 if (c[0] == c_ec) {
596 nextchar(c + 1, next);
597 if (c[1] == '(') {
598 int l = nextchar(c + 2, next);
599 l += nextchar(c + 2 + l, next);
600 c[2 + l] = '\0';
601 } else if (c[1] == 'z') {
602 w = wb_wid(wb);
603 ren_char(wb, next, back);
604 wb_hmov(wb, w - wb_wid(wb));
605 return;
606 } else if (c[1] == '!') {
607 if (ren_nl && next == ren_next) {
608 s = arg;
609 n = next();
610 while (n >= 0 && n != '\n') {
611 *s++ = n;
612 n = next();
614 *s = '\0';
615 ren_transparent(arg);
617 return;
618 } else if (strchr(" bcDdfhkLlorsuvXxz0^|{}&", c[1])) {
619 escarg_ren(arg, c[1], next, back);
620 ren_cmd(wb, c[1], arg);
621 return;
624 if (c[0] == c_ni)
625 nextchar(c + 1, next);
626 wb_put(wb, c);
629 /* read the argument of \w and push its width */
630 int ren_wid(int (*next)(void), void (*back)(int))
632 char delim[GNLEN];
633 int c, n;
634 struct wb wb;
635 wb_init(&wb);
636 schar_read(delim, next);
637 odiv_beg();
638 c = next();
639 while (c >= 0 && c != '\n') {
640 back(c);
641 if (!schar_jump(delim, next, back))
642 break;
643 ren_char(&wb, next, back);
644 c = next();
646 odiv_end();
647 n = wb_wid(&wb);
648 wb_wconf(&wb, &n_ct, &n_st, &n_sb);
649 wb_done(&wb);
650 return n;
653 /* return 1 if the ending character (ec) was read */
654 static int ren_until(struct wb *wb, char *delim, int ec,
655 int (*next)(void), void (*back)(int))
657 int c;
658 c = next();
659 while (c >= 0 && c != '\n' && c != ec) {
660 back(c);
661 if (!schar_jump(delim, next, back))
662 break;
663 ren_char(wb, next, back);
664 c = next();
666 if (c == '\n')
667 back(c);
668 return c == ec;
671 static void wb_cpy(struct wb *dst, struct wb *src, int left)
673 wb_hmov(dst, left - wb_wid(dst));
674 wb_cat(dst, src);
677 void ren_tl(int (*next)(void), void (*back)(int))
679 struct adj *adj;
680 struct wb wb, wb2;
681 char delim[GNLEN];
682 adj = adj_alloc();
683 wb_init(&wb);
684 wb_init(&wb2);
685 schar_read(delim, next);
686 /* the left-adjusted string */
687 ren_until(&wb2, delim, '\n', next, back);
688 wb_cpy(&wb, &wb2, 0);
689 /* the centered string */
690 ren_until(&wb2, delim, '\n', next, back);
691 wb_cpy(&wb, &wb2, (n_lt - wb_wid(&wb2)) / 2);
692 /* the right-adjusted string */
693 ren_until(&wb2, delim, '\n', next, back);
694 wb_cpy(&wb, &wb2, n_lt - wb_wid(&wb2));
695 /* flushing the line */
696 adj_ll(adj, n_lt);
697 adj_wb(adj, &wb);
698 adj_nl(adj);
699 ren_bradj(adj, 0, AD_L);
700 adj_free(adj);
701 wb_done(&wb2);
702 wb_done(&wb);
705 static void ren_field(struct wb *wb, int (*next)(void), void (*back)(int))
707 struct wb wbs[NFIELDS];
708 int i, n = 0;
709 int wid = 0;
710 int left, right, cur_left;
711 int pad, rem;
712 while (n < LEN(wbs)) {
713 wb_init(&wbs[n]);
714 if (ren_until(&wbs[n++], c_fb, c_fa, next, back))
715 break;
717 left = wb == &ren_wb ? f_hpos() : wb_wid(wb);
718 right = tab_next(left);
719 for (i = 0; i < n; i++)
720 wid += wb_wid(&wbs[i]);
721 pad = (right - left - wid) / (n > 1 ? n - 1 : 1);
722 rem = (right - left - wid) % (n > 1 ? n - 1 : 1);
723 for (i = 0; i < n; i++) {
724 if (i == 0)
725 cur_left = left;
726 else if (i == n - 1)
727 cur_left = right - wb_wid(&wbs[i]);
728 else
729 cur_left = wb_wid(wb) + pad + (i + rem >= n);
730 wb_cpy(wb, &wbs[i], cur_left);
731 wb_done(&wbs[i]);
735 /* read characters from in.c and pass rendered lines to out.c */
736 void render(void)
738 struct wb *wb = &ren_wb;
739 int c;
740 n_nl = -1;
741 wb_init(wb);
742 tr_first();
743 ren_first(); /* transition to the first page */
744 c = ren_next();
745 while (c >= 0) {
746 if (c == ' ' || c == '\n') {
747 adj_swid(cadj, charwid(dev_spacewid(), n_s));
748 adj_wb(cadj, wb);
749 if (!wb_part(wb)) {
750 if (c == '\n')
751 adj_nl(cadj);
752 else
753 adj_sp(cadj);
756 while (adj_full(cadj, !n_ce && n_u))
757 ren_br(0);
758 if (c == '\n' || ren_nl) /* end or start of input line */
759 n_lb = f_hpos();
760 if (c == '\n' && !wb_part(wb))
761 n_ce = MAX(0, n_ce - 1);
762 if (c != ' ') {
763 ren_back(c);
764 ren_char(wb, ren_next, ren_back);
766 ren_nl = c == '\n';
767 c = ren_next();
769 ren_br(1);
770 wb_done(wb);
773 /* trap handling */
775 static int tpos[NTRAPS]; /* trap positions */
776 static int treg[NTRAPS]; /* trap registers */
777 static int ntraps;
779 static int trap_first(int pos)
781 int best = -1;
782 int i;
783 for (i = 0; i < ntraps; i++)
784 if (treg[i] >= 0 && tpos[i] > pos)
785 if (best < 0 || tpos[i] < tpos[best])
786 best = i;
787 return best;
790 static int trap_byreg(int reg)
792 int i;
793 for (i = 0; i < ntraps; i++)
794 if (treg[i] == reg)
795 return i;
796 return -1;
799 static int trap_bypos(int reg, int pos)
801 int i;
802 for (i = 0; i < ntraps; i++)
803 if (treg[i] >= 0 && tpos[i] == pos)
804 if (reg == -1 || treg[i] == reg)
805 return i;
806 return -1;
809 static int tpos_parse(char *s)
811 int pos = eval(s, 'v');
812 return pos >= 0 ? pos : n_p + pos;
815 void tr_wh(char **args)
817 int reg, pos, id;
818 if (!args[1])
819 return;
820 pos = tpos_parse(args[1]);
821 id = trap_bypos(-1, pos);
822 if (!args[2]) {
823 if (id >= 0)
824 treg[id] = -1;
825 return;
827 reg = REG(args[2][0], args[2][1]);
828 if (id < 0)
829 id = trap_byreg(-1);
830 if (id < 0)
831 id = ntraps++;
832 tpos[id] = pos;
833 treg[id] = reg;
836 void tr_ch(char **args)
838 int reg;
839 int id;
840 if (!args[1])
841 return;
842 reg = REG(args[1][0], args[1][1]);
843 id = trap_byreg(reg);
844 if (id)
845 tpos[id] = args[2] ? tpos_parse(args[2]) : -1;
848 void tr_dt(char **args)
850 if (!cdiv)
851 return;
852 if (args[2]) {
853 cdiv->tpos = eval(args[1], 'v');
854 cdiv->treg = REG(args[2][0], args[2][1]);
855 } else {
856 cdiv->treg = -1;
860 static int trap_pos(int pos)
862 int ret = trap_first(pos);
863 if (cdiv)
864 return cdiv->treg && cdiv->tpos > pos ? cdiv->tpos : -1;
865 return ret >= 0 ? tpos[ret] : -1;
868 static int trap_reg(int pos)
870 int ret = trap_first(pos);
871 if (cdiv)
872 return cdiv->treg && cdiv->tpos > pos ? cdiv->treg : -1;
873 return ret >= 0 ? treg[ret] : -1;
876 int f_nexttrap(void)
878 int pos = trap_pos(n_d);
879 if (cdiv)
880 return pos >= 0 ? pos : 0x7fffffff;
881 return (pos >= 0 && pos < n_p ? pos : n_p) - n_d;