ren: .ti +-N should be relative to \n(.i
[neatroff.git] / ren.c
blob6439964ed8c3b13612d4a68324a01546bbce6c19
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_f = -1; /* last rendered n_f */
24 static int ren_s = -1; /* last rendered n_s */
25 static int ren_div; /* rendering a diversion */
27 static int ren_backed = -1; /* pushed back character */
29 static int bp_first = 1; /* prior to the first page */
30 static int bp_next = 1; /* next page number */
31 static int bp_force; /* execute the traps until the next page */
33 static int ren_next(void)
35 int c = ren_backed >= 0 ? ren_backed : tr_next();
36 ren_backed = -1;
37 return c;
40 static void ren_back(int c)
42 ren_backed = c;
45 void tr_di(char **args)
47 if (args[1]) {
48 cdiv = cdiv ? cdiv + 1 : divs;
49 memset(cdiv, 0, sizeof(*cdiv));
50 sbuf_init(&cdiv->sbuf);
51 cdiv->reg = REG(args[1][0], args[1][1]);
52 cdiv->treg = -1;
53 if (args[0][2] == 'a' && str_get(cdiv->reg)) /* .da */
54 sbuf_append(&cdiv->sbuf, str_get(cdiv->reg));
55 sbuf_append(&cdiv->sbuf, DIV_BEG "\n");
56 ren_f = -1;
57 ren_s = -1;
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_append(&cdiv->sbuf, DIV_END "\n");
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;
78 ren_f = -1;
79 ren_s = -1;
83 int f_divreg(void)
85 return cdiv ? cdiv->reg : -1;
88 int f_hpos(void)
90 return adj_wid(cadj);
93 void tr_divbeg(char **args)
95 odiv_beg();
96 ren_div++;
99 void tr_divend(char **args)
101 odiv_end();
102 ren_div--;
105 static int trap_reg(int pos);
106 static int trap_pos(int pos);
107 static void trap_exec(int reg);
109 static void ren_page(int pg)
111 n_nl = 0;
112 n_d = 0;
113 n_h = 0;
114 n_pg = pg;
115 bp_next = n_pg + 1;
116 out("p%d\n", pg);
117 out("V%d\n", 0);
118 if (trap_pos(-1) == 0)
119 trap_exec(trap_reg(-1));
122 static void ren_first(void)
124 if (bp_first) {
125 bp_first = 0;
126 ren_page(bp_next);
130 static void ren_sp(int n)
132 char cmd[32];
133 ren_first();
134 if (!n && ren_div && !n_u)
135 return;
136 n_d += n ? n : n_v;
137 if (n_d > n_h)
138 n_h = n_d;
139 if (cdiv) {
140 sbuf_putnl(&cdiv->sbuf);
141 sprintf(cmd, "'sp %du\n", n ? n : n_v);
142 sbuf_append(&cdiv->sbuf, cmd);
143 } else {
144 n_nl = n_d;
148 static void push_ne(void)
150 char buf[32];
151 sprintf(buf, ".ne %du\n", n_p);
152 in_pushnl(buf, NULL);
155 static void trap_exec(int reg)
157 if (bp_force)
158 push_ne();
159 if (str_get(reg))
160 in_pushnl(str_get(reg), NULL);
163 /* return 1 if executed a trap */
164 static int ren_traps(int beg, int end, int dosp)
166 int pos = trap_pos(beg);
167 if (pos >= 0 && (cdiv || pos < n_p) && pos <= end) {
168 if (dosp && pos > beg)
169 ren_sp(pos - beg);
170 trap_exec(trap_reg(beg));
171 return 1;
173 return 0;
176 /* start a new page if needed */
177 static int ren_pagelimit(int ne)
179 if (n_nl + ne >= n_p && !cdiv) {
180 bp_force = 0;
181 ren_page(bp_next);
182 return 1;
184 return 0;
187 static void down(int n)
189 if (!ren_traps(n_d, n_d + (n ? n : n_v), 1)) {
190 ren_sp(n);
191 ren_pagelimit(0);
195 /* flush the given line and send it to out.c */
196 static void ren_line(char *s, int w, int ll, int li, int lt)
198 int ljust = 0;
199 char cmd[32];
200 int llen = ll - (lt >= 0 ? lt : li);
201 n_n = w;
202 if (n_u && !n_na && (n_j == AD_C || n_j == AD_R))
203 ljust = n_j == AD_C ? (llen - w) / 2 : llen - w;
204 if (n_ce)
205 ljust = llen > w ? (llen - w) / 2 : 0;
206 if (cdiv) {
207 if (cdiv->dl < w)
208 cdiv->dl = w;
209 ljust += lt >= 0 ? lt : li;
210 if (ljust) {
211 sprintf(cmd, "\\h'%du'", ljust);
212 sbuf_append(&cdiv->sbuf, cmd);
214 sbuf_append(&cdiv->sbuf, s);
215 } else {
216 out("H%d\n", n_o + (lt >= 0 ? lt : li) + ljust);
217 out("V%d\n", n_d);
218 out_line(s);
222 /* return 1 if triggered a trap */
223 static int ren_br(int force)
225 char buf[LNLEN];
226 int ll, li, lt, els_neg, els_pos;
227 int adj_b, fill, w, prev_d;
228 ren_first();
229 if (!adj_empty(cadj, !n_ce && n_u)) {
230 adj_b = !n_ce && n_u && !n_na && n_j == AD_B;
231 fill = !n_ce && n_u;
232 w = adj_fill(cadj, !force && adj_b, !force && fill, buf,
233 &ll, &li, &lt, &els_neg, &els_pos);
234 prev_d = n_d;
235 if (els_neg)
236 ren_sp(-els_neg);
237 if (!n_ns || w || els_neg || els_pos) {
238 ren_sp(0);
239 ren_line(buf, w, ll, li, lt);
240 n_ns = 0;
242 if (els_pos)
243 ren_sp(els_pos);
244 n_a = els_pos;
245 if (!ren_traps(prev_d, n_d, 0)) {
246 if (n_L > n_v && (cdiv || n_d < n_p)) {
247 down(n_L - n_v);
248 } else {
249 if (ren_pagelimit(0))
250 return 1;
252 return 0;
254 return 1;
256 return 0;
259 void tr_br(char **args)
261 if (args[0][0] == '.')
262 ren_br(1);
265 void tr_sp(char **args)
267 int traps = 0;
268 if (args[0][0] == '.')
269 traps = ren_br(1);
270 if (!n_ns && !traps)
271 down(args[1] ? eval(args[1], 'v') : n_v);
274 void tr_sv(char **args)
276 int n = eval(args[1], 'v');
277 n_sv = 0;
278 if (n_d + n < f_nexttrap())
279 down(n);
280 else
281 n_sv = n;
284 void tr_ns(char **args)
286 n_ns = 1;
289 void tr_rs(char **args)
291 n_ns = 0;
294 void tr_os(char **args)
296 if (n_sv)
297 down(n_sv);
298 n_sv = 0;
301 void tr_mk(char **args)
303 if (args[1])
304 num_set(REG(args[1][0], args[1][1]), n_d);
305 else
306 n_mk = n_d;
309 void tr_rt(char **args)
311 int n = args[1] ? eval_re(args[1], n_d, 'v') : n_mk;
312 if (n >= 0 && n < n_d)
313 ren_sp(n - n_d);
316 void tr_ne(char **args)
318 int n = args[1] ? eval(args[1], 'v') : n_v;
319 if (!ren_traps(n_d, n_d + n - 1, 1))
320 ren_pagelimit(n);
323 void tr_bp(char **args)
325 if (!cdiv && (args[1] || !n_ns)) {
326 if (!bp_force)
327 push_ne();
328 if (args[0][0] == '.')
329 in_pushnl(".br\n", NULL);
330 bp_force = 1;
331 if (args[1])
332 bp_next = eval_re(args[1], n_pg, 0);
336 void tr_pn(char **args)
338 if (args[1])
339 bp_next = eval_re(args[1], n_pg, 0);
342 static void ren_ps(char *s)
344 int ps = !s || !*s || !strcmp("0", s) ? n_s0 : eval_re(s, n_s, 0);
345 n_s0 = n_s;
346 n_s = MAX(1, ps);
349 void tr_ps(char **args)
351 ren_ps(args[1]);
354 void tr_ll(char **args)
356 int ll = args[1] ? eval_re(args[1], n_l, 'm') : n_l0;
357 n_l0 = n_l;
358 n_l = MAX(0, ll);
359 adj_ll(cadj, n_l);
362 void tr_in(char **args)
364 int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0;
365 if (args[0][0] == '.')
366 ren_br(1);
367 n_i0 = n_i;
368 n_i = MAX(0, in);
369 adj_in(cadj, n_i);
370 adj_ti(cadj, -1);
373 void tr_ti(char **args)
375 if (args[0][0] == '.')
376 ren_br(1);
377 if (args[1])
378 adj_ti(cadj, eval_re(args[1], n_i, 'm'));
381 static void ren_ft(char *s)
383 int fn = !*s || !strcmp("P", s) ? n_f0 : dev_font(s);
384 if (fn >= 0) {
385 n_f0 = n_f;
386 n_f = fn;
390 void tr_ft(char **args)
392 if (args[1])
393 ren_ft(args[1]);
396 void tr_fp(char **args)
398 if (!args[2])
399 return;
400 if (dev_mnt(atoi(args[1]), args[2], args[3] ? args[3] : args[2]) < 0)
401 errmsg("troff: failed to mount %s\n", args[2]);
404 void tr_nf(char **args)
406 if (args[0][0] == '.')
407 ren_br(1);
408 n_u = 0;
411 void tr_fi(char **args)
413 if (args[0][0] == '.')
414 ren_br(1);
415 n_u = 1;
418 void tr_ce(char **args)
420 if (args[0][0] == '.')
421 ren_br(1);
422 n_ce = args[1] ? atoi(args[1]) : 1;
425 static void escarg_ren(char *d, int cmd, int (*next)(void), void (*back)(int))
427 int c, q;
428 if (strchr(ESC_P, cmd)) {
429 c = next();
430 if (cmd == 's' && (c == '-' || c == '+')) {
431 *d++ = c;
432 c = next();
434 if (c == '(') {
435 *d++ = next();
436 *d++ = next();
437 } else {
438 *d++ = c;
439 if (cmd == 's' && c >= '1' && c <= '3') {
440 c = next();
441 if (isdigit(c))
442 *d++ = c;
443 else
444 back(c);
448 if (strchr(ESC_Q, cmd)) {
449 q = next();
450 while (1) {
451 c = next();
452 if (c == q || c < 0)
453 break;
454 *d++ = c;
457 if (cmd == 'z')
458 *d++ = next();
459 *d = '\0';
462 static int nextchar(char *s, int (*next)(void))
464 int c = next();
465 int l = utf8len(c);
466 int i;
467 if (c < 0)
468 return 0;
469 s[0] = c;
470 for (i = 1; i < l; i++)
471 s[i] = next();
472 s[l] = '\0';
473 return l;
476 static void ren_cmd(struct adj *adj, int c, char *arg)
478 char draw_arg[ILNLEN];
479 struct glyph *g;
480 int n, w;
481 switch (c) {
482 case ' ':
483 w = dev_spacewid();
484 adj_put(adj, w, "\\h'%du'", w);
485 break;
486 case 'b':
487 ren_bracket(adj, arg);
488 break;
489 case 'D':
490 w = out_draw(arg, draw_arg);
491 adj_put(adj, w, "\\D'%s'", draw_arg);
492 break;
493 case 'd':
494 adj_put(adj, 0, "\\v'%du'", eval(".5m", 0));
495 break;
496 case 'f':
497 ren_ft(arg);
498 break;
499 case 'h':
500 n = eval(arg, 'm');
501 adj_put(adj, n, "\\h'%du'", n);
502 break;
503 case 'k':
504 num_set(REG(arg[0], arg[1]), f_hpos() - n_lb);
505 break;
506 case 'L':
507 ren_vline(adj, arg);
508 break;
509 case 'l':
510 ren_hline(adj, arg);
511 break;
512 case 'o':
513 ren_over(adj, arg);
514 break;
515 case 'r':
516 adj_put(adj, 0, "\\v'%du'", eval("-1m", 0));
517 break;
518 case 's':
519 ren_ps(arg);
520 break;
521 case 'u':
522 adj_put(adj, 0, "\\v'%du'", eval("-.5m", 0));
523 break;
524 case 'v':
525 adj_put(adj, 0, "\\v'%du'", eval(arg, 'v'));
526 break;
527 case 'X':
528 adj_put(adj, 0, "\\X'%s'", arg);
529 break;
530 case 'x':
531 adj_els(adj, eval(arg, 'v'));
532 break;
533 case '0':
534 g = dev_glyph("0", n_f);
535 w = charwid(g ? g->wid : SC_DW, n_s);
536 adj_put(adj, w, "\\h'%du'", w);
537 break;
538 case '|':
539 w = eval("1m/6", 0);
540 adj_put(adj, w, "\\h'%du'", w);
541 break;
542 case '^':
543 w = eval("1m/12", 0);
544 adj_put(adj, w, "\\h'%du'", w);
545 break;
546 case '{':
547 case '}':
548 break;
552 /* read one character and place it inside adj buffer */
553 static int ren_char(struct adj *adj, int (*next)(void), void (*back)(int))
555 char c[GNLEN * 4];
556 char arg[ILNLEN];
557 char widbuf[16];
558 struct glyph *g;
559 int zerowid = 0;
560 int n, w;
561 nextchar(c, next);
562 if (c[0] == ' ' || c[0] == '\n') {
563 adj_put(adj, charwid(dev_spacewid(), n_s), c);
564 return 0;
566 if (c[0] == '\\') {
567 nextchar(c + 1, next);
568 if (c[1] == '(') {
569 int l = nextchar(c + 2, next);
570 l += nextchar(c + 2 + l, next);
571 c[2 + l] = '\0';
572 } else if (c[1] == 'z') {
573 zerowid = 1;
574 nextchar(c, next);
575 if (c[0] == '\\') {
576 nextchar(c + 1, next);
577 if (c[1] == '(') {
578 nextchar(c + 2, next);
579 nextchar(c + strlen(c), next);
582 } else if (c[1] == 'w') {
583 n = ren_wid(next, back);
584 sprintf(widbuf, "%d", n);
585 in_push(widbuf, NULL);
586 return 0;
587 } else if (strchr(" bDdfhkLlorsuvXxz0^|{}&", c[1])) {
588 escarg_ren(arg, c[1], next, back);
589 ren_cmd(adj, c[1], arg);
590 return 0;
593 if (ren_s != n_s) {
594 adj_swid(adj, charwid(dev_spacewid(), n_s));
595 adj_put(adj, 0, "\\s(%02d", n_s);
596 ren_s = n_s;
598 if (ren_f != n_f) {
599 adj_put(adj, 0, "\\f(%02d", n_f);
600 ren_f = n_f;
602 g = dev_glyph(c, n_f);
603 w = charwid(g ? g->wid : SC_DW, n_s);
604 adj_put(adj, w, c);
605 if (zerowid)
606 adj_put(adj, -w, "\\h'%du'", -w);
607 return g ? g->type : 0;
610 /* read the argument of \w and push its width */
611 int ren_wid(int (*next)(void), void (*back)(int))
613 struct adj *adj = adj_alloc();
614 int c, wid_c, n;
615 int type = 0;
616 wid_c = next();
617 c = next();
618 adj_ll(adj, n_l);
619 odiv_beg();
620 while (c >= 0 && c != wid_c) {
621 back(c);
622 type |= ren_char(adj, next, back);
623 c = next();
625 odiv_end();
626 ren_f = -1;
627 ren_s = -1;
628 n = adj_wid(adj);
629 n_ct = type;
630 adj_free(adj);
631 return n;
634 /* read characters from in.c and pass rendered lines to out.c */
635 void render(void)
637 int c;
638 n_nl = -1;
639 tr_first();
640 ren_first(); /* transition to the first page */
641 c = ren_next();
642 while (c >= 0) {
643 if (c == ' ' || c == '\n') {
644 ren_back(c);
645 ren_char(cadj, ren_next, ren_back);
647 while (adj_full(cadj, !n_ce && n_u))
648 ren_br(0);
649 if (c == '\n') { /* end of input line */
650 n_lb = adj_wid(cadj);
651 n_ce = MAX(0, n_ce - 1);
653 if (c != ' ' && c != '\n') {
654 ren_back(c);
655 ren_char(cadj, ren_next, ren_back);
657 c = ren_next();
659 ren_br(1);
662 /* trap handling */
664 static int tpos[NTRAPS]; /* trap positions */
665 static int treg[NTRAPS]; /* trap registers */
666 static int ntraps;
668 static int trap_first(int pos)
670 int best = -1;
671 int i;
672 for (i = 0; i < ntraps; i++)
673 if (treg[i] >= 0 && tpos[i] > pos)
674 if (best < 0 || tpos[i] < tpos[best])
675 best = i;
676 return best;
679 static int trap_byreg(int reg)
681 int i;
682 for (i = 0; i < ntraps; i++)
683 if (treg[i] == reg)
684 return i;
685 return -1;
688 static int trap_bypos(int reg, int pos)
690 int i;
691 for (i = 0; i < ntraps; i++)
692 if (treg[i] >= 0 && tpos[i] == pos)
693 if (reg == -1 || treg[i] == reg)
694 return i;
695 return -1;
698 static int tpos_parse(char *s)
700 int pos = eval(s, 'v');
701 return pos >= 0 ? pos : n_p + pos;
704 void tr_wh(char **args)
706 int reg, pos, id;
707 if (!args[1])
708 return;
709 pos = tpos_parse(args[1]);
710 id = trap_bypos(-1, pos);
711 if (!args[2]) {
712 if (id >= 0)
713 treg[id] = -1;
714 return;
716 reg = REG(args[2][0], args[2][1]);
717 if (id < 0)
718 id = trap_byreg(-1);
719 if (id < 0)
720 id = ntraps++;
721 tpos[id] = pos;
722 treg[id] = reg;
725 void tr_ch(char **args)
727 int reg;
728 int id;
729 if (!args[1])
730 return;
731 reg = REG(args[1][0], args[1][1]);
732 id = trap_byreg(reg);
733 if (id)
734 tpos[id] = args[2] ? tpos_parse(args[2]) : -1;
737 void tr_dt(char **args)
739 if (!cdiv)
740 return;
741 if (args[2]) {
742 cdiv->tpos = eval(args[1], 'v');
743 cdiv->treg = REG(args[2][0], args[2][1]);
744 } else {
745 cdiv->treg = -1;
749 static int trap_pos(int pos)
751 int ret = trap_first(pos);
752 if (cdiv)
753 return cdiv->treg && cdiv->tpos > pos ? cdiv->tpos : -1;
754 return ret >= 0 ? tpos[ret] : -1;
757 static int trap_reg(int pos)
759 int ret = trap_first(pos);
760 if (cdiv)
761 return cdiv->treg && cdiv->tpos > pos ? cdiv->treg : -1;
762 return ret >= 0 ? treg[ret] : -1;
765 int f_nexttrap(void)
767 int pos = trap_pos(n_d);
768 if (cdiv)
769 return pos >= 0 ? pos : 0x7fffffff;
770 return pos >= 0 && pos < n_p ? pos : n_p - n_d;