Index: usr.bin/diff/diff.h =================================================================== --- usr.bin/diff/diff.h +++ usr.bin/diff/diff.h @@ -72,6 +72,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 @@ -94,11 +95,12 @@ extern int diff_format, diff_context, status, ignore_file_case; extern int suppress_common; 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 struct stat stb1, stb2; extern struct excludes *excludes_list; -extern regex_t ignore_re; +extern regex_t ignore_re, most_recent_re; char *splice(char *, char *); int diffreg(char *, char *, int, int); Index: usr.bin/diff/diff.1 =================================================================== --- usr.bin/diff/diff.1 +++ usr.bin/diff/diff.1 @@ -64,11 +64,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 -changed-group-format Ar GFMT @@ -120,6 +122,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 -changed-group-format Ar GFMT @@ -175,6 +178,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 @@ -336,6 +340,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 Index: usr.bin/diff/diff.c =================================================================== --- usr.bin/diff/diff.c +++ usr.bin/diff/diff.c @@ -41,13 +41,14 @@ int lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag, Wflag; int diff_format, diff_context, status, ignore_file_case, suppress_common; int tabsize = 8, width = 130; -char *start, *ifdefname, *diffargs, *label[2], *ignore_pats; +char *start, *ifdefname, *diffargs, *label[2]; +char *ignore_pats, *most_recent_pat; char *group_format = NULL; 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, @@ -67,6 +68,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' }, @@ -100,6 +102,7 @@ { NULL, 0, 0, '\0'} }; +void check_regex(char const *, regex_t *); void usage(void) __dead2; void conflicting_format(void) __dead2; void push_excludes(char *); @@ -184,6 +187,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; @@ -210,6 +219,8 @@ diff_format = D_NREVERSE; break; case 'p': + if (dflags & D_MATCHLAST) + conflicting_format(); dflags |= D_PROTOTYPE; break; case 'P': @@ -327,19 +338,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); - } - } + check_regex(ignore_pats, &ignore_re); + check_regex(most_recent_pat, &most_recent_re); if (strcmp(argv[0], "-") == 0) { fstat(STDIN_FILENO, &stb1); gotstdin = 1; @@ -394,6 +394,25 @@ exit(status); } +void +check_regex(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); + } +} + void set_argstr(char **av, char **ave) { @@ -522,18 +541,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" Index: usr.bin/diff/diffreg.c =================================================================== --- usr.bin/diff/diffreg.c +++ usr.bin/diff/diffreg.c @@ -1402,43 +1402,49 @@ match_function(const long *f, int pos, FILE *fp) { unsigned char buf[FUNCTION_CONTEXT_SIZE]; - size_t nc; + size_t nc, read; int last = lastline; const char *state = NULL; lastline = pos; - while (pos > last) { + for (; pos > last; pos--) { fseek(fp, f[pos - 1], SEEK_SET); nc = f[pos] - f[pos - 1]; if (nc >= sizeof(buf)) nc = sizeof(buf) - 1; - nc = fread(buf, 1, nc, fp); - if (nc > 0) { - buf[nc] = '\0'; - buf[strcspn(buf, "\n")] = '\0'; - if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') { - if (begins_with(buf, "private:")) { - if (!state) - state = " (private)"; - } else if (begins_with(buf, "protected:")) { - if (!state) - state = " (protected)"; - } else if (begins_with(buf, "public:")) { - if (!state) - state = " (public)"; - } else { - strlcpy(lastbuf, buf, sizeof lastbuf); - if (state) - strlcat(lastbuf, state, - sizeof lastbuf); - lastmatchline = pos; - return lastbuf; - } + read = fread(buf, 1, nc, fp); + if (read < nc) + break; + + buf[nc] = '\0'; + buf[strcspn(buf, "\n")] = '\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)"; + } else if (begins_with(buf, "protected:")) { + if (!state) + state = " (protected)"; + } else if (begins_with(buf, "public:")) { + if (!state) + state = " (public)"; + } else { + strlcpy(lastbuf, buf, sizeof(lastbuf)); + if (state) + strlcat(lastbuf, state, sizeof(lastbuf)); + lastmatchline = pos; + return (lastbuf); } } - pos--; } - return lastmatchline > 0 ? lastbuf : NULL; + return (lastmatchline > 0 ? lastbuf : NULL); } /* dump accumulated "context" diff changes */ @@ -1460,7 +1466,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); @@ -1567,7 +1573,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);