ref: c55cd099d93cd4a34a48f61bfffdba6866ea263a
parent: a964826bb75b0776bf34898855aa8193b84c4e7c
author: Ali Gholami Rudi <ali@rudi.ir>
date: Thu Apr 24 18:51:10 EDT 2014
fmt: collect words in fmt buffer for whole paragraphs This patch prepares neatroff for filling whole paragraphs at once.
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@
all: roff
%.o: %.c roff.h
$(CC) -c $(CFLAGS) $<
-roff: roff.o dev.o font.o in.o cp.o tr.o ren.o out.o reg.o sbuf.o adj.o eval.o draw.o wb.o hyph.o map.o clr.o char.o
+roff: roff.o dev.o font.o in.o cp.o tr.o ren.o out.o reg.o sbuf.o fmt.o eval.o draw.o wb.o hyph.o map.o clr.o char.o
$(CC) -o $@ $^ $(LDFLAGS)
clean:
rm -f *.o roff
--- a/adj.c
+++ /dev/null
@@ -1,243 +1,0 @@
-/* adjustment buffer for putting words into lines */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "roff.h"
-
-#define ADJ_LLEN(a) MAX(0, (a)->ll - (a)->li)
-
-struct adj {
- char *words[NWORDS]; /* words to be adjusted */
- int wids[NWORDS]; /* the width of words */
- int elsn[NWORDS]; /* els_neg of words */
- int elsp[NWORDS]; /* els_pos of words */
- int gaps[NWORDS]; /* gaps before words */
- int nwords;
- int wid; /* total width of buf */
- int swid; /* current space width */
- int gap; /* space before the next word */
- int nls; /* newlines before the next word */
- int li, ll; /* current line indentation and length */
- int filled; /* filled all words in the last adj_fill() */
- int eos; /* last word ends a sentence */
-};
-
-/* .ll, .in and .ti are delayed until the partial line is output */
-static void adj_confupdate(struct adj *adj)
-{
- adj->ll = n_l;
- adj->li = n_ti > 0 ? n_ti : n_i;
- n_ti = 0;
-}
-
-/* does the adjustment buffer need to be flushed without filling? */
-static int adj_fullnf(struct adj *a)
-{
- /* blank lines; indented lines; newlines when buffer is empty */
- return a->nls > 1 || (a->nls && a->gap) ||
- (a->nls - a->filled > 0 && !a->nwords);
-}
-
-/* does the adjustment buffer need to be flushed? */
-int adj_full(struct adj *a, int fill)
-{
- if (!fill)
- return a->nls - a->filled > 0;
- if (adj_fullnf(a))
- return 1;
- return a->nwords && a->wid > ADJ_LLEN(a);
-}
-
-/* is the adjustment buffer empty? */
-int adj_empty(struct adj *a, int fill)
-{
- return !fill ? a->nls - a->filled <= 0 : !a->nwords && !adj_fullnf(a);
-}
-
-/* set space width */
-void adj_swid(struct adj *adj, int swid)
-{
- adj->swid = swid;
-}
-
-/* move words inside an adj struct */
-static void adj_movewords(struct adj *a, int dst, int src, int len)
-{
- memmove(a->words + dst, a->words + src, len * sizeof(a->words[0]));
- memmove(a->wids + dst, a->wids + src, len * sizeof(a->wids[0]));
- memmove(a->elsn + dst, a->elsn + src, len * sizeof(a->elsn[0]));
- memmove(a->elsp + dst, a->elsp + src, len * sizeof(a->elsp[0]));
- memmove(a->gaps + dst, a->gaps + src, len * sizeof(a->gaps[0]));
-}
-
-static char *adj_strdup(char *s)
-{
- int l = strlen(s);
- char *r = malloc(l + 1);
- memcpy(r, s, l + 1);
- return r;
-}
-
-/* copy word buffer wb in adj->words[i] */
-static void adj_word(struct adj *adj, int i, struct wb *wb, int gap)
-{
- adj->words[i] = adj_strdup(wb_buf(wb));
- adj->wids[i] = wb_wid(wb);
- adj->elsn[i] = wb->els_neg;
- adj->elsp[i] = wb->els_pos;
- adj->gaps[i] = gap;
-}
-
-static int adj_linewid(struct adj *a, int n)
-{
- int i, w = 0;
- for (i = 0; i < n; i++)
- w += a->wids[i] + a->gaps[i];
- return w;
-}
-
-static int adj_linefit(struct adj *a, int llen)
-{
- int i, w = 0;
- for (i = 0; i < a->nwords; i++) {
- w += a->wids[i] + a->gaps[i];
- if (w > llen)
- return i;
- }
- return i;
-}
-
-/* move n words from the adjustment buffer to s */
-static int adj_move(struct adj *a, int n, struct sbuf *s, int *els_neg, int *els_pos)
-{
- int w = 0;
- int i;
- *els_neg = 0;
- *els_pos = 0;
- for (i = 0; i < n; i++) {
- sbuf_printf(s, "%ch'%du'", c_ec, a->gaps[i]);
- sbuf_append(s, a->words[i]);
- w += a->wids[i] + a->gaps[i];
- if (a->elsn[i] < *els_neg)
- *els_neg = a->elsn[i];
- if (a->elsp[i] > *els_pos)
- *els_pos = a->elsp[i];
- free(a->words[i]);
- }
- if (!n)
- return 0;
- a->nwords -= n;
- adj_movewords(a, 0, n, a->nwords);
- a->wid = adj_linewid(a, a->nwords);
- if (a->nwords) /* apply the new .l and .i */
- adj_confupdate(a);
- return w;
-}
-
-/* try to hyphenate the n-th word */
-static void adj_hyph(struct adj *a, int n, int w, int hyph)
-{
- struct wb w1, w2;
- int flg = hyph | (n ? 0 : HY_ANY);
- wb_init(&w1);
- wb_init(&w2);
- if (!wb_hyph(a->words[n], w, &w1, &w2, flg)) {
- adj_movewords(a, n + 2, n + 1, a->nwords - n);
- free(a->words[n]);
- adj_word(a, n, &w1, a->gaps[n]);
- adj_word(a, n + 1, &w2, 0);
- a->nwords++;
- a->wid = adj_linewid(a, a->nwords);
- }
- wb_done(&w1);
- wb_done(&w2);
-}
-
-/* fill and copy a line into s */
-int adj_fill(struct adj *a, int ad_b, int fill, int hyph, struct sbuf *s,
- int *li, int *ll, int *els_neg, int *els_pos)
-{
- int adj_div, adj_rem;
- int w = 0;
- int i, n;
- int llen = ADJ_LLEN(a);
- *ll = a->ll;
- *li = a->li;
- if (!fill || adj_fullnf(a)) {
- a->filled = 0;
- a->nls--;
- return adj_move(a, a->nwords, s, els_neg, els_pos);
- }
- n = adj_linefit(a, llen);
- if (n < a->nwords)
- adj_hyph(a, n, llen - adj_linewid(a, n) - a->gaps[n], hyph);
- n = adj_linefit(a, llen);
- if (!n && a->nwords)
- n = 1;
- w = adj_linewid(a, n);
- if (ad_b && n > 1) {
- adj_div = (llen - w) / (n - 1);
- adj_rem = (llen - w) % (n - 1);
- for (i = 0; i < n - 1; i++)
- a->gaps[i + 1] += adj_div + (i < adj_rem);
- }
- w = adj_move(a, n, s, els_neg, els_pos);
- if (a->nwords)
- a->wid -= a->gaps[0];
- a->gaps[0] = 0;
- a->filled = n && !a->nwords;
- return w;
-}
-
-void adj_sp(struct adj *adj)
-{
- adj->gap += adj->swid;
-}
-
-void adj_nl(struct adj *adj)
-{
- adj->nls++;
- adj->gap = 0;
-}
-
-/* ignore the previous newline */
-void adj_nonl(struct adj *adj)
-{
- if (adj->nls)
- adj->gap += adj->swid;
- adj->nls = 0;
-}
-
-/* insert wb into the adjustment buffer */
-void adj_wb(struct adj *adj, struct wb *wb)
-{
- if (wb_empty(wb) || adj->nwords == NWORDS)
- return;
- if (!adj->nwords) /* apply the new .l and .i */
- adj_confupdate(adj);
- if (adj->nls && !adj->gap && adj->nwords >= 1)
- adj->gap = (adj->nwords && adj->eos) ? adj->swid * 2 : adj->swid;
- adj_word(adj, adj->nwords++, wb, adj->filled ? 0 : adj->gap);
- adj->filled = 0;
- adj->wid += adj->wids[adj->nwords - 1] + adj->gaps[adj->nwords - 1];
- adj->nls = 0;
- adj->gap = 0;
- adj->eos = wb_eos(wb);
-}
-
-struct adj *adj_alloc(void)
-{
- struct adj *adj = malloc(sizeof(*adj));
- memset(adj, 0, sizeof(*adj));
- return adj;
-}
-
-void adj_free(struct adj *adj)
-{
- free(adj);
-}
-
-int adj_wid(struct adj *adj)
-{
- return adj->wid + (adj->nls ? adj->swid : adj->gap);
-}
--- /dev/null
+++ b/fmt.c
@@ -1,0 +1,321 @@
+/*
+ * line formatting buffer for line adjustment and hyphenation
+ *
+ * The line formatting buffer does two main functions: breaking
+ * words into lines (possibly after hyphenating some of them), and, if
+ * requested, adjusting the space between words in a line. In this
+ * file the first step is referred to as filling.
+ *
+ * Inputs are specified via these functions:
+ * + fmt_word(): for appending space-separated words.
+ * + fmt_space(): for appending spaces.
+ * + fmt_newline(): for appending new lines.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "roff.h"
+
+#define FMT_LLEN(f) MAX(0, (f)->ll - (f)->li)
+#define FMT_FILL(f) (!n_ce && n_u)
+#define FMT_ADJ(f) (n_u && !n_na && !n_ce && (n_j & AD_B) == AD_B)
+#define FMT_SWID(f) (spacewid(n_f, n_s))
+
+struct word {
+ char *s;
+ int wid;
+ int elsn, elsp;
+ int gap;
+};
+
+struct line {
+ struct sbuf sbuf;
+ int wid, li, ll;
+ int elsn, elsp;
+};
+
+struct fmt {
+ /* queued words */
+ struct word words[NWORDS];
+ int nwords;
+ /* queued lines */
+ struct line lines[NLINES];
+ int l_head, l_tail;
+ /* current line */
+ int gap; /* space before the next word */
+ int nls; /* newlines before the next word */
+ int li, ll; /* current line indentation and length */
+ int filled; /* filled all words in the last fmt_fill() */
+ int eos; /* last word ends a sentence */
+};
+
+/* .ll, .in and .ti are delayed until the partial line is output */
+static void fmt_confupdate(struct fmt *f)
+{
+ f->ll = n_l;
+ f->li = n_ti >= 0 ? n_ti : n_i;
+ n_ti = -1;
+}
+
+/* move words inside an fmt struct */
+static void fmt_movewords(struct fmt *a, int dst, int src, int len)
+{
+ memmove(a->words + dst, a->words + src, len * sizeof(a->words[0]));
+}
+
+static char *fmt_strdup(char *s)
+{
+ int l = strlen(s);
+ char *r = malloc(l + 1);
+ memcpy(r, s, l + 1);
+ return r;
+}
+
+/* copy word buffer wb in fmt->words[i] */
+static void fmt_insertword(struct fmt *f, int i, struct wb *wb, int gap)
+{
+ struct word *w = &f->words[i];
+ w->s = fmt_strdup(wb_buf(wb));
+ w->wid = wb_wid(wb);
+ w->elsn = wb->els_neg;
+ w->elsp = wb->els_pos;
+ w->gap = gap;
+}
+
+/* the total width of the first n words in f->words[] */
+static int fmt_wordslen(struct fmt *f, int n)
+{
+ int i, w = 0;
+ for (i = 0; i < n; i++)
+ w += f->words[i].wid + f->words[i].gap;
+ return w;
+}
+
+/* select as many words as can be fit in llen */
+static int fmt_linefit(struct fmt *f, int llen)
+{
+ int i, w = 0;
+ for (i = 0; i < f->nwords; i++) {
+ w += f->words[i].wid + f->words[i].gap;
+ if (w > llen)
+ return i;
+ }
+ return i;
+}
+
+/* move n words from the buffer to s */
+static int fmt_move(struct fmt *f, int n, struct sbuf *s, int *els_neg, int *els_pos)
+{
+ struct word *wcur;
+ int w = 0;
+ int i;
+ *els_neg = 0;
+ *els_pos = 0;
+ for (i = 0; i < n; i++) {
+ wcur = &f->words[i];
+ sbuf_printf(s, "%ch'%du'", c_ec, wcur->gap);
+ sbuf_append(s, wcur->s);
+ w += wcur->wid + wcur->gap;
+ if (wcur->elsn < *els_neg)
+ *els_neg = wcur->elsn;
+ if (wcur->elsp > *els_pos)
+ *els_pos = wcur->elsp;
+ free(wcur->s);
+ }
+ if (!n)
+ return 0;
+ f->nwords -= n;
+ fmt_movewords(f, 0, n, f->nwords);
+ if (f->nwords) /* apply the new .l and .i */
+ fmt_confupdate(f);
+ return w;
+}
+
+/* try to hyphenate the n-th word */
+static void fmt_hyph(struct fmt *f, int n, int w, int hyph)
+{
+ struct wb w1, w2;
+ int flg = hyph | (n ? 0 : HY_ANY);
+ wb_init(&w1);
+ wb_init(&w2);
+ if (!wb_hyph(f->words[n].s, w, &w1, &w2, flg)) {
+ fmt_movewords(f, n + 2, n + 1, f->nwords - n);
+ free(f->words[n].s);
+ fmt_insertword(f, n, &w1, f->words[n].gap);
+ fmt_insertword(f, n + 1, &w2, 0);
+ f->nwords++;
+ }
+ wb_done(&w1);
+ wb_done(&w2);
+}
+
+/* estimated number of lines until traps or the end of a page */
+static int ren_safelines(void)
+{
+ return f_nexttrap() / (MAX(1, n_L) * n_v);
+}
+
+static int fmt_nlines(struct fmt *f)
+{
+ if (f->l_tail <= f->l_head)
+ return f->l_head - f->l_tail;
+ return NLINES - f->l_tail + f->l_head;
+}
+
+int fmt_fill(struct fmt *f, int all)
+{
+ int llen, fmt_div, fmt_rem;
+ int w = 0;
+ int i, n;
+ struct line *l;
+ int hyph = n_hy;
+ if (!FMT_FILL(f))
+ return 0;
+ while (f->nwords) {
+ l = &f->lines[f->l_head];
+ llen = FMT_LLEN(f);
+ if ((f->l_head + 1) % NLINES == f->l_tail)
+ return 1;
+ l->li = f->li;
+ l->ll = f->ll;
+ n = fmt_linefit(f, llen);
+ if (n == f->nwords && !all)
+ break;
+ if ((n_hy & HY_LAST) && ren_safelines() < 2 + fmt_nlines(f))
+ hyph = 0; /* disable hyphenation for final lines */
+ if (n < f->nwords)
+ fmt_hyph(f, n, llen - fmt_wordslen(f, n) -
+ f->words[n].gap, hyph);
+ n = fmt_linefit(f, llen);
+ if (!n && f->nwords)
+ n = 1;
+ w = fmt_wordslen(f, n);
+ if (FMT_ADJ(f) && n > 1) {
+ fmt_div = (llen - w) / (n - 1);
+ fmt_rem = (llen - w) % (n - 1);
+ for (i = 0; i < n - 1; i++)
+ f->words[i + 1].gap += fmt_div + (i < fmt_rem);
+ }
+ sbuf_init(&l->sbuf);
+ l->wid = fmt_move(f, n, &l->sbuf, &l->elsn, &l->elsp);
+ f->words[0].gap = 0;
+ f->filled = n && !f->nwords;
+ f->l_head = (f->l_head + 1) % NLINES;
+ }
+ return 0;
+}
+
+/* return the next line in the buffer */
+int fmt_nextline(struct fmt *f, struct sbuf *sbuf, int *w,
+ int *li, int *ll, int *els_neg, int *els_pos)
+{
+ struct line *l;
+ l = &f->lines[f->l_tail];
+ if (f->l_head == f->l_tail)
+ return 1;
+ *li = l->li;
+ *ll = l->ll;
+ *w = l->wid;
+ *els_neg = l->elsn;
+ *els_pos = l->elsp;
+ sbuf_append(sbuf, sbuf_buf(&l->sbuf));
+ sbuf_done(&l->sbuf);
+ f->l_tail = (f->l_tail + 1) % NLINES;
+ return 0;
+}
+
+static int fmt_sp(struct fmt *f)
+{
+ struct line *l;
+ fmt_fill(f, 0);
+ if ((f->l_head + 1) % NLINES == f->l_tail)
+ return 1;
+ l = &f->lines[f->l_head];
+ f->filled = 0;
+ f->nls--;
+ l->li = f->li;
+ l->ll = f->ll;
+ sbuf_init(&l->sbuf);
+ l->wid = fmt_move(f, f->nwords, &l->sbuf, &l->elsn, &l->elsp);
+ f->l_head = (f->l_head + 1) % NLINES;
+ return 0;
+}
+
+void fmt_br(struct fmt *f)
+{
+ fmt_fill(f, 0);
+ f->filled = 0;
+ if (f->nwords)
+ fmt_sp(f);
+}
+
+void fmt_space(struct fmt *fmt)
+{
+ fmt->gap += FMT_SWID(fmt);
+}
+
+void fmt_newline(struct fmt *f)
+{
+ f->nls++;
+ f->gap = 0;
+ if (!FMT_FILL(f)) {
+ fmt_sp(f);
+ return;
+ }
+ if (f->nls == 1 && !f->filled && !f->nwords)
+ fmt_sp(f);
+ if (f->nls > 1) {
+ if (!f->filled)
+ fmt_sp(f);
+ fmt_sp(f);
+ }
+}
+
+/* insert wb into fmt */
+void fmt_word(struct fmt *f, struct wb *wb)
+{
+ if (f->nwords == NWORDS)
+ fmt_fill(f, 0);
+ if (wb_empty(wb) || f->nwords == NWORDS)
+ return;
+ if (FMT_FILL(f) && f->nls && f->gap)
+ fmt_sp(f);
+ if (!f->nwords) /* apply the new .l and .i */
+ fmt_confupdate(f);
+ if (f->nls && !f->gap && f->nwords >= 1)
+ f->gap = (f->nwords && f->eos) ? FMT_SWID(f) * 2 : FMT_SWID(f);
+ fmt_insertword(f, f->nwords++, wb, f->filled ? 0 : f->gap);
+ f->filled = 0;
+ f->nls = 0;
+ f->gap = 0;
+ f->eos = wb_eos(wb);
+}
+
+struct fmt *fmt_alloc(void)
+{
+ struct fmt *fmt = malloc(sizeof(*fmt));
+ memset(fmt, 0, sizeof(*fmt));
+ return fmt;
+}
+
+void fmt_free(struct fmt *fmt)
+{
+ free(fmt);
+}
+
+int fmt_wid(struct fmt *fmt)
+{
+ return fmt_wordslen(fmt, fmt->nwords) +
+ (fmt->nls ? FMT_SWID(fmt) : fmt->gap);
+}
+
+int fmt_morewords(struct fmt *fmt)
+{
+ return fmt_morelines(fmt) || fmt->nwords;
+}
+
+int fmt_morelines(struct fmt *fmt)
+{
+ return fmt->l_head != fmt->l_tail;
+}
--- a/reg.c
+++ b/reg.c
@@ -13,7 +13,7 @@
int eregs[NENVS]; /* environment-specific number registers */
int tabs[NTABS]; /* tab stops */
char tabs_type[NTABS]; /* type of tabs: L, C, R */
- struct adj *adj; /* per environment line buffer */
+ struct fmt *fmt; /* per environment line formatting buffer */
char tc[GNLEN]; /* tab character (.tc) */
char lc[GNLEN]; /* leader character (.lc) */
char hc[GNLEN]; /* hyphenation character (.hc) */
@@ -167,13 +167,13 @@
{
struct env *env = malloc(sizeof(*env));
memset(env, 0, sizeof(*env));
- env->adj = adj_alloc();
+ env->fmt = fmt_alloc();
return env;
}
static void env_free(struct env *env)
{
- adj_free(env->adj);
+ fmt_free(env->fmt);
free(env);
}
@@ -261,9 +261,9 @@
env_set(id);
}
-struct adj *env_adj(void)
+struct fmt *env_fmt(void)
{
- return env->adj;
+ return env->fmt;
}
char *env_hc(void)
--- a/ren.c
+++ b/ren.c
@@ -5,7 +5,7 @@
#include <string.h>
#include "roff.h"
-#define cadj env_adj() /* line buffer */
+#define cfmt env_fmt() /* line buffer */
#define RENWB(wb) ((wb) == &ren_wb) /* is ren_wb */
/* diversions */
@@ -38,6 +38,7 @@
static int bp_count; /* number of pages so far */
static int bp_ejected; /* current ejected page */
static int bp_final; /* 1: executing em, 2: the final page, 3: the 2nd final page */
+static int ren_level; /* the depth of render_rec() calls */
static char c_fa[GNLEN]; /* field delimiter */
static char c_fb[GNLEN]; /* field padding */
@@ -105,7 +106,7 @@
int f_hpos(void)
{
- return adj_wid(cadj) + wb_wid(&ren_wb);
+ return fmt_wid(cfmt) + wb_wid(&ren_wb);
}
void tr_divbeg(char **args)
@@ -166,10 +167,16 @@
}
}
+static int render_rec(int level);
static void trap_exec(int reg)
{
- if (str_get(reg))
+ char cmd[16];
+ if (str_get(reg)) {
+ sprintf(cmd, "%c%s %d\n", c_cc, TR_POPREN, ren_level);
+ in_pushnl(cmd, NULL);
in_pushnl(str_get(reg), NULL);
+ render_rec(++ren_level);
+ }
}
static int detect_traps(int beg, int end)
@@ -221,9 +228,9 @@
int ljust = li;
int llen = ll - ljust;
n_n = w;
- if (ad == AD_C)
+ if ((ad & AD_B) == AD_C)
ljust += llen > w ? (llen - w) / 2 : 0;
- if (ad == AD_R)
+ if ((ad & AD_B) == AD_R)
ljust += llen - w;
if (ljust)
sbuf_printf(spre, "%ch'%du'", c_ec, ljust);
@@ -300,13 +307,6 @@
wb_done(&wb);
}
-/* return one if the next line causes a trap or new page */
-static int ren_lastline(void)
-{
- int lspc = MAX(1, n_L) * n_v;
- return detect_traps(n_d, n_d + lspc) || detect_pagelimit(lspc);
-}
-
/* process a line and print it with ren_out() */
static int ren_line(char *line, int w, int ad, int body,
int li, int ll, int els_neg, int els_pos)
@@ -349,21 +349,21 @@
return 0;
}
-/* output current line; returns 1 if triggered a trap */
-static int ren_bradj(struct adj *adj, int fill, int ad)
+/* read a line from fmt and send it to ren_line() */
+static int ren_passline(struct fmt *fmt)
{
struct sbuf sbuf;
- int ll, li, els_neg, els_pos;
- int w, hyph, ret;
+ int ll, li, els_neg, els_pos, w, ret;
+ int ad = n_j;
ren_first();
- if (adj_empty(adj, fill))
+ if (!fmt_morewords(fmt))
return 0;
sbuf_init(&sbuf);
- hyph = n_hy;
- if ((n_hy & HY_LAST) && ren_lastline())
- hyph = 0; /* disable hyphenation final lines */
- w = adj_fill(adj, ad == AD_B, fill, hyph, &sbuf,
- &li, &ll, &els_neg, &els_pos);
+ fmt_nextline(fmt, &sbuf, &w, &li, &ll, &els_neg, &els_pos);
+ if (!n_u || n_na)
+ ad = AD_L;
+ if (n_ce)
+ ad = AD_C;
ret = ren_line(sbuf_buf(&sbuf), w, ad, 1, li, ll, els_neg, els_pos);
sbuf_done(&sbuf);
return ret;
@@ -370,20 +370,19 @@
}
/* output current line; returns 1 if triggered a trap */
-static int ren_br(int force)
+static int ren_br(void)
{
- int ad = n_j;
- if (!n_u || n_na || (n_j == AD_B && force))
- ad = AD_L;
- if (n_ce)
- ad = AD_C;
- return ren_bradj(cadj, !force && !n_ce && n_u, ad);
+ fmt_fill(cfmt, 0);
+ while (fmt_morelines(cfmt))
+ ren_passline(cfmt);
+ fmt_br(cfmt);
+ return ren_passline(cfmt);
}
void tr_br(char **args)
{
if (args[0][0] == c_cc)
- ren_br(1);
+ ren_br();
}
void tr_sp(char **args)
@@ -391,7 +390,7 @@
int traps = 0;
int n = args[1] ? eval(args[1], 'v') : n_v;
if (args[0][0] == c_cc)
- traps = ren_br(1);
+ traps = ren_br();
if (n && !n_ns && !traps)
down(n);
}
@@ -445,25 +444,13 @@
ren_pagelimit(n);
}
-static void push_eject(void)
+static void ren_ejectpage(int br)
{
- char buf[32];
bp_ejected = bp_count;
- sprintf(buf, "%c%s %d\n", c_cc, TR_EJECT, bp_ejected);
- in_pushnl(buf, NULL);
-}
-
-static void push_br(void)
-{
- char br[8] = {c_cc, 'b', 'r', '\n'};
- in_pushnl(br, NULL);
-}
-
-static void ren_eject(int id)
-{
- if (id == bp_ejected && id == bp_count && !cdiv) {
+ if (br)
+ ren_br();
+ while (bp_count == bp_ejected && !cdiv) {
if (detect_traps(n_d, n_p)) {
- push_eject();
ren_traps(n_d, n_p, 1);
} else {
bp_ejected = 0;
@@ -472,20 +459,12 @@
}
}
-void tr_eject(char **args)
-{
- ren_eject(atoi(args[1]));
-}
-
void tr_bp(char **args)
{
if (!cdiv && (args[1] || !n_ns)) {
- if (bp_ejected != bp_count)
- push_eject();
- if (args[0][0] == c_cc)
- push_br();
if (args[1])
bp_next = eval_re(args[1], n_pg, 0);
+ ren_ejectpage(args[0][0] == c_cc);
}
}
@@ -518,16 +497,16 @@
{
int in = args[1] ? eval_re(args[1], n_i, 'm') : n_i0;
if (args[0][0] == c_cc)
- ren_br(1);
+ ren_br();
n_i0 = n_i;
n_i = MAX(0, in);
- n_ti = 0;
+ n_ti = -1;
}
void tr_ti(char **args)
{
if (args[0][0] == c_cc)
- ren_br(1);
+ ren_br();
if (args[1])
n_ti = eval_re(args[1], n_i, 'm');
}
@@ -559,7 +538,7 @@
void tr_nf(char **args)
{
if (args[0][0] == c_cc)
- ren_br(1);
+ ren_br();
n_u = 0;
}
@@ -566,7 +545,7 @@
void tr_fi(char **args)
{
if (args[0][0] == c_cc)
- ren_br(1);
+ ren_br();
n_u = 1;
}
@@ -573,7 +552,7 @@
void tr_ce(char **args)
{
if (args[0][0] == c_cc)
- ren_br(1);
+ ren_br();
n_ce = args[1] ? atoi(args[1]) : 1;
}
@@ -928,56 +907,61 @@
return 0;
}
-/* read characters from in.c and pass rendered lines to out.c */
-int render(void)
+/* cause nested render_rec() to exit */
+void tr_popren(char **args)
{
+ ren_level = args[1] ? atoi(args[1]) : 0;
+}
+
+/* read characters from tr.c and pass the rendered lines to out.c */
+static int render_rec(int level)
+{
struct wb *wb = &ren_wb;
int c;
- n_nl = -1;
- wb_init(wb);
- tr_first();
- ren_first(); /* transition to the first page */
- c = ren_next();
- while (1) {
+ while (ren_level >= level) {
+ while (!tr_nextreq())
+ if (ren_level < level)
+ break;
+ if (ren_level < level)
+ break;
if (ren_aborted)
return 1;
+ c = ren_next();
if (c < 0) {
if (bp_final >= 2)
break;
- if (bp_final == 0 && trap_em >= 0) {
- trap_exec(trap_em);
+ if (bp_final == 0) {
bp_final = 1;
+ fmt_fill(cfmt, 0);
+ if (trap_em >= 0)
+ trap_exec(trap_em);
} else {
bp_final = 2;
- push_eject();
- push_br();
+ ren_ejectpage(1);
}
- c = ren_next();
- continue;
}
- ren_cnl = c == '\n';
- /* add wb (the current word) to cadj */
+ if (c >= 0)
+ ren_cnl = c == '\n';
+ /* add wb (the current word) to cfmt */
if (c == ' ' || c == '\n') {
- adj_swid(cadj, spacewid(n_f, n_s));
if (!wb_part(wb)) { /* not after a \c */
- adj_wb(cadj, wb);
- wb_reset(wb);
- /* wb contains only commands like \f */
- if (!ren_nl && wb_empty(wb))
- adj_nonl(cadj);
+ fmt_word(cfmt, wb);
if (c == '\n')
- adj_nl(cadj);
+ fmt_newline(cfmt);
else
- adj_sp(cadj);
+ fmt_space(cfmt);
+ if (!(n_j & AD_P))
+ fmt_fill(cfmt, 0);
+ wb_reset(wb);
}
}
/* flush the line if necessary */
- if (c == ' ' || c == '\n') {
- while ((ren_fillreq && !wb_part(wb) && !n_ce && n_u) ||
- adj_full(cadj, !n_ce && n_u)) {
- ren_br(0);
- ren_fillreq = 0;
- }
+ if (c == ' ' || c == '\n' || c < 0) {
+ if (ren_fillreq && !wb_part(wb))
+ fmt_fill(cfmt, 1);
+ while (fmt_morelines(cfmt))
+ ren_passline(cfmt);
+ ren_fillreq = 0;
}
if (c == '\n' || ren_nl) /* end or start of input line */
n_lb = f_hpos();
@@ -985,17 +969,30 @@
trap_exec(n_it);
if (c == '\n' && !wb_part(wb))
n_ce = MAX(0, n_ce - 1);
- if (c != ' ') {
+ if (c != ' ' && c >= 0) {
ren_back(c);
ren_char(wb, ren_next, ren_back);
}
- ren_nl = c == '\n';
- c = ren_next();
+ if (c >= 0)
+ ren_nl = c == '\n';
}
+ return 0;
+}
+
+/* render input words */
+int render(void)
+{
+ struct wb *wb = &ren_wb;
+ n_nl = -1;
+ wb_init(wb);
+ while (!tr_nextreq())
+ ;
+ ren_first(); /* transition to the first page */
+ render_rec(0);
bp_final = 3;
- if (!adj_empty(cadj, 0))
+ if (fmt_morewords(cfmt))
ren_page(bp_next, 1);
- ren_br(1);
+ ren_br();
wb_done(wb);
return 0;
}
--- a/roff.h
+++ b/roff.h
@@ -15,7 +15,7 @@
* + font_xyz: fonts (font.c)
* + sbuf_xyz: variable length string buffers (sbuf.c)
* + wb_xyz: word buffers (wb.c)
- * + adj_xyz: line adjustment buffers (adj.c)
+ * + fmt_xyz: line formatting buffers (fmt.c)
* + n_xyz: builtin number register xyz
* + c_xyz: characters for requests like hc and mc
*
@@ -34,7 +34,8 @@
#define RNLEN NMLEN /* register/macro name */
#define ILNLEN 1000 /* line limit of input files */
#define LNLEN 4000 /* line buffer length (ren.c/out.c) */
-#define NWORDS 256 /* number of words in line buffer */
+#define NWORDS 512 /* number of queued words in formatting buffer */
+#define NLINES 32 /* number of queued lines in formatting buffer */
#define NARGS 16 /* number of macro arguments */
#define NPREV 16 /* environment stack depth */
#define NTRAPS 1024 /* number of traps per page */
@@ -99,7 +100,7 @@
/* enviroments */
void env_init(void);
void env_done(void);
-struct adj *env_adj(void);
+struct fmt *env_fmt(void);
char *env_hc(void);
char *env_mc(void);
char *env_tc(void);
@@ -205,7 +206,7 @@
void cp_blk(int skip); /* skip or read the next line or block */
void cp_wid(int enable); /* control inlining \w requests */
#define cp_back in_back /* cp.c is stateless */
-void tr_first(void); /* read until the first non-command line */
+int tr_nextreq(void); /* read the next troff request */
/* variable length string buffer */
struct sbuf {
@@ -291,24 +292,26 @@
void hyphenate(char *hyphs, char *word, int flg);
-/* adjustment */
-#define AD_L 0
-#define AD_B 1
-#define AD_C 3
-#define AD_R 5
+/* adjustment types */
+#define AD_C 0 /* center */
+#define AD_L 1 /* adjust left margin (flag) */
+#define AD_R 2 /* adjust right margin (flag) */
+#define AD_B 3 /* adjust both margin (mask) */
+#define AD_P 4 /* paragraph-at-once adjustment (flag) */
-struct adj *adj_alloc(void);
-void adj_free(struct adj *adj);
-int adj_fill(struct adj *adj, int ad_b, int fill, int hyph, struct sbuf *dst,
+/* line formatting */
+struct fmt *fmt_alloc(void);
+void fmt_free(struct fmt *fmt);
+int fmt_wid(struct fmt *fmt);
+void fmt_word(struct fmt *fmt, struct wb *wb);
+void fmt_newline(struct fmt *fmt);
+void fmt_space(struct fmt *fmt);
+void fmt_br(struct fmt *fmt);
+int fmt_fill(struct fmt *fmt, int all);
+int fmt_morelines(struct fmt *fmt);
+int fmt_morewords(struct fmt *fmt);
+int fmt_nextline(struct fmt *fmt, struct sbuf *sbuf, int *w,
int *li, int *ll, int *els_neg, int *els_pos);
-int adj_full(struct adj *adj, int fill);
-int adj_empty(struct adj *adj, int fill);
-int adj_wid(struct adj *adj);
-void adj_swid(struct adj *adj, int swid);
-void adj_wb(struct adj *adj, struct wb *wb);
-void adj_nl(struct adj *adj);
-void adj_sp(struct adj *adj);
-void adj_nonl(struct adj *adj);
/* rendering */
int render(void); /* the main loop */
@@ -362,7 +365,7 @@
void tr_ta(char **args);
void tr_ti(char **args);
void tr_wh(char **args);
-void tr_eject(char **args);
+void tr_popren(char **args);
void tr_init(void);
@@ -389,7 +392,7 @@
/* internal commands */
#define TR_DIVBEG "\07<" /* diversion begins */
#define TR_DIVEND "\07>" /* diversion ends */
-#define TR_EJECT "\07P" /* page eject */
+#define TR_POPREN "\07P" /* exit render_rec() */
/* mapping register, macro and environment names to indices */
#define NREGS 4096 /* maximum number of mapped names */
--- a/tr.c
+++ b/tr.c
@@ -269,32 +269,34 @@
n_na = 1;
}
-static void tr_ad(char **args)
+static int adjmode(int c, int def)
{
- n_na = 0;
- if (!args[1])
- return;
- switch (args[1][0]) {
- case '0' + AD_L:
+ switch (c) {
case 'l':
- n_j = AD_L;
- break;
- case '0' + AD_R:
+ return AD_L;
case 'r':
- n_j = AD_R;
- break;
- case '0' + AD_C:
+ return AD_R;
case 'c':
- n_j = AD_C;
- break;
- case '0' + AD_B:
+ return AD_C;
case 'b':
case 'n':
- n_j = AD_B;
- break;
+ return AD_B;
}
+ return def;
}
+static void tr_ad(char **args)
+{
+ char *s = args[1];
+ n_na = 0;
+ if (!s)
+ return;
+ if (isdigit(s[0]))
+ n_j = atoi(s) & 15;
+ else
+ n_j = s[0] == 'p' ? AD_P | adjmode(s[1], AD_B) : adjmode(s[0], n_j);
+}
+
static void tr_tm(char **args)
{
fprintf(stderr, "%s\n", args[1]);
@@ -817,7 +819,7 @@
} cmds[] = {
{TR_DIVBEG, tr_divbeg},
{TR_DIVEND, tr_divend},
- {TR_EJECT, tr_eject},
+ {TR_POPREN, tr_popren},
{"ab", tr_ab, mkargs_eol},
{"ad", tr_ad},
{"af", tr_af},
@@ -906,39 +908,50 @@
{"wh", tr_wh},
};
-int tr_next(void)
+/* read the next troff request; return zero if a request was executed. */
+int tr_nextreq(void)
{
- int c = cp_next();
- int nl = c == '\n';
char *args[NARGS + 3] = {NULL};
char cmd[RNLEN];
char buf[LNLEN];
struct cmd *req;
- while (tr_nl && c >= 0 && (c == c_cc || c == c_c2)) {
- nl = 1;
- memset(args, 0, sizeof(args));
- args[0] = cmd;
- cmd[0] = c;
- req = NULL;
- arg_regname(cmd + 1, sizeof(cmd) - 1);
- req = str_dget(map(cmd + 1));
- if (req) {
- if (req->args)
- req->args(args + 1, buf, sizeof(buf));
- else
- mkargs_req(args + 1, buf, sizeof(buf));
- req->f(args);
- } else {
- cp_wid(0);
- mkargs(args + 1, buf, sizeof(buf));
- cp_wid(1);
- if (str_get(map(cmd + 1)))
- in_push(str_get(map(cmd + 1)), args + 1);
- }
- c = cp_next();
- nl = c == '\n';
+ int c;
+ if (!tr_nl)
+ return 1;
+ c = cp_next();
+ if (c < 0 || (c != c_cc && c != c_c2)) {
+ cp_back(c);
+ return 1;
}
- tr_nl = c < 0 || nl;
+ memset(args, 0, sizeof(args));
+ args[0] = cmd;
+ cmd[0] = c;
+ req = NULL;
+ arg_regname(cmd + 1, sizeof(cmd) - 1);
+ req = str_dget(map(cmd + 1));
+ if (req) {
+ if (req->args)
+ req->args(args + 1, buf, sizeof(buf));
+ else
+ mkargs_req(args + 1, buf, sizeof(buf));
+ req->f(args);
+ } else {
+ cp_wid(0);
+ mkargs(args + 1, buf, sizeof(buf));
+ cp_wid(1);
+ if (str_get(map(cmd + 1)))
+ in_push(str_get(map(cmd + 1)), args + 1);
+ }
+ return 0;
+}
+
+int tr_next(void)
+{
+ int c;
+ while (!tr_nextreq())
+ ;
+ c = cp_next();
+ tr_nl = c == '\n' || c < 0;
return c;
}
@@ -947,10 +960,4 @@
int i;
for (i = 0; i < LEN(cmds); i++)
str_dset(map(cmds[i].id), &cmds[i]);
-}
-
-void tr_first(void)
-{
- cp_back(tr_next());
- tr_nl = 1;
}