ref: 3bb6a7bbeebc83a29b60a83f356e2935af02d992
dir: /adj.c/
/* 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); }