aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPiotr Pawel Stefaniak <pstef@FreeBSD.org>2021-09-05 14:54:07 +0000
committerPiotr Pawel Stefaniak <pstef@FreeBSD.org>2021-09-15 23:46:44 +0000
commite51aabf8cbb9b412c725c4c9c727ca6faa0630c6 (patch)
tree63813b2dec97de72f65db709ea956bda2f800b72
parentf38702e5a52e1350b9349bc297ccd0b50b1941c3 (diff)
downloadsrc-e51aabf8cbb9b412c725c4c9c727ca6faa0630c6.tar.gz
src-e51aabf8cbb9b412c725c4c9c727ca6faa0630c6.zip
diff: implement option -F (--show-function-line)
With unified and context diffs, show the last line that matches the provided pattern before the context. Reviewed by: bapt Differential Revision: https://reviews.freebsd.org/D31714
-rw-r--r--usr.bin/diff/diff.18
-rw-r--r--usr.bin/diff/diff.c59
-rw-r--r--usr.bin/diff/diff.h6
-rw-r--r--usr.bin/diff/diffreg.c14
4 files changed, 62 insertions, 25 deletions
diff --git a/usr.bin/diff/diff.1 b/usr.bin/diff/diff.1
index 6056ddd3ac76..dea01bf918f4 100644
--- a/usr.bin/diff/diff.1
+++ b/usr.bin/diff/diff.1
@@ -65,11 +65,13 @@
.Op Fl -text
.Op Fl -unified
.Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern
+.Op Fl F Ar pattern | Fl -show-function-line Ar pattern
.Op Fl L Ar label | Fl -label Ar label
.Ar file1 file2
.Nm diff
.Op Fl aBbdilpTtw
.Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern
+.Op Fl F Ar pattern | Fl -show-function-line Ar pattern
.Op Fl L Ar label | Fl -label Ar label
.Op Fl -brief
.Op Fl -color Ns = Ns Ar when
@@ -123,6 +125,7 @@
.Nm diff
.Op Fl aBbdilpTtw
.Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern
+.Op Fl F Ar pattern | Fl -show-function-line Ar pattern
.Op Fl L Ar label | Fl -label Ar label
.Op Fl -brief
.Op Fl -color Ns = Ns Ar when
@@ -180,6 +183,7 @@
.Op Fl -unidirectional-new-file
.Op Fl -unified
.Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern
+.Op Fl F Ar pattern | Fl -show-function-line Ar pattern
.Bk -words
.Op Fl L Ar label | Fl -label Ar label
.Op Fl S Ar name | Fl -starting-file Ar name
@@ -357,6 +361,10 @@ environment variable is set to a non-empty string.
Try very hard to produce a diff as small as possible.
This may consume a lot of processing power and memory when processing
large files with many changes.
+.It Fl F Ar pattern, Fl -show-function-line Ar pattern
+Like
+.Fl p,
+but display the last line that matches provided pattern.
.It Fl I Ar pattern Fl -ignore-matching-lines Ar pattern
Ignores changes, insertions, and deletions whose lines match the
extended regular expression
diff --git a/usr.bin/diff/diff.c b/usr.bin/diff/diff.c
index 4fc3094035d9..8074261742ae 100644
--- a/usr.bin/diff/diff.c
+++ b/usr.bin/diff/diff.c
@@ -43,14 +43,15 @@ bool ignore_file_case, suppress_common, color;
int diff_format, diff_context, status;
int tabsize = 8, width = 130;
static int colorflag = COLORFLAG_NEVER;
-char *start, *ifdefname, *diffargs, *label[2], *ignore_pats;
+char *start, *ifdefname, *diffargs, *label[2];
+char *ignore_pats, *most_recent_pat;
char *group_format = NULL;
const char *add_code, *del_code;
struct stat stb1, stb2;
struct excludes *excludes_list;
-regex_t ignore_re;
+regex_t ignore_re, most_recent_re;
-#define OPTIONS "0123456789aBbC:cdD:efHhI:iL:lnNPpqrS:sTtU:uwW:X:x:y"
+#define OPTIONS "0123456789aBbC:cdD:efF:HhI:iL:lnNPpqrS:sTtU:uwW:X:x:y"
enum {
OPT_TSIZE = CHAR_MAX + 1,
OPT_STRIPCR,
@@ -71,6 +72,7 @@ static struct option longopts[] = {
{ "minimal", no_argument, 0, 'd' },
{ "ed", no_argument, 0, 'e' },
{ "forward-ed", no_argument, 0, 'f' },
+ { "show-function-line", required_argument, 0, 'F' },
{ "speed-large-files", no_argument, NULL, 'H' },
{ "ignore-blank-lines", no_argument, 0, 'B' },
{ "ignore-matching-lines", required_argument, 0, 'I' },
@@ -105,6 +107,7 @@ static struct option longopts[] = {
{ NULL, 0, 0, '\0'}
};
+static void checked_regcomp(char const *, regex_t *);
static void usage(void) __dead2;
static void conflicting_format(void) __dead2;
static void push_excludes(char *);
@@ -190,6 +193,12 @@ main(int argc, char **argv)
case 'B':
dflags |= D_SKIPBLANKLINES;
break;
+ case 'F':
+ if (dflags & D_PROTOTYPE)
+ conflicting_format();
+ dflags |= D_MATCHLAST;
+ most_recent_pat = xstrdup(optarg);
+ break;
case 'I':
push_ignore_pats(optarg);
break;
@@ -216,6 +225,8 @@ main(int argc, char **argv)
diff_format = D_NREVERSE;
break;
case 'p':
+ if (dflags & D_MATCHLAST)
+ conflicting_format();
dflags |= D_PROTOTYPE;
break;
case 'P':
@@ -359,19 +370,8 @@ main(int argc, char **argv)
*/
if (argc != 2)
usage();
- if (ignore_pats != NULL) {
- char buf[BUFSIZ];
- int error;
-
- if ((error = regcomp(&ignore_re, ignore_pats,
- REG_NEWLINE | REG_EXTENDED)) != 0) {
- regerror(error, &ignore_re, buf, sizeof(buf));
- if (*ignore_pats != '\0')
- errx(2, "%s: %s", ignore_pats, buf);
- else
- errx(2, "%s", buf);
- }
- }
+ checked_regcomp(ignore_pats, &ignore_re);
+ checked_regcomp(most_recent_pat, &most_recent_re);
if (strcmp(argv[0], "-") == 0) {
fstat(STDIN_FILENO, &stb1);
gotstdin = 1;
@@ -427,6 +427,25 @@ main(int argc, char **argv)
}
static void
+checked_regcomp(char const *pattern, regex_t *comp)
+{
+ char buf[BUFSIZ];
+ int error;
+
+ if (pattern == NULL)
+ return;
+
+ error = regcomp(comp, pattern, REG_NEWLINE | REG_EXTENDED);
+ if (error != 0) {
+ regerror(error, comp, buf, sizeof(buf));
+ if (*pattern != '\0')
+ errx(2, "%s: %s", pattern, buf);
+ else
+ errx(2, "%s", buf);
+ }
+}
+
+static void
set_argstr(char **av, char **ave)
{
size_t argsize;
@@ -548,18 +567,18 @@ usage(void)
(void)fprintf(stderr,
"usage: diff [-aBbdilpTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n"
" [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n"
- " [-I pattern] [-L label] file1 file2\n"
+ " [-I pattern] [-F pattern] [-L label] file1 file2\n"
" diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]\n"
" [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n"
- " -C number file1 file2\n"
+ " [-F pattern] -C number file1 file2\n"
" diff [-aBbdiltw] [-I pattern] [--ignore-case] [--no-ignore-case]\n"
" [--normal] [--strip-trailing-cr] [--tabsize] -D string file1 file2\n"
" diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]\n"
" [--no-ignore-case] [--normal] [--tabsize] [--strip-trailing-cr]\n"
- " -U number file1 file2\n"
+ " [-F pattern] -U number file1 file2\n"
" diff [-aBbdilNPprsTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n"
" [--no-ignore-case] [--normal] [--tabsize] [-I pattern] [-L label]\n"
- " [-S name] [-X file] [-x pattern] dir1 dir2\n"
+ " [-F pattern] [-S name] [-X file] [-x pattern] dir1 dir2\n"
" diff [-aBbditwW] [--expand-tabs] [--ignore-all-blanks]\n"
" [--ignore-blank-lines] [--ignore-case] [--minimal]\n"
" [--no-ignore-file-name-case] [--strip-trailing-cr]\n"
diff --git a/usr.bin/diff/diff.h b/usr.bin/diff/diff.h
index 5164fe22ace4..4a7d19ee8982 100644
--- a/usr.bin/diff/diff.h
+++ b/usr.bin/diff/diff.h
@@ -74,6 +74,7 @@
#define D_IGNOREBLANKS 0x200 /* Ignore white space changes */
#define D_STRIPCR 0x400 /* Strip trailing cr */
#define D_SKIPBLANKLINES 0x800 /* Skip blank lines */
+#define D_MATCHLAST 0x1000 /* Display last line matching provided regex */
/*
* Status values for print_status() and diffreg() return values
@@ -103,12 +104,13 @@ extern bool lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag;
extern bool ignore_file_case, suppress_common, color;
extern int diff_format, diff_context, status;
extern int tabsize, width;
-extern char *start, *ifdefname, *diffargs, *label[2], *ignore_pats;
+extern char *start, *ifdefname, *diffargs, *label[2];
+extern char *ignore_pats, *most_recent_pat;
extern char *group_format;
extern const char *add_code, *del_code;
extern struct stat stb1, stb2;
extern struct excludes *excludes_list;
-extern regex_t ignore_re;
+extern regex_t ignore_re, most_recent_re;
int diffreg(char *, char *, int, int);
void diffdir(char *, char *, int);
diff --git a/usr.bin/diff/diffreg.c b/usr.bin/diff/diffreg.c
index 47c1934a6a65..fc3c3406a073 100644
--- a/usr.bin/diff/diffreg.c
+++ b/usr.bin/diff/diffreg.c
@@ -1407,7 +1407,15 @@ match_function(const long *f, int pos, FILE *fp)
continue;
buf[nc] = '\0';
buf[strcspn(buf, "\n")] = '\0';
- if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
+ if (most_recent_pat != NULL) {
+ int ret = regexec(&most_recent_re, buf, 0, NULL, 0);
+
+ if (ret != 0)
+ continue;
+ strlcpy(lastbuf, buf, sizeof(lastbuf));
+ lastmatchline = pos;
+ return (lastbuf);
+ } else if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
if (begins_with(buf, "private:")) {
if (!state)
state = " (private)";
@@ -1448,7 +1456,7 @@ dump_context_vec(FILE *f1, FILE *f2, int flags)
upd = MIN(len[1], context_vec_ptr->d + diff_context);
printf("***************");
- if ((flags & D_PROTOTYPE)) {
+ if (flags & (D_PROTOTYPE | D_MATCHLAST)) {
f = match_function(ixold, lowa - 1, f1);
if (f != NULL)
printf(" %s", f);
@@ -1555,7 +1563,7 @@ dump_unified_vec(FILE *f1, FILE *f2, int flags)
printf(" +");
uni_range(lowc, upd);
printf(" @@");
- if ((flags & D_PROTOTYPE)) {
+ if (flags & (D_PROTOTYPE | D_MATCHLAST)) {
f = match_function(ixold, lowa - 1, f1);
if (f != NULL)
printf(" %s", f);