diff(1): Implement -B/--ignore-blank-lines

As noted by cem in r338035, coccinelle invokes diff(1) with the -B flag.
This was not previously implemented here, so one was forced to create a link
for GNU diff to /usr/local/bin/diff

Implement the -B flag and add some primitive tests for it. It is implemented
in the same fashion that -I is implemented; each chunk's lines are scanned,
and if a non-blank line is encountered then the chunk will be output.
Otherwise, it's skipped.

MFC after:	2 weeks
This commit is contained in:
Kyle Evans 2018-08-19 03:57:20 +00:00
parent d17f8070a1
commit e68edb8cf0
10 changed files with 72 additions and 12 deletions

View File

@ -5,7 +5,6 @@
* make a libsdiff and use that directly to avoid duplicating the code * make a libsdiff and use that directly to avoid duplicating the code
to be implemented: to be implemented:
--ignore-blank-lines
--horizon-lines --horizon-lines
--ignore-tab-expansion --ignore-tab-expansion
--line-format --line-format

View File

@ -30,7 +30,7 @@
.\" @(#)diff.1 8.1 (Berkeley) 6/30/93 .\" @(#)diff.1 8.1 (Berkeley) 6/30/93
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd April 20, 2017 .Dd August 18, 2018
.Dt DIFF 1 .Dt DIFF 1
.Os .Os
.Sh NAME .Sh NAME
@ -38,7 +38,7 @@
.Nd differential file and directory comparator .Nd differential file and directory comparator
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm diff .Nm diff
.Op Fl abdipTtw .Op Fl aBbdipTtw
.Oo .Oo
.Fl c | e | f | .Fl c | e | f |
.Fl n | q | u .Fl n | q | u
@ -67,7 +67,7 @@
.Op Fl L Ar label | Fl -label Ar label .Op Fl L Ar label | Fl -label Ar label
.Ar file1 file2 .Ar file1 file2
.Nm diff .Nm diff
.Op Fl abdilpTtw .Op Fl aBbdilpTtw
.Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern .Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern
.Op Fl L Ar label | Fl -label Ar label .Op Fl L Ar label | Fl -label Ar label
.Op Fl -brief .Op Fl -brief
@ -93,7 +93,7 @@
.Fl C Ar number | -context Ar number .Fl C Ar number | -context Ar number
.Ar file1 file2 .Ar file1 file2
.Nm diff .Nm diff
.Op Fl abdiltw .Op Fl aBbdiltw
.Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern .Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern
.Op Fl -brief .Op Fl -brief
.Op Fl -changed-group-format Ar GFMT .Op Fl -changed-group-format Ar GFMT
@ -118,7 +118,7 @@
.Fl D Ar string | Fl -ifdef Ar string .Fl D Ar string | Fl -ifdef Ar string
.Ar file1 file2 .Ar file1 file2
.Nm diff .Nm diff
.Op Fl abdilpTtw .Op Fl aBbdilpTtw
.Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern .Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern
.Op Fl L Ar label | Fl -label Ar label .Op Fl L Ar label | Fl -label Ar label
.Op Fl -brief .Op Fl -brief
@ -144,7 +144,7 @@
.Fl U Ar number | Fl -unified Ar number .Fl U Ar number | Fl -unified Ar number
.Ar file1 file2 .Ar file1 file2
.Nm diff .Nm diff
.Op Fl abdilNPprsTtw .Op Fl aBbdilNPprsTtw
.Oo .Oo
.Fl c | e | f | .Fl c | e | f |
.Fl n | q | u .Fl n | q | u
@ -300,6 +300,8 @@ if files contain binary characters.
Use of this option forces Use of this option forces
.Nm .Nm
to produce a diff. to produce a diff.
.It Fl B Fl -ignore-blank-lines
Causes chunks that include only blank lines to be ignored.
.It Fl b .It Fl b
Causes trailing blanks (spaces and tabs) to be ignored, and other Causes trailing blanks (spaces and tabs) to be ignored, and other
strings of blanks to compare equal. strings of blanks to compare equal.

View File

@ -66,6 +66,7 @@ static struct option longopts[] = {
{ "ed", no_argument, 0, 'e' }, { "ed", no_argument, 0, 'e' },
{ "forward-ed", no_argument, 0, 'f' }, { "forward-ed", no_argument, 0, 'f' },
{ "speed-large-files", no_argument, NULL, 'H' }, { "speed-large-files", no_argument, NULL, 'H' },
{ "ignore-blank-lines", no_argument, 0, 'B' },
{ "ignore-matching-lines", required_argument, 0, 'I' }, { "ignore-matching-lines", required_argument, 0, 'I' },
{ "ignore-case", no_argument, 0, 'i' }, { "ignore-case", no_argument, 0, 'i' },
{ "paginate", no_argument, NULL, 'l' }, { "paginate", no_argument, NULL, 'l' },
@ -164,6 +165,9 @@ main(int argc, char **argv)
case 'h': case 'h':
/* silently ignore for backwards compatibility */ /* silently ignore for backwards compatibility */
break; break;
case 'B':
dflags |= D_SKIPBLANKLINES;
break;
case 'I': case 'I':
push_ignore_pats(optarg); push_ignore_pats(optarg);
break; break;
@ -447,18 +451,18 @@ void
usage(void) usage(void)
{ {
(void)fprintf(stderr, (void)fprintf(stderr,
"usage: diff [-abdilpTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n" "usage: diff [-aBbdilpTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n"
" [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n" " [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n"
" [-I pattern] [-L label] file1 file2\n" " [-I pattern] [-L label] file1 file2\n"
" diff [-abdilpTtw] [-I pattern] [-L label] [--ignore-case]\n" " diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]\n"
" [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n" " [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n"
" -C number file1 file2\n" " -C number file1 file2\n"
" diff [-abdiltw] [-I pattern] [--ignore-case] [--no-ignore-case]\n" " diff [-aBbdiltw] [-I pattern] [--ignore-case] [--no-ignore-case]\n"
" [--normal] [--strip-trailing-cr] [--tabsize] -D string file1 file2\n" " [--normal] [--strip-trailing-cr] [--tabsize] -D string file1 file2\n"
" diff [-abdilpTtw] [-I pattern] [-L label] [--ignore-case]\n" " diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]\n"
" [--no-ignore-case] [--normal] [--tabsize] [--strip-trailing-cr]\n" " [--no-ignore-case] [--normal] [--tabsize] [--strip-trailing-cr]\n"
" -U number file1 file2\n" " -U number file1 file2\n"
" diff [-abdilNPprsTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n" " diff [-aBbdilNPprsTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n"
" [--no-ignore-case] [--normal] [--tabsize] [-I pattern] [-L label]\n" " [--no-ignore-case] [--normal] [--tabsize] [-I pattern] [-L label]\n"
" [-S name] [-X file] [-x pattern] dir1 dir2\n"); " [-S name] [-X file] [-x pattern] dir1 dir2\n");

View File

@ -67,6 +67,7 @@
#define D_EXPANDTABS 0x100 /* Expand tabs to spaces */ #define D_EXPANDTABS 0x100 /* Expand tabs to spaces */
#define D_IGNOREBLANKS 0x200 /* Ignore white space changes */ #define D_IGNOREBLANKS 0x200 /* Ignore white space changes */
#define D_STRIPCR 0x400 /* Strip trailing cr */ #define D_STRIPCR 0x400 /* Strip trailing cr */
#define D_SKIPBLANKLINES 0x800 /* Skip blank lines */
/* /*
* Status values for print_status() and diffreg() return values * Status values for print_status() and diffreg() return values

View File

@ -79,6 +79,7 @@ __FBSDID("$FreeBSD$");
#include <fcntl.h> #include <fcntl.h>
#include <paths.h> #include <paths.h>
#include <regex.h> #include <regex.h>
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
@ -1000,6 +1001,31 @@ restart:
} }
return; return;
} }
if (*pflags & D_SKIPBLANKLINES) {
char *line;
/*
* All lines in the change, insert, or delete must not be
* empty for the change to be ignored.
*/
if (a <= b) { /* Changes and deletes. */
for (i = a; i <= b; i++) {
line = preadline(fileno(f1),
ixold[i] - ixold[i - 1], ixold[i - 1]);
if (*line != '\0')
goto proceed;
}
}
if (a > b || c <= d) { /* Changes and inserts. */
for (i = c; i <= d; i++) {
line = preadline(fileno(f2),
ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
if (*line != '\0')
goto proceed;
}
}
return;
}
proceed: proceed:
if (*pflags & D_HEADER && diff_format != D_BRIEF) { if (*pflags & D_HEADER && diff_format != D_BRIEF) {
diff_output("%s %s %s\n", diffargs, file1, file2); diff_output("%s %s %s\n", diffargs, file1, file2);

View File

@ -0,0 +1,2 @@
1a2
>

View File

@ -0,0 +1,2 @@
1a2
> C

View File

@ -0,0 +1,4 @@
7c8
< G
---
> X

View File

@ -5,6 +5,9 @@ PACKAGE= tests
ATF_TESTS_SH= diff_test ATF_TESTS_SH= diff_test
${PACKAGE}FILES+= \ ${PACKAGE}FILES+= \
Bflag_C.out \
Bflag_D.out \
Bflag_F.out \
input1.in \ input1.in \
input2.in \ input2.in \
input_c1.in \ input_c1.in \

View File

@ -9,6 +9,7 @@ atf_test_case group_format
atf_test_case side_by_side atf_test_case side_by_side
atf_test_case brief_format atf_test_case brief_format
atf_test_case b230049 atf_test_case b230049
atf_test_case Bflag
simple_body() simple_body()
{ {
@ -150,6 +151,21 @@ brief_format_body()
diff -Nrq A D diff -Nrq A D
} }
Bflag_body()
{
atf_check -x 'printf "A\nB\n" > A'
atf_check -x 'printf "A\n\nB\n" > B'
atf_check -x 'printf "A\n \nB\n" > C'
atf_check -x 'printf "A\nC\nB\n" > D'
atf_check -x 'printf "A\nB\nC\nD\nE\nF\nG\nH" > E'
atf_check -x 'printf "A\n\nB\nC\nD\nE\nF\nX\nH" > F'
atf_check -s exit:0 -o inline:"" diff -B A B
atf_check -s exit:1 -o file:"$(atf_get_srcdir)/Bflag_C.out" diff -B A C
atf_check -s exit:1 -o file:"$(atf_get_srcdir)/Bflag_D.out" diff -B A D
atf_check -s exit:1 -o file:"$(atf_get_srcdir)/Bflag_F.out" diff -B E F
}
atf_init_test_cases() atf_init_test_cases()
{ {
atf_add_test_case simple atf_add_test_case simple
@ -161,4 +177,5 @@ atf_init_test_cases()
atf_add_test_case side_by_side atf_add_test_case side_by_side
atf_add_test_case brief_format atf_add_test_case brief_format
atf_add_test_case b230049 atf_add_test_case b230049
atf_add_test_case Bflag
} }