diff --git a/usr.bin/diff/diff.h b/usr.bin/diff/diff.h --- 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 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/diff.1 b/usr.bin/diff/diff.1 --- 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 @@ 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 --- a/usr.bin/diff/diff.c +++ b/usr.bin/diff/diff.c @@ -43,14 +43,15 @@ 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 @@ { "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 @@ { 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 @@ 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 @@ diff_format = D_NREVERSE; break; case 'p': + if (dflags & D_MATCHLAST) + conflicting_format(); dflags |= D_PROTOTYPE; break; case 'P': @@ -359,19 +370,8 @@ */ 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; @@ -426,6 +426,25 @@ exit(status); } +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) { @@ -548,18 +567,18 @@ (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/diffreg.c b/usr.bin/diff/diffreg.c --- a/usr.bin/diff/diffreg.c +++ b/usr.bin/diff/diffreg.c @@ -1407,7 +1407,15 @@ 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 @@ 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 @@ 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);