ref: 8626cedf2864bf52786f0df30aad70e10a24ef91
dir: /ren.c/
#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "xroff.h" #define cadj env_adj() /* line buffer */ /* diversions */ struct div { struct sbuf sbuf; /* diversion output */ int reg; /* diversion register */ int tpos; /* diversion trap position */ int treg; /* diversion trap register */ int dl; /* diversion width */ int prev_d; /* previous \(.d value */ int prev_h; /* previous \(.h value */ int prev_mk; /* previous .mk internal register */ int prev_ns; /* previous .ns value */ }; static struct div divs[NPREV]; /* diversion stack */ static struct div *cdiv; /* current diversion */ static int ren_f = -1; /* last rendered n_f */ static int ren_s = -1; /* last rendered n_s */ static int ren_div; /* rendering a diversion */ static int ren_backed = -1; /* pushed back character */ static int bp_first = 1; /* prior to the first page */ static int bp_next = 1; /* next page number */ static int bp_force; /* execute the traps until the next page */ static int ren_next(void) { int c = ren_backed >= 0 ? ren_backed : tr_next(); ren_backed = -1; return c; } static void ren_back(int c) { ren_backed = c; } void tr_di(char **args) { if (args[1]) { cdiv = cdiv ? cdiv + 1 : divs; memset(cdiv, 0, sizeof(*cdiv)); sbuf_init(&cdiv->sbuf); cdiv->reg = REG(args[1][0], args[1][1]); cdiv->treg = -1; if (args[0][2] == 'a' && str_get(cdiv->reg)) /* .da */ sbuf_append(&cdiv->sbuf, str_get(cdiv->reg)); sbuf_append(&cdiv->sbuf, DIV_BEG "\n"); ren_f = -1; ren_s = -1; cdiv->prev_d = n_d; cdiv->prev_h = n_h; cdiv->prev_mk = n_mk; cdiv->prev_ns = n_ns; n_d = 0; n_h = 0; n_mk = 0; n_ns = 0; } else if (cdiv) { sbuf_putnl(&cdiv->sbuf); sbuf_append(&cdiv->sbuf, DIV_END "\n"); str_set(cdiv->reg, sbuf_buf(&cdiv->sbuf)); sbuf_done(&cdiv->sbuf); n_dl = cdiv->dl; n_dn = n_d; n_d = cdiv->prev_d; n_h = cdiv->prev_h; n_mk = cdiv->prev_mk; n_ns = cdiv->prev_ns; cdiv = cdiv > divs ? cdiv - 1 : NULL; ren_f = -1; ren_s = -1; } } int f_divreg(void) { return cdiv ? cdiv->reg : -1; } int f_hpos(void) { return adj_wid(cadj); } void tr_divbeg(char **args) { odiv_beg(); ren_div++; } void tr_divend(char **args) { odiv_end(); ren_div--; } static int trap_reg(int pos); static int trap_pos(int pos); static void trap_exec(int reg); static void ren_page(int pg) { n_nl = 0; n_d = 0; n_h = 0; n_pg = pg; bp_next = n_pg + 1; out("p%d\n", pg); out("V%d\n", 0); if (trap_pos(-1) == 0) trap_exec(trap_reg(-1)); } static void ren_first(void) { if (bp_first) { bp_first = 0; ren_page(bp_next); } } static void ren_sp(int n) { char cmd[32]; ren_first(); if (!n && ren_div && !n_u) return; n_d += n ? n : n_v; if (n_d > n_h) n_h = n_d; if (cdiv) { sbuf_putnl(&cdiv->sbuf); sprintf(cmd, "'sp %du\n", n ? n : n_v); sbuf_append(&cdiv->sbuf, cmd); } else { n_nl = n_d; } } static void push_ne(void) { char buf[32]; sprintf(buf, ".ne %du\n", n_p); in_pushnl(buf, NULL); } static void trap_exec(int reg) { if (bp_force) push_ne(); if (str_get(reg)) in_pushnl(str_get(reg), NULL); } /* return 1 if executed a trap */ static int ren_traps(int beg, int end, int dosp) { int pos = trap_pos(beg); if (pos >= 0 && (cdiv || pos < n_p) && pos <= end) { if (dosp && pos > beg) ren_sp(pos - beg); trap_exec(trap_reg(beg)); return 1; } return 0; } /* start a new page if needed */ static int ren_pagelimit(int ne) { if (n_nl + ne >= n_p && !cdiv) { bp_force = 0; ren_page(bp_next); return 1; } return 0; } static void down(int n) { if (!ren_traps(n_d, n_d + (n ? n : n_v), 1)) { ren_sp(n); ren_pagelimit(0); } } /* flush the given line and send it to out.c */ static void ren_line(char *s, int w, int ll, int li, int lt) { int ljust = 0; char cmd[32]; int llen = ll - li - lt; n_n = w; if (n_u && !n_na && (n_j == AD_C || n_j == AD_R)) ljust = n_j == AD_C ? (llen - w) / 2 : llen - w; if (cdiv) { if (cdiv->dl < w) cdiv->dl = w; if (ljust + lt) { sprintf(cmd, "\\h'%du'", lt + ljust); sbuf_append(&cdiv->sbuf, cmd); } sbuf_append(&cdiv->sbuf, s); } else { out("H%d\n", n_o + li + lt + ljust); out("V%d\n", n_d); out_line(s); } } /* return 1 if triggered a trap */ static int ren_br(int force) { char buf[LNLEN]; int ll, li, lt, els_neg, els_pos; int adj_b, w, prev_d; ren_first(); if (!adj_empty(cadj, n_u)) { adj_b = n_u && !n_na && n_j == AD_B; w = adj_fill(cadj, !force && adj_b, !force && n_u, buf, &ll, &li, <, &els_neg, &els_pos); prev_d = n_d; if (els_neg) ren_sp(-els_neg); if (!n_ns || w || els_neg || els_pos) { ren_sp(0); ren_line(buf, w, ll, li, lt); n_ns = 0; } if (els_pos) ren_sp(els_pos); n_a = els_pos; if (!ren_traps(prev_d, n_d, 0)) { if (n_L > n_v && (cdiv || n_d < n_p)) { down(n_L - n_v); } else { if (ren_pagelimit(0)) return 1; } return 0; } return 1; } return 0; } void tr_br(char **args) { if (args[0][0] == '.') ren_br(1); } void tr_sp(char **args) { int traps = 0; if (args[0][0] == '.') traps = ren_br(1); if (!n_ns && !traps) down(args[1] ? eval(args[1], 'v') : n_v); } void tr_sv(char **args) { int n = eval(args[1], 'v'); n_sv = 0; if (n_d + n < f_nexttrap()) down(n); else n_sv = n; } void tr_ns(char **args) { n_ns = 1; } void tr_rs(char **args) { n_ns = 0; } void tr_os(char **args) { if (n_sv) down(n_sv); n_sv = 0; } void tr_mk(char **args) { if (args[1]) num_set(REG(args[1][0], args[1][1]), n_d); else n_mk = n_d; } void tr_rt(char **args) { int n = args[1] ? eval_re(args[1], n_d, 'v') : n_mk; if (n >= 0 && n < n_d) ren_sp(n - n_d); } void tr_ne(char **args) { int n = args[1] ? eval(args[1], 'v') : n_v; if (!ren_traps(n_d, n_d + n - 1, 1)) ren_pagelimit(n); } void tr_bp(char **args) { if (!cdiv && (args[1] || !n_ns)) { if (!bp_force) push_ne(); if (args[0][0] == '.') in_pushnl(".br\n", NULL); bp_force = 1; if (args[1]) bp_next = eval_re(args[1], n_pg, 0); } } void tr_pn(char **args) { if (args[1]) bp_next = eval_re(args[1], n_pg, 0); } static void ren_ps(char *s) { int ps = !s || !*s || !strcmp("0", s) ? n_s0 : eval_re(s, n_s, 0); n_s0 = n_s; n_s = MAX(1, ps); } void tr_ps(char **args) { ren_ps(args[1]); } void tr_ll(char **args) { int ll = args[1] ? eval_re(args[1], n_l, 'm') : n_l0; n_l0 = n_l; n_l = MAX(0, ll); adj_ll(cadj, n_l); } void tr_in(char **args) { int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0; if (args[0][0] == '.') ren_br(1); n_i0 = n_i; n_i = MAX(0, in); adj_in(cadj, n_i); } void tr_ti(char **args) { if (args[0][0] == '.') ren_br(1); if (args[1]) adj_ti(cadj, eval(args[1], 'm')); } static void ren_ft(char *s) { int fn = !*s || !strcmp("P", s) ? n_f0 : dev_font(s); if (fn >= 0) { n_f0 = n_f; n_f = fn; } } void tr_ft(char **args) { if (args[1]) ren_ft(args[1]); } void tr_fp(char **args) { if (!args[2]) return; if (dev_mnt(atoi(args[1]), args[2], args[3] ? args[3] : args[2]) < 0) errmsg("troff: failed to mount %s\n", args[2]); } void tr_nf(char **args) { if (args[0][0] == '.') ren_br(1); n_u = 0; } void tr_fi(char **args) { if (args[0][0] == '.') ren_br(1); n_u = 1; } static void escarg_ren(char *d, int cmd, int (*next)(void), void (*back)(int)) { int c, q; if (strchr(ESC_P, cmd)) { c = next(); if (cmd == 's' && (c == '-' || c == '+')) { *d++ = c; c = next(); } if (c == '(') { *d++ = next(); *d++ = next(); } else { *d++ = c; if (cmd == 's' && c >= '1' && c <= '3') { c = next(); if (isdigit(c)) *d++ = c; else back(c); } } } if (strchr(ESC_Q, cmd)) { q = next(); while (1) { c = next(); if (c == q || c < 0) break; *d++ = c; } } if (cmd == 'z') *d++ = next(); *d = '\0'; } static int nextchar(char *s, int (*next)(void)) { int c = next(); int l = utf8len(c); int i; if (c < 0) return 0; s[0] = c; for (i = 1; i < l; i++) s[i] = next(); s[l] = '\0'; return l; } static void ren_cmd(struct adj *adj, int c, char *arg) { char draw_arg[ILNLEN]; struct glyph *g; int n, w; switch (c) { case ' ': w = dev_spacewid(); adj_put(adj, w, "\\h'%du'", w); break; case 'b': ren_bracket(adj, arg); break; case 'D': w = out_draw(arg, draw_arg); adj_put(adj, w, "\\D'%s'", draw_arg); break; case 'd': adj_put(adj, 0, "\\v'%du'", eval(".5m", 0)); break; case 'f': ren_ft(arg); break; case 'h': n = eval(arg, 'm'); adj_put(adj, n, "\\h'%du'", n); break; case 'k': num_set(REG(arg[0], arg[1]), f_hpos() - n_lb); break; case 'L': ren_vline(adj, arg); break; case 'l': ren_hline(adj, arg); break; case 'o': ren_over(adj, arg); break; case 'r': adj_put(adj, 0, "\\v'%du'", eval("-1m", 0)); break; case 's': ren_ps(arg); break; case 'u': adj_put(adj, 0, "\\v'%du'", eval("-.5m", 0)); break; case 'v': adj_put(adj, 0, "\\v'%du'", eval(arg, 'v')); break; case 'X': adj_put(adj, 0, "\\X'%s'", arg); break; case 'x': adj_els(adj, eval(arg, 'v')); break; case '0': g = dev_glyph("0", n_f); w = charwid(g ? g->wid : SC_DW, n_s); adj_put(adj, w, "\\h'%du'", w); break; case '|': w = eval("1m/6", 0); adj_put(adj, w, "\\h'%du'", w); break; case '^': w = eval("1m/12", 0); adj_put(adj, w, "\\h'%du'", w); break; case '{': case '}': break; } } /* read one character and place it inside adj buffer */ static int ren_char(struct adj *adj, int (*next)(void), void (*back)(int)) { char c[GNLEN * 4]; char arg[ILNLEN]; char widbuf[16]; struct glyph *g; int zerowid = 0; int n, w; nextchar(c, next); if (c[0] == ' ' || c[0] == '\n') { adj_put(adj, charwid(dev_spacewid(), n_s), c); return 0; } if (c[0] == '\\') { nextchar(c + 1, next); if (c[1] == '(') { int l = nextchar(c + 2, next); l += nextchar(c + 2 + l, next); c[2 + l] = '\0'; } else if (c[1] == 'z') { zerowid = 1; nextchar(c, next); if (c[0] == '\\') { nextchar(c + 1, next); if (c[1] == '(') { nextchar(c + 2, next); nextchar(c + strlen(c), next); } } } else if (c[1] == 'w') { n = ren_wid(next, back); sprintf(widbuf, "%d", n); in_push(widbuf, NULL); return 0; } else if (strchr(" bDdfhkLlorsuvXxz0^|{}&", c[1])) { escarg_ren(arg, c[1], next, back); ren_cmd(adj, c[1], arg); return 0; } } if (ren_s != n_s) { adj_swid(adj, charwid(dev_spacewid(), n_s)); adj_put(adj, 0, "\\s(%02d", n_s); ren_s = n_s; } if (ren_f != n_f) { adj_put(adj, 0, "\\f(%02d", n_f); ren_f = n_f; } g = dev_glyph(c, n_f); w = charwid(g ? g->wid : SC_DW, n_s); adj_put(adj, w, c); if (zerowid) adj_put(adj, -w, "\\h'%du'", -w); return g ? g->type : 0; } /* read the argument of \w and push its width */ int ren_wid(int (*next)(void), void (*back)(int)) { struct adj *adj = adj_alloc(); int c, wid_c, n; int type = 0; wid_c = next(); c = next(); adj_ll(adj, n_l); odiv_beg(); while (c >= 0 && c != wid_c) { back(c); type |= ren_char(adj, next, back); c = next(); } odiv_end(); ren_f = -1; ren_s = -1; n = adj_wid(adj); n_ct = type; adj_free(adj); return n; } /* read characters from in.c and pass rendered lines to out.c */ void render(void) { int c; n_nl = -1; tr_first(); ren_first(); /* transition to the first page */ c = ren_next(); while (c >= 0) { if (c == ' ' || c == '\n') { ren_back(c); ren_char(cadj, ren_next, ren_back); } while (adj_full(cadj, n_u)) ren_br(0); if (c == '\n') n_lb = adj_wid(cadj); if (c != ' ' && c != '\n') { ren_back(c); ren_char(cadj, ren_next, ren_back); } c = ren_next(); } ren_br(1); } /* trap handling */ static int tpos[NTRAPS]; /* trap positions */ static int treg[NTRAPS]; /* trap registers */ static int ntraps; static int trap_first(int pos) { int best = -1; int i; for (i = 0; i < ntraps; i++) if (treg[i] >= 0 && tpos[i] > pos) if (best < 0 || tpos[i] < tpos[best]) best = i; return best; } static int trap_byreg(int reg) { int i; for (i = 0; i < ntraps; i++) if (treg[i] == reg) return i; return -1; } static int trap_bypos(int reg, int pos) { int i; for (i = 0; i < ntraps; i++) if (treg[i] >= 0 && tpos[i] == pos) if (reg == -1 || treg[i] == reg) return i; return -1; } static int tpos_parse(char *s) { int pos = eval(s, 'v'); return pos >= 0 ? pos : n_p + pos; } void tr_wh(char **args) { int reg, pos, id; if (!args[1]) return; pos = tpos_parse(args[1]); id = trap_bypos(-1, pos); if (!args[2]) { if (id >= 0) treg[id] = -1; return; } reg = REG(args[2][0], args[2][1]); if (id < 0) id = trap_byreg(-1); if (id < 0) id = ntraps++; tpos[id] = pos; treg[id] = reg; } void tr_ch(char **args) { int reg; int id; if (!args[1]) return; reg = REG(args[1][0], args[1][1]); id = trap_byreg(reg); if (id) tpos[id] = args[2] ? tpos_parse(args[2]) : -1; } void tr_dt(char **args) { if (!cdiv) return; if (args[2]) { cdiv->tpos = eval(args[1], 'v'); cdiv->treg = REG(args[2][0], args[2][1]); } else { cdiv->treg = -1; } } static int trap_pos(int pos) { int ret = trap_first(pos); if (cdiv) return cdiv->treg && cdiv->tpos > pos ? cdiv->tpos : -1; return ret >= 0 ? tpos[ret] : -1; } static int trap_reg(int pos) { int ret = trap_first(pos); if (cdiv) return cdiv->treg && cdiv->tpos > pos ? cdiv->treg : -1; return ret >= 0 ? treg[ret] : -1; } int f_nexttrap(void) { int pos = trap_pos(n_d); if (cdiv) return pos >= 0 ? pos : 0x7fffffff; return pos >= 0 && pos < n_p ? pos : n_p - n_d; }