From nobody Mon Feb 26 18:08:56 2024 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4Tk7sh3VcNz5BtPC; Mon, 26 Feb 2024 18:08:56 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Tk7sh31rVz4lrB; Mon, 26 Feb 2024 18:08:56 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1708970936; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=7AlgVJ7PXsoCokmkMMiR/dSAIYu4N1FP41tQvyV636Q=; b=b9PxX7FNc8nU9LS0KIpRlwfPsMMaNMLzTKA1qQNqoOSUvwb0RaxWTM785xBb4vyWEW0Nf2 TJN8Y24a2ah4o1YieSZSMnwXEsUsbziikEB3dSZUFQ2SiI1n1Yc9jI9qAQVlP0oUIvULYj x1FdlRWi79LTzyZQENHzo6yMWDvUs+CcuTShzXcDYfwCciUQ1AagGFL9GB4FzwDP2rNq9V 0D0+MJ2b4NfokTJ3AFmbXYvJuMaUXF2z//YITf8vbn64c68IUi9r/KRF9kQx6u15qfO/WC +kDUbx66BwZqh0V2aP39R8h5w9eh511m3S3RWFx6gAm3Xc7czVmrTRu5Go4FoQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1708970936; a=rsa-sha256; cv=none; b=ythVJeYRMjyY4GepoqQ3J3fpLX/nj8QGLHtb/PbtWNJh5Rp7YMALpGdD908Opy1aGkw/Ak GyJw1Z/I45hDRUu5EtEAujEPXJMIX3ie+pN5a4RqN1ZMMjalW2ZuFKtFTLTGQcLQxfdPX1 ovP8MN4QTVlpr7vMyUnIM2bu8Sw8gwfSOIawKP51eVuO6yId0nH987NT5f1fPB5WH0/23J Y3yy9bH3oOrAetI53ViaCVlOQdalkFwBRNU3xe3MJEsTUEMhYNO9chMKIic3F80TMxyYCq 7Lom44VfBo34nqlFlw/9Kd+oroXsMW/3VQ3YTnp0YZlhQTOQs0JF/sst1bW9Jw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1708970936; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=7AlgVJ7PXsoCokmkMMiR/dSAIYu4N1FP41tQvyV636Q=; b=agNDYovmUCQz/wkPP/9bTHmrdlsC8kufpg9q4zac29I5yomE4GK26LZ9iAqv/jUuJ8RM0G fzbalyRwBU420HZ5tMDbtnma1YKy1vp1GmmvUjPT+EBy6e3QsSesaG8UK+8bCRGvrBSu2V C9sfKmAPvReeYVhcdSkzrLCPEmkfEVXEYYxZRPD7qBWdvSBMuaAhg4XGnzRCfccNpq+/Au IM2lQV30qWrn6XT1DpKUUdHLoJ2ToNhHrOOQPhfNRQy/RnD34zzndzK3f4bFFZ6d8JcWWS aJ59tb/LAF9RLa80aWHvzE0Fw9Rg5wPl7JYImby0w0Z5zezcV2DHepwlXu7Y1A== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4Tk7sh25pSzdXV; Mon, 26 Feb 2024 18:08:56 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 41QI8u4s081218; Mon, 26 Feb 2024 18:08:56 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 41QI8u4C081215; Mon, 26 Feb 2024 18:08:56 GMT (envelope-from git) Date: Mon, 26 Feb 2024 18:08:56 GMT Message-Id: <202402261808.41QI8u4C081215@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Dag-Erling =?utf-8?Q?Sm=C3=B8rgrav?= Subject: git: 53de23f4d140 - main - diff: Fix --expand-tabs and --side-by-side. List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: des X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 53de23f4d140becc3166e87665b0064f215a220e Auto-Submitted: auto-generated The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=53de23f4d140becc3166e87665b0064f215a220e commit 53de23f4d140becc3166e87665b0064f215a220e Author: Dag-Erling Smørgrav AuthorDate: 2024-02-26 18:08:06 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2024-02-26 18:08: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 --- usr.bin/diff/diff.1 | 9 ++++ usr.bin/diff/diff.c | 12 ++---- usr.bin/diff/diffreg.c | 113 ++++++++++++++++++++++++++++--------------------- 3 files changed, 78 insertions(+), 56 deletions(-) diff --git a/usr.bin/diff/diff.1 b/usr.bin/diff/diff.1 index 10a349348cfd..34658ad69da0 100644 --- a/usr.bin/diff/diff.1 +++ b/usr.bin/diff/diff.1 @@ -427,6 +427,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 348741f8ccca..c67f3105290f 100644 --- a/usr.bin/diff/diffreg.c +++ b/usr.bin/diff/diffreg.c @@ -166,7 +166,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); @@ -206,7 +205,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 */ @@ -251,21 +250,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 @@ -866,7 +888,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"); } @@ -1144,10 +1166,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); @@ -1156,7 +1178,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"); } @@ -1262,30 +1284,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 == '.') { @@ -1665,18 +1681,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, ""); }