diff --git a/lib/Makefile b/lib/Makefile --- a/lib/Makefile +++ b/lib/Makefile @@ -16,6 +16,7 @@ libc++ \ libc++experimental \ libcxxrt \ + libdiff \ libelf \ libssp \ libssp_nonshared \ diff --git a/lib/libdiff/Makefile b/lib/libdiff/Makefile new file mode 100644 --- /dev/null +++ b/lib/libdiff/Makefile @@ -0,0 +1,15 @@ +LIB= diff +INTERNALLIB= # API not published or supported. + +.PATH: ${SRCTOP}/contrib/libdiff/compat +.PATH: ${SRCTOP}/contrib/libdiff/lib + +SRCS= diff_atomize_text.c diff_main.c diff_myers.c \ + diff_patience.c diff_output.c diff_output_plain.c \ + diff_output_unidiff.c diff_output_edscript.c recallocarray.c + +WARNS= +CFLAGS+= -I${SRCTOP}/contrib/libdiff/compat/include +CFLAGS+= -I${SRCTOP}/contrib/libdiff/include + +.include diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -43,6 +43,7 @@ bsnmptools \ c_nossp_pic \ cron \ + diff \ elftc \ fdt \ fifolog \ @@ -544,6 +545,9 @@ _LIB_OBJTOP?= ${OBJTOP} # INTERNALLIB definitions. +LIBDIFFDIR= ${_LIB_OBJTOP}/lib/libdiff +LIBDIFF?= ${LIBDIFFDIR}/libdiff${PIE_SUFFIX}.a + LIBELFTCDIR= ${_LIB_OBJTOP}/lib/libelftc LIBELFTC?= ${LIBELFTCDIR}/libelftc${PIE_SUFFIX}.a diff --git a/usr.bin/diff/Makefile b/usr.bin/diff/Makefile --- a/usr.bin/diff/Makefile +++ b/usr.bin/diff/Makefile @@ -1,9 +1,10 @@ - .include PROG= diff -SRCS= diff.c diffdir.c diffreg.c xmalloc.c pr.c -LIBADD= m +SRCS= diff.c diffdir.c diffreg.c xmalloc.c pr.c diffreg_new.c + +LIBADD= m diff +CFLAGS+= -I${.CURDIR} -I${SRCTOP}/contrib/libdiff/lib -I${SRCTOP}/contrib/libdiff/include HAS_TESTS= SUBDIR.${MK_TESTS}+= tests 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 @@ -51,6 +51,14 @@ #define D_UNSET -2 +/* + * Algorithms + */ + +#define D_DIFFNONE 0 +#define D_DIFFSTONE 1 /* Stone or 'old diff' algorithm */ +#define D_DIFFMYERS 2 /* Myers diff algorithm */ +#define D_DIFFPATIENCE 3 /* Patience diff algorithm */ /* * Output flags @@ -73,6 +81,9 @@ #define D_SKIPBLANKLINES 0x800 /* Skip blank lines */ #define D_MATCHLAST 0x1000 /* Display last line matching provided regex */ +/* Features supported by new algorithms */ +#define D_NEWALGO_FLAGS (D_FORCEASCII | D_PROTOTYPE | D_IGNOREBLANKS) + /* * Status values for print_status() and diffreg() return values */ @@ -98,8 +109,9 @@ }; extern bool lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag; -extern bool ignore_file_case, suppress_common, color, noderef; -extern int diff_format, diff_context, status; +extern bool ignore_file_case, suppress_common, color, noderef, algorithm_set; +extern int diff_format, diff_context, diff_algorithm, status; +extern bool diff_algorithm_set; extern int tabsize, width; extern char *start, *ifdefname, *diffargs, *label[2]; extern char *ignore_pats, *most_recent_pat; @@ -110,5 +122,7 @@ extern regex_t ignore_re, most_recent_re; int diffreg(char *, char *, int, int); +int diffreg_new(char *, char *, int, int); +bool can_libdiff(int); void diffdir(char *, char *, int); void print_status(int, char *, char *, const char *); 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 @@ -27,7 +27,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 26, 2024 +.Dd March 7, 2024 .Dt DIFF 1 .Os .Sh NAME @@ -40,6 +40,7 @@ .Fl c | e | f | .Fl n | q | u | y .Oc +.Op Fl A Ar algo | Fl -algorithm Ar algo .Op Fl -brief .Op Fl -color Ns = Ns Ar when .Op Fl -changed-group-format Ar GFMT @@ -67,6 +68,7 @@ .Ar file1 file2 .Nm diff .Op Fl aBbdilpTtw +.Op Fl A Ar algo | Fl -algorithm Ar algo .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 @@ -95,6 +97,7 @@ .Ar file1 file2 .Nm diff .Op Fl aBbdiltw +.Op Fl A Ar algo | Fl -algorithm Ar algo .Op Fl I Ar pattern | Fl -ignore-matching-lines Ar pattern .Op Fl -brief .Op Fl -color Ns = Ns Ar when @@ -121,6 +124,7 @@ .Ar file1 file2 .Nm diff .Op Fl aBbdilpTtw +.Op Fl A Ar algo | Fl -algorithm Ar algo .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 @@ -153,6 +157,7 @@ .Fl c | e | f | .Fl n | q | u .Oc +.Op Fl A Ar algo | Fl -algorithm Ar algo .Op Fl -brief .Op Fl -color Ns = Ns Ar when .Op Fl -changed-group-format Ar GFMT @@ -276,6 +281,18 @@ .Ar dir1 to their state in .Ar dir2 . +Note that when comparing directories with +.Fl e , +the resulting file may no longer be interpreted as an +.Xr ed 1 +script. +Output is added to indicate which file each set of +.Xr ed 1 +commands applies to. +These hunks can be manually extracted to produce an +.Xr ed 1 +script, which can also be applied with +.Xr patch 1 . .It Fl f -forward-ed Identical output to that of the .Fl e @@ -331,6 +348,38 @@ .Pp Comparison options: .Bl -tag -width Ds +.It Fl A Ar algo, Fl -algorithm Ar algo +Configure the algorithm used when comparing files. +.Nm +supports 3 algorithms: +.Pp +.Bl -tag -width Ds -compact +.It Cm myers +The Myers diff algorithm finds the shortest edit which transforms one +input into the other. +It generally runs in O(N+D\(S2) time, requiring O(N) space, where N is +the sum of the lengths of the inputs and D is the length of the +difference between them, with a theoretical O(N\(pcD) worst case. +If it encounters worst-case input, the implementation used by +.Nm +falls back to a less optimal but faster algorithm. +.It Cm patience +The Patience variant of the Myers algorithm attempts to create more +aesthetically pleasing diff output by logically grouping lines. +.It Cm stone +The Stone algorithm (commonly known as Hunt-McIlroy or Hunt-Szymanski) +looks for the longest common subsequence between compared files. +Stone encounters worst case performance when there are long common +subsequences. +In large files this can lead to a significant performance impact. +The Stone algorithm is maintained for compatibility. +.El +.Pp +The +.Nm +utility defaults to the Myers algorithm, but will fall back to the +Stone algorithm if the input or output options are not supported by +the Myers implementation. .It Fl a -text Treat all files as ASCII text. Normally @@ -741,7 +790,7 @@ specification. .Pp The flags -.Op Fl aDdIiLlNnPpqSsTtwXxy +.Op Fl AaDdIiLlNnPpqSsTtwXxy are extensions to that specification. .Sh HISTORY A @@ -759,3 +808,6 @@ by a BSD-licensed implementation written by .An Todd Miller . Some GNUisms were lost in the process. +.Pp +libdiff was imported from the Game of Trees version control system and default +algorithm was changed to Myers for FreeBSD 15. 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 @@ -20,7 +20,6 @@ * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ -#include #include #include @@ -36,11 +35,12 @@ #include "diff.h" #include "xmalloc.h" -static const char diff_version[] = "FreeBSD diff 20220309"; +static const char diff_version[] = "FreeBSD diff 20240307"; bool lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag; bool ignore_file_case, suppress_common, color, noderef; static bool help = false; -int diff_format, diff_context, status; +int diff_format, diff_context, diff_algorithm, status; +bool diff_algorithm_set; int tabsize = 8, width = 130; static int colorflag = COLORFLAG_NEVER; char *start, *ifdefname, *diffargs, *label[2]; @@ -51,7 +51,17 @@ struct excludes *excludes_list; regex_t ignore_re, most_recent_re; -#define OPTIONS "0123456789aBbC:cdD:efF:HhI:iL:lnNPpqrS:sTtU:uwW:X:x:y" +static struct algorithm { + const char *name; + int id; +} algorithms[] = { + {"stone", D_DIFFSTONE}, + {"myers", D_DIFFMYERS}, + {"patience", D_DIFFPATIENCE}, + {NULL, D_DIFFNONE} +}; + +#define OPTIONS "0123456789A:aBbC:cdD:efF:HhI:iL:lnNPpqrS:sTtU:uwW:X:x:y" enum { OPT_TSIZE = CHAR_MAX + 1, OPT_STRIPCR, @@ -68,6 +78,7 @@ }; static struct option longopts[] = { + { "algorithm", required_argument, 0, 'A' }, { "text", no_argument, 0, 'a' }, { "ignore-space-change", no_argument, 0, 'b' }, { "context", optional_argument, 0, 'C' }, @@ -139,6 +150,8 @@ newarg = 1; diff_context = 3; diff_format = D_UNSET; + diff_algorithm = D_DIFFMYERS; + diff_algorithm_set = false; #define FORMAT_MISMATCHED(type) \ (diff_format != D_UNSET && diff_format != (type)) while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) { @@ -153,6 +166,21 @@ usage(); diff_context = (diff_context * 10) + (ch - '0'); break; + case 'A': + diff_algorithm = D_DIFFNONE; + for (struct algorithm *a = algorithms; a->name;a++) { + if(strcasecmp(optarg, a->name) == 0) { + diff_algorithm = a->id; + diff_algorithm_set = true; + break; + } + } + + if (diff_algorithm == D_DIFFNONE) { + printf("unknown algorithm: %s\n", optarg); + usage(); + } + break; case 'a': dflags |= D_FORCEASCII; break; @@ -276,8 +304,10 @@ break; case 'W': width = (int) strtonum(optarg, 1, INT_MAX, &errstr); - if (errstr) - errx(1, "width is %s: %s", errstr, optarg); + if (errstr) { + warnx("Invalid argument for width"); + usage(); + } break; case 'X': read_excludes_file(optarg); @@ -315,8 +345,10 @@ break; case OPT_TSIZE: tabsize = (int) strtonum(optarg, 1, INT_MAX, &errstr); - if (errstr) - errx(1, "tabsize is %s: %s", errstr, optarg); + if (errstr) { + warnx("Invalid argument for tabsize"); + usage(); + } break; case OPT_STRIPCR: dflags |= D_STRIPCR; @@ -437,6 +469,8 @@ print_status(diffreg(argv[0], argv[1], dflags, 1), argv[0], argv[1], ""); } + if (fflush(stdout) != 0) + err(2, "stdout"); exit(status); } diff --git a/usr.bin/diff/diffdir.c b/usr.bin/diff/diffdir.c --- a/usr.bin/diff/diffdir.c +++ b/usr.bin/diff/diffdir.c @@ -20,7 +20,6 @@ * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ -#include #include #include 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 @@ -72,6 +72,7 @@ #include #include #include +#include #include #include #include @@ -166,6 +167,7 @@ enum readhash { RH_BINARY, RH_OK, RH_EOF }; +static int diffreg_stone(char *, char *, int, int); static FILE *opentemp(const char *); static void output(char *, FILE *, char *, FILE *, int); static void check(FILE *, FILE *, int); @@ -205,7 +207,7 @@ static int pref, suff; /* length of prefix and suffix */ static int slen[2]; static int anychange; -static int hw, lpad, rpad; /* 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 */ @@ -222,6 +224,32 @@ static int lastline; static int lastmatchline; +int +diffreg(char *file1, char *file2, int flags, int capsicum) +{ + /* + * If we have set the algorithm with -A or --algorithm use that if we + * can and if not print an error. + */ + if (diff_algorithm_set) { + if (diff_algorithm == D_DIFFMYERS || + diff_algorithm == D_DIFFPATIENCE) { + if (can_libdiff(flags)) + return diffreg_new(file1, file2, flags, capsicum); + else + errx(2, "cannot use Myers algorithm with selected options"); + } else { + /* Fallback to using stone. */ + return diffreg_stone(file1, file2, flags, capsicum); + } + } else { + if (can_libdiff(flags)) + return diffreg_new(file1, file2, flags, capsicum); + else + return diffreg_stone(file1, file2, flags, capsicum); + } +} + static int clow2low(int c) { @@ -237,7 +265,7 @@ } int -diffreg(char *file1, char *file2, int flags, int capsicum) +diffreg_stone(char *file1, char *file2, int flags, int capsicum) { FILE *f1, *f2; int i, rval; @@ -726,10 +754,10 @@ * in one file for -b or -w. */ if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS)) { - if (c == EOF && d == '\n') { + if (c == EOF && isspace(d)) { ctnew++; break; - } else if (c == '\n' && d == EOF) { + } else if (isspace(c) && d == EOF) { ctold++; break; } @@ -1219,6 +1247,7 @@ edoffset = 0; nc = 0; + col = 0; /* * When doing #ifdef's, copy down to current line * if this is the first file, so that stuff makes it to output. @@ -1284,6 +1313,10 @@ 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') { /* * Calculate where the tab would bring us. diff --git a/usr.bin/diff/diffreg_new.c b/usr.bin/diff/diffreg_new.c new file mode 100644 --- /dev/null +++ b/usr.bin/diff/diffreg_new.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2018 Martin Pieuchot + * Copyright (c) 2020 Neels Hofmeyr + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diff.h" +#include +#include +#include + +const char *format_label(const char *, struct stat *); + +enum diffreg_algo { + DIFFREG_ALGO_MYERS_THEN_MYERS_DIVIDE = 0, + DIFFREG_ALGO_MYERS_THEN_PATIENCE = 1, + DIFFREG_ALGO_PATIENCE = 2, + DIFFREG_ALGO_NONE = 3, +}; + +int diffreg_new(char *, char *, int, int); +FILE * openfile(const char *, char **, struct stat *); + +static const struct diff_algo_config myers_then_patience; +static const struct diff_algo_config myers_then_myers_divide; +static const struct diff_algo_config patience; +static const struct diff_algo_config myers_divide; + +static const struct diff_algo_config myers_then_patience = (struct diff_algo_config){ + .impl = diff_algo_myers, + .permitted_state_size = 1024 * 1024 * sizeof(int), + .fallback_algo = &patience, +}; + +static const struct diff_algo_config myers_then_myers_divide = + (struct diff_algo_config){ + .impl = diff_algo_myers, + .permitted_state_size = 1024 * 1024 * sizeof(int), + .fallback_algo = &myers_divide, +}; + +static const struct diff_algo_config patience = (struct diff_algo_config){ + .impl = diff_algo_patience, + /* After subdivision, do Patience again: */ + .inner_algo = &patience, + /* If subdivision failed, do Myers Divide et Impera: */ + .fallback_algo = &myers_then_myers_divide, +}; + +static const struct diff_algo_config myers_divide = (struct diff_algo_config){ + .impl = diff_algo_myers_divide, + /* When division succeeded, start from the top: */ + .inner_algo = &myers_then_myers_divide, + /* (fallback_algo = NULL implies diff_algo_none). */ +}; + +static const struct diff_algo_config no_algo = (struct diff_algo_config){ + .impl = diff_algo_none, +}; + +/* If the state for a forward-Myers is small enough, use Myers, otherwise first + * do a Myers-divide. */ +static const struct diff_config diff_config_myers_then_myers_divide = { + .atomize_func = diff_atomize_text_by_line, + .algo = &myers_then_myers_divide, +}; + +/* If the state for a forward-Myers is small enough, use Myers, otherwise first + * do a Patience. */ +static const struct diff_config diff_config_myers_then_patience = { + .atomize_func = diff_atomize_text_by_line, + .algo = &myers_then_patience, +}; + +/* Directly force Patience as a first divider of the source file. */ +static const struct diff_config diff_config_patience = { + .atomize_func = diff_atomize_text_by_line, + .algo = &patience, +}; + +/* Directly force Patience as a first divider of the source file. */ +static const struct diff_config diff_config_no_algo = { + .atomize_func = diff_atomize_text_by_line, +}; + +const char * +format_label(const char *oldlabel, struct stat *stb) +{ + const char *time_format = "%Y-%m-%d %H:%M:%S"; + char *newlabel; + char buf[256]; + char end[10]; + struct tm tm, *tm_ptr; + int nsec = stb->st_mtim.tv_nsec; + size_t newlabellen, timelen, endlen; + tm_ptr = localtime_r(&stb->st_mtime, &tm); + + timelen = strftime(buf, 256, time_format, tm_ptr); + endlen = strftime(end, 10, "%z", tm_ptr); + + /* + * The new label is the length of the time, old label, timezone, + * 9 characters for nanoseconds, and 4 characters for a period + * and for formatting. + */ + newlabellen = timelen + strlen(oldlabel) + endlen + 9 + 4; + newlabel = calloc(newlabellen, sizeof(char)); + + snprintf(newlabel, newlabellen ,"%s\t%s.%.9d %s\n", + oldlabel, buf, nsec, end); + + return newlabel; +} + +int +diffreg_new(char *file1, char *file2, int flags, int capsicum) +{ + char *str1, *str2; + FILE *f1, *f2; + struct stat st1, st2; + struct diff_input_info info; + struct diff_data left = {}, right = {}; + struct diff_result *result = NULL; + bool force_text, have_binary; + int rc, atomizer_flags, rflags, diff_flags = 0; + int context_lines = diff_context; + const struct diff_config *cfg; + enum diffreg_algo algo; + cap_rights_t rights_ro; + + algo = DIFFREG_ALGO_MYERS_THEN_MYERS_DIVIDE; + + switch (algo) { + default: + case DIFFREG_ALGO_MYERS_THEN_MYERS_DIVIDE: + cfg = &diff_config_myers_then_myers_divide; + break; + case DIFFREG_ALGO_MYERS_THEN_PATIENCE: + cfg = &diff_config_myers_then_patience; + break; + case DIFFREG_ALGO_PATIENCE: + cfg = &diff_config_patience; + break; + case DIFFREG_ALGO_NONE: + cfg = &diff_config_no_algo; + break; + } + + f1 = openfile(file1, &str1, &st1); + f2 = openfile(file2, &str2, &st2); + + if (capsicum) { + cap_rights_init(&rights_ro, CAP_READ, CAP_FSTAT, CAP_SEEK); + if (caph_rights_limit(fileno(f1), &rights_ro) < 0) + err(2, "unable to limit rights on: %s", file1); + if (caph_rights_limit(fileno(f2), &rights_ro) < 0) + err(2, "unable to limit rights on: %s", file2); + if (fileno(f1) == STDIN_FILENO || fileno(f2) == STDIN_FILENO) { + /* stdin has already been limited */ + if (caph_limit_stderr() == -1) + err(2, "unable to limit stderr"); + if (caph_limit_stdout() == -1) + err(2, "unable to limit stdout"); + } else if (caph_limit_stdio() == -1) + err(2, "unable to limit stdio"); + caph_cache_catpages(); + caph_cache_tzdata(); + if (caph_enter() < 0) + err(2, "unable to enter capability mode"); + } + /* + * If we have been given a label use that for the paths, if not format + * the path with the files modification time. + */ + info.flags = 0; + info.left_path = (label[0] != NULL) ? + label[0] : format_label(file1, &stb1); + info.right_path = (label[1] != NULL) ? + label[1] : format_label(file2, &stb2); + + if (flags & D_FORCEASCII) + diff_flags |= DIFF_FLAG_FORCE_TEXT_DATA; + if (flags & D_IGNOREBLANKS) + diff_flags |= DIFF_FLAG_IGNORE_WHITESPACE; + if (flags & D_PROTOTYPE) + diff_flags |= DIFF_FLAG_SHOW_PROTOTYPES; + + if (diff_atomize_file(&left, cfg, f1, (uint8_t *)str1, st1.st_size, diff_flags)) { + rc = D_ERROR; + goto done; + } + if (diff_atomize_file(&right, cfg, f2, (uint8_t *)str2, st2.st_size, diff_flags)) { + rc = D_ERROR; + goto done; + } + + result = diff_main(cfg, &left, &right); + if (result->rc != DIFF_RC_OK) { + rc = D_ERROR; + status |= 2; + goto done; + } + /* + * If there wasn't an error, but we don't have any printable chunks + * then the files must match. + */ + if (!diff_result_contains_printable_chunks(result)) { + rc = D_SAME; + goto done; + } + + atomizer_flags = (result->left->atomizer_flags | result->right->atomizer_flags); + rflags = (result->left->root->diff_flags | result->right->root->diff_flags); + force_text = (rflags & DIFF_FLAG_FORCE_TEXT_DATA); + have_binary = (atomizer_flags & DIFF_ATOMIZER_FOUND_BINARY_DATA); + + if (have_binary && !force_text) { + rc = D_BINARY; + status |= 1; + goto done; + } + + if (diff_format == D_NORMAL) { + rc = diff_output_plain(NULL, stdout, &info, result, false); + } else if (diff_format == D_EDIT) { + rc = diff_output_edscript(NULL, stdout, &info, result); + } else { + rc = diff_output_unidiff(NULL, stdout, &info, result, + context_lines); + } + if (rc != DIFF_RC_OK) { + rc = D_ERROR; + status |= 2; + } else { + rc = D_DIFFER; + status |= 1; + } +done: + diff_result_free(result); + diff_data_free(&left); + diff_data_free(&right); + if (str1) + munmap(str1, st1.st_size); + if (str2) + munmap(str2, st2.st_size); + fclose(f1); + fclose(f2); + + return rc; +} + +FILE * +openfile(const char *path, char **p, struct stat *st) +{ + FILE *f = NULL; + + if (strcmp(path, "-") == 0) + f = stdin; + else + f = fopen(path, "r"); + + if (f == NULL) + err(2, "%s", path); + + if (fstat(fileno(f), st) == -1) + err(2, "%s", path); + +#ifndef DIFF_NO_MMAP + *p = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0); + if (*p == MAP_FAILED) +#endif + *p = NULL; /* fall back on file I/O */ + + return f; +} + +bool +can_libdiff(int flags) +{ + /* We can't use fifos with libdiff yet */ + if (S_ISFIFO(stb1.st_mode) || S_ISFIFO(stb2.st_mode)) + return false; + + /* Is this one of the supported input/output modes for diffreg_new? */ + if ((flags == 0 || !(flags & ~D_NEWALGO_FLAGS)) && + ignore_pats == NULL && ( + diff_format == D_NORMAL || +#if 0 + diff_format == D_EDIT || +#endif + diff_format == D_UNIFIED) && + (diff_algorithm == D_DIFFMYERS || diff_algorithm == D_DIFFPATIENCE)) { + return true; + } + + /* Fallback to using stone. */ + return false; +} diff --git a/usr.bin/diff/pr.c b/usr.bin/diff/pr.c --- a/usr.bin/diff/pr.c +++ b/usr.bin/diff/pr.c @@ -24,7 +24,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include diff --git a/usr.bin/diff/tests/diff_test.sh b/usr.bin/diff/tests/diff_test.sh --- a/usr.bin/diff/tests/diff_test.sh +++ b/usr.bin/diff/tests/diff_test.sh @@ -266,6 +266,10 @@ { printf "\tA\n" > A printf "\tB\n" > B + atf_check -s exit:0 -o match:"are identical" \ + diff -s A A + atf_check -s exit:1 -o not-match:"are identical" \ + diff -s A B chmod -r B atf_check -s exit:2 -e inline:"diff: B: Permission denied\n" \ -o empty diff -s A B diff --git a/usr.bin/diff/xmalloc.c b/usr.bin/diff/xmalloc.c --- a/usr.bin/diff/xmalloc.c +++ b/usr.bin/diff/xmalloc.c @@ -13,7 +13,6 @@ * called by a name other than "ssh" or "Secure Shell". */ -#include #include #include #include