shithub: neatroff

Download patch

ref: 4338ad0427d0aaa401b2f9f2411c0ff239ce7d34
parent: 5d20e93d9fcd37af03128fc712b282c7e9993b98
author: Ali Gholami Rudi <ali@rudi.ir>
date: Wed Apr 30 19:15:48 EDT 2014

fmt: not hyphenating last lines if requested

--- a/fmt.c
+++ b/fmt.c
@@ -47,6 +47,7 @@
 	/* for paragraph adjustment */
 	long best[NWORDS];
 	int best_pos[NWORDS];
+	int best_dep[NWORDS];
 	/* current line */
 	int gap;		/* space before the next word */
 	int nls;		/* newlines before the next word */
@@ -103,6 +104,13 @@
 	return w;
 }
 
+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;
+}
+
 /* the total width of the specified words in f->words[] */
 static int fmt_wordslen(struct fmt *f, int beg, int end)
 {
@@ -156,7 +164,7 @@
 static int fmt_sp(struct fmt *f)
 {
 	struct line *l;
-	fmt_fill(f, 0);
+	fmt_fill(f, 0, 0);
 	l = fmt_mkline(f);
 	if (!l)
 		return 1;
@@ -169,7 +177,7 @@
 
 void fmt_br(struct fmt *f)
 {
-	fmt_fill(f, 0);
+	fmt_fill(f, 0, 0);
 	f->filled = 0;
 	if (f->nwords)
 		fmt_sp(f);
@@ -233,7 +241,7 @@
 void fmt_word(struct fmt *f, struct wb *wb)
 {
 	if (f->nwords == NWORDS || fmt_confchanged(f))
-		fmt_fill(f, 0);
+		fmt_fill(f, 0, 0);
 	if (wb_empty(wb) || f->nwords == NWORDS)
 		return;
 	if (FMT_FILL(f) && f->nls && f->gap)
@@ -279,6 +287,7 @@
 		cur = fmt_findcost(f, i) + FMT_COST(lwid, llen, pen);
 		if (f->best_pos[pos] < 0 || cur < f->best[pos]) {
 			f->best_pos[pos] = i;
+			f->best_dep[pos] = f->best_dep[i] + 1;
 			f->best[pos] = cur;
 		}
 		i--;
@@ -296,8 +305,7 @@
 static int fmt_breakparagraph(struct fmt *f, int pos, int all)
 {
 	int i;
-	long best = 0;
-	int best_i = -1;
+	int best = -1;
 	int llen = FMT_LLEN(f);
 	int lwid = 0;
 	if (all || (pos > 0 && f->words[pos - 1].wid >= llen)) {
@@ -314,15 +322,27 @@
 			lwid += f->words[i + 1].gap;
 		if (lwid > llen && pos - i > 1)
 			break;
-		if (best_i < 0 || fmt_findcost(f, i) < best) {
-			best_i = i;
-			best = fmt_findcost(f, i);
-		}
+		if (best < 0 || fmt_findcost(f, i) < fmt_findcost(f, best))
+			best = i;
 		i--;
 	}
-	return best_i;
+	return best;
 }
 
+static int fmt_head(struct fmt *f, int nreq, int pos)
+{
+	int best = -1;
+	int i;
+	if (nreq <= 0 || f->best_dep[pos] < nreq)
+		return pos;
+	for (i = 1; i < pos && f->best_dep[i] <= nreq; i++) {
+		fmt_findcost(f, i);
+		if (f->best_dep[i] == nreq && !f->words[i - 1].hy)
+			best = i;
+	}
+	return best >= 0 ? best : i - 1;
+}
+
 /* break f->words[0..end] into lines according to fmt_bestpos() */
 static int fmt_break(struct fmt *f, int end)
 {
@@ -353,18 +373,39 @@
 	return ret + (end - beg);
 }
 
-int fmt_fill(struct fmt *f, int all)
+/*
+ * fill the words collected in the buffer
+ *
+ * The argument nreq, when nonzero, limits the number of lines
+ * to format.  It also tells fmt_fill() not to hyphenate the
+ * last word of nreq-th line.  This is used in ren.c to prevent
+ * hyphenating last lines of pages.
+ *
+ * The all argument forces fmt_fill() to fill all of the words.
+ * This is used for the \p escape sequence.
+ */
+int fmt_fill(struct fmt *f, int nreq, int all)
 {
-	int end, n, i;
+	int end;	/* the final line ends before this word */
+	int end_head;	/* like end, but only the first nreq lines included */
+	int head = 0;	/* only nreq first lines have been formatted */
+	int n, i;
 	if (!FMT_FILL(f))
 		return 0;
 	/* not enough words to fill */
 	if (!all && fmt_wordslen(f, 0, f->nwords) <= FMT_LLEN(f))
 		return 0;
+	if (nreq > 0 && nreq <= fmt_nlines(f))
+		return 0;
 	/* resetting positions */
 	for (i = 0; i < f->nwords + 1; i++)
 		f->best_pos[i] = -1;
 	end = fmt_breakparagraph(f, f->nwords, all);
+	if (nreq > 0) {
+		end_head = fmt_head(f, nreq - fmt_nlines(f), end);
+		head = end_head < end;
+		end = end_head;
+	}
 	/* recursively add lines */
 	n = fmt_break(f, end);
 	f->nwords -= n;
@@ -374,7 +415,7 @@
 		f->words[0].gap = 0;
 	if (f->nwords)		/* apply the new .l and .i */
 		fmt_confupdate(f);
-	return n != end;
+	return head || n != end;
 }
 
 struct fmt *fmt_alloc(void)
--- a/ren.c
+++ b/ren.c
@@ -369,12 +369,27 @@
 	return ret;
 }
 
+/* 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);
+}
+
+/* call fmt_fill() for all pending lines */
+static void ren_fmtfill(struct fmt *fmt, int all)
+{
+	int n;
+	do {	/* do not hyphenate lines preceding traps or new pages */
+		n = fmt_fill(fmt, (n_hy & HY_LAST) ? ren_safelines() : 0, all);
+		while (fmt_morelines(fmt))
+			ren_passline(fmt);
+	} while (n);
+}
+
 /* output current line; returns 1 if triggered a trap */
 static int ren_br(void)
 {
-	fmt_fill(cfmt, 0);
-	while (fmt_morelines(cfmt))
-		ren_passline(cfmt);
+	ren_fmtfill(cfmt, 0);
 	fmt_br(cfmt);
 	return ren_passline(cfmt);
 }
@@ -932,7 +947,7 @@
 				break;
 			if (bp_final == 0) {
 				bp_final = 1;
-				fmt_fill(cfmt, 0);
+				ren_fmtfill(cfmt, 0);
 				if (trap_em >= 0)
 					trap_exec(trap_em);
 			} else {
@@ -950,15 +965,15 @@
 					fmt_newline(cfmt);
 				else
 					fmt_space(cfmt);
-				if (!(n_j & AD_P))
-					fmt_fill(cfmt, 0);
 				wb_reset(wb);
+				if (!(n_j & AD_P))
+					ren_fmtfill(cfmt, 0);
 			}
 		}
 		/* flush the line if necessary */
 		if (c == ' ' || c == '\n' || c < 0) {
 			if (ren_fillreq && !wb_part(wb))
-				fmt_fill(cfmt, 1);
+				ren_fmtfill(cfmt, 1);
 			while (fmt_morelines(cfmt))
 				ren_passline(cfmt);
 			ren_fillreq = 0;
--- a/roff.h
+++ b/roff.h
@@ -306,7 +306,7 @@
 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_fill(struct fmt *fmt, int nreq, 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,