git: da2905289748 - stable/14 - diff: Fix --expand-tabs and --side-by-side.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 11 Mar 2024 12:35:06 UTC
The branch stable/14 has been updated by des:
URL: https://cgit.FreeBSD.org/src/commit/?id=da290528974846d3ed49138ca0f2de7fff02eaf6
commit da290528974846d3ed49138ca0f2de7fff02eaf6
Author: Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2024-02-26 18:08:06 +0000
Commit: Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2024-03-11 12:19:06 +0000
diff: Fix --expand-tabs and --side-by-side.
* Overhaul column width and padding calculation.
* Rewrite print_space() so it is now a) correct and b) understandable.
* Rewrite tab expansion in fetch() for the same reason.
This brings us in line with GNU diff for all cases I could think of.
Sponsored by: Klara, Inc.
Reviewed by: imp
Differential Revision: https://reviews.freebsd.org/D44014
(cherry picked from commit 53de23f4d140becc3166e87665b0064f215a220e)
diff: Bump manual page date.
Sponsored by: Klara, Inc.
(cherry picked from commit 312b1076c6b0aff9bbcaff058b93385eaf607685)
---
usr.bin/diff/diff.1 | 11 ++++-
usr.bin/diff/diff.c | 12 ++----
usr.bin/diff/diffreg.c | 113 ++++++++++++++++++++++++++++---------------------
3 files changed, 79 insertions(+), 57 deletions(-)
diff --git a/usr.bin/diff/diff.1 b/usr.bin/diff/diff.1
index e04aeb5d6d67..d3978158d1d1 100644
--- a/usr.bin/diff/diff.1
+++ b/usr.bin/diff/diff.1
@@ -29,7 +29,7 @@
.\"
.\" @(#)diff.1 8.1 (Berkeley) 6/30/93
.\"
-.Dd March 10, 2022
+.Dd February 26, 2024
.Dt DIFF 1
.Os
.Sh NAME
@@ -429,6 +429,15 @@ Output at most
.Ar number
columns when using side by side format.
The default value is 130.
+Note that unless
+.It Fl t
+was specified,
+.Nm
+will always align the second column to a tab stop, so values of
+.Fl -width
+smaller than approximately five times the value of
+.Fl -tabsize
+may yield surprising results.
.It Fl -changed-group-format Ar GFMT
Format input groups in the provided
.Pp
diff --git a/usr.bin/diff/diff.c b/usr.bin/diff/diff.c
index 03eb363fc323..d947c1e01705 100644
--- a/usr.bin/diff/diff.c
+++ b/usr.bin/diff/diff.c
@@ -276,10 +276,8 @@ main(int argc, char **argv)
break;
case 'W':
width = (int) strtonum(optarg, 1, INT_MAX, &errstr);
- if (errstr) {
- warnx("Invalid argument for width");
- usage();
- }
+ if (errstr)
+ errx(1, "width is %s: %s", errstr, optarg);
break;
case 'X':
read_excludes_file(optarg);
@@ -317,10 +315,8 @@ main(int argc, char **argv)
break;
case OPT_TSIZE:
tabsize = (int) strtonum(optarg, 1, INT_MAX, &errstr);
- if (errstr) {
- warnx("Invalid argument for tabsize");
- usage();
- }
+ if (errstr)
+ errx(1, "tabsize is %s: %s", errstr, optarg);
break;
case OPT_STRIPCR:
dflags |= D_STRIPCR;
diff --git a/usr.bin/diff/diffreg.c b/usr.bin/diff/diffreg.c
index 11aefb73cda6..8042003cd836 100644
--- a/usr.bin/diff/diffreg.c
+++ b/usr.bin/diff/diffreg.c
@@ -169,7 +169,6 @@ struct context_vec {
enum readhash { RH_BINARY, RH_OK, RH_EOF };
-#define MIN_PAD 1
static FILE *opentemp(const char *);
static void output(char *, FILE *, char *, FILE *, int);
static void check(FILE *, FILE *, int);
@@ -209,7 +208,7 @@ static int len[2];
static int pref, suff; /* length of prefix and suffix */
static int slen[2];
static int anychange;
-static int hw, padding; /* half width and padding */
+static int hw, lpad, rpad; /* half width and padding */
static int edoffset;
static long *ixnew; /* will be overlaid on file[1] */
static long *ixold; /* will be overlaid on klist */
@@ -254,21 +253,44 @@ diffreg(char *file1, char *file2, int flags, int capsicum)
lastline = 0;
lastmatchline = 0;
- /*
- * hw excludes padding and make sure when -t is not used,
- * the second column always starts from the closest tab stop
- */
+ /*
+ * In side-by-side mode, we need to print the left column, a
+ * change marker surrounded by padding, and the right column.
+ *
+ * If expanding tabs, we don't care about alignment, so we simply
+ * subtract 3 from the width and divide by two.
+ *
+ * If not expanding tabs, we need to ensure that the right column
+ * is aligned to a tab stop. We start with the same formula, then
+ * decrement until we reach a size that lets us tab-align the
+ * right column. We then adjust the width down if necessary for
+ * the padding calculation to work.
+ *
+ * Left padding is half the space left over, rounded down; right
+ * padding is whatever is needed to match the width.
+ */
if (diff_format == D_SIDEBYSIDE) {
- hw = width >> 1;
- padding = tabsize - (hw % tabsize);
- if ((flags & D_EXPANDTABS) != 0 || (padding % tabsize == 0))
- padding = MIN_PAD;
-
- hw = (width >> 1) -
- ((padding == MIN_PAD) ? (padding << 1) : padding) - 1;
+ if (flags & D_EXPANDTABS) {
+ if (width > 3) {
+ hw = (width - 3) / 2;
+ } else {
+ /* not enough space */
+ hw = 0;
+ }
+ } else if (width <= 3 || width <= tabsize) {
+ /* not enough space */
+ hw = 0;
+ } else {
+ hw = (width - 3) / 2;
+ while (hw > 0 && roundup(hw + 3, tabsize) + hw > width)
+ hw--;
+ if (width - (roundup(hw + 3, tabsize) + hw) < tabsize)
+ width = roundup(hw + 3, tabsize) + hw;
+ }
+ lpad = (width - hw * 2 - 1) / 2;
+ rpad = (width - hw * 2 - 1) - lpad;
}
-
if (flags & D_IGNORECASE)
chrtran = cup2low;
else
@@ -869,7 +891,7 @@ output(char *file1, FILE *f1, char *file2, FILE *f2, int flags)
while (i0 <= m && J[i0] == J[i0 - 1] + 1) {
if (diff_format == D_SIDEBYSIDE && suppress_common != 1) {
nc = fetch(ixold, i0, i0, f1, '\0', 1, flags);
- print_space(nc, (hw - nc) + (padding << 1) + 1, flags);
+ print_space(nc, hw - nc + lpad + 1 + rpad, flags);
fetch(ixnew, J[i0], J[i0], f2, '\0', 0, flags);
printf("\n");
}
@@ -1147,10 +1169,10 @@ proceed:
else if (color && c > d)
printf("\033[%sm", del_code);
if (a > b) {
- print_space(0, hw + padding , *pflags);
+ print_space(0, hw + lpad, *pflags);
} else {
nc = fetch(ixold, a, b, f1, '\0', 1, *pflags);
- print_space(nc, hw - nc + padding, *pflags);
+ print_space(nc, hw - nc + lpad, *pflags);
}
if (color && a > b)
printf("\033[%sm", add_code);
@@ -1159,7 +1181,7 @@ proceed:
printf("%c", (a > b) ? '>' : ((c > d) ? '<' : '|'));
if (color && c > d)
printf("\033[m");
- print_space(hw + padding + 1 , padding, *pflags);
+ print_space(hw + lpad + 1, rpad, *pflags);
fetch(ixnew, c, d, f2, '\0', 0, *pflags);
printf("\n");
}
@@ -1265,30 +1287,24 @@ fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags)
printf("\n\\ No newline at end of file\n");
return (col);
}
- /*
- * when using --side-by-side, col needs to be increased
- * in any case to keep the columns aligned
- */
if (c == '\t') {
- if (flags & D_EXPANDTABS) {
- newcol = ((col / tabsize) + 1) * tabsize;
- do {
- printf(" ");
- } while (++col < newcol && col < hw);
+ /*
+ * Calculate where the tab would bring us.
+ * If it would take us to the end of the
+ * column, either clip it (if expanding
+ * tabs) or return right away (if not).
+ */
+ newcol = roundup(col + 1, tabsize);
+ if ((flags & D_EXPANDTABS) == 0) {
+ if (hw > 0 && newcol >= hw)
+ return (col);
+ printf("\t");
} else {
- if (diff_format == D_SIDEBYSIDE) {
- if ((col + tabsize) > hw) {
- printf("%*s", hw - col, "");
- col = hw;
- } else {
- printf("\t");
- col += tabsize - 1;
- }
- } else {
- printf("\t");
- col++;
- }
+ if (hw > 0 && newcol > hw)
+ newcol = hw;
+ printf("%*s", newcol - col, "");
}
+ col = newcol;
} else {
if (diff_format == D_EDIT && j == 1 && c == '\n' &&
lastc == '.') {
@@ -1668,18 +1684,19 @@ print_header(const char *file1, const char *file2)
* nc is the preceding number of characters
*/
static void
-print_space(int nc, int n, int flags) {
- int i, col;
+print_space(int nc, int n, int flags)
+{
+ int col, newcol, tabstop;
- col = n;
+ col = nc;
+ newcol = nc + n;
+ /* first, use tabs if allowed */
if ((flags & D_EXPANDTABS) == 0) {
- /* first tabstop may be closer than tabsize */
- i = tabsize - (nc % tabsize);
- while (col >= tabsize) {
+ while ((tabstop = roundup(col + 1, tabsize)) <= newcol) {
printf("\t");
- col -= i;
- i = tabsize;
+ col = tabstop;
}
}
- printf("%*s", col, "");
+ /* finish with spaces */
+ printf("%*s", newcol - col, "");
}