📄 sdiff.c
字号:
/* sdiff - side-by-side merge of file differences Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 2001, 2002, 2004 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU DIFF is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include "system.h"#include "paths.h"#include <stdio.h>#include <unlocked-io.h>#include <c-stack.h>#include <dirname.h>#include <error.h>#include <exit.h>#include <exitfail.h>#include <file-type.h>#include <getopt.h>#include <quotesys.h>#include <version-etc.h>#include <xalloc.h>/* Size of chunks read from files which must be parsed into lines. */#define SDIFF_BUFSIZE ((size_t) 65536)char *program_name;static char const *editor_program = DEFAULT_EDITOR_PROGRAM;static char const **diffargv;static char * volatile tmpname;static FILE *tmp;#if HAVE_WORKING_FORK || HAVE_WORKING_VFORKstatic pid_t volatile diffpid;#endifstruct line_filter;static void catchsig (int);static bool edit (struct line_filter *, char const *, lin, lin, struct line_filter *, char const *, lin, lin, FILE *);static bool interact (struct line_filter *, struct line_filter *, char const *, struct line_filter *, char const *, FILE *);static void checksigs (void);static void diffarg (char const *);static void fatal (char const *) __attribute__((noreturn));static void perror_fatal (char const *) __attribute__((noreturn));static void trapsigs (void);static void untrapsig (int);#define NUM_SIGS (sizeof sigs / sizeof *sigs)static int const sigs[] = {#ifdef SIGHUP SIGHUP,#endif#ifdef SIGQUIT SIGQUIT,#endif#ifdef SIGTERM SIGTERM,#endif#ifdef SIGXCPU SIGXCPU,#endif#ifdef SIGXFSZ SIGXFSZ,#endif SIGINT, SIGPIPE};#define handler_index_of_SIGINT (NUM_SIGS - 2)#define handler_index_of_SIGPIPE (NUM_SIGS - 1)#if HAVE_SIGACTION /* Prefer `sigaction' if available, since `signal' can lose signals. */ static struct sigaction initial_action[NUM_SIGS];# define initial_handler(i) (initial_action[i].sa_handler) static void signal_handler (int, void (*) (int));#else static void (*initial_action[NUM_SIGS]) ();# define initial_handler(i) (initial_action[i])# define signal_handler(sig, handler) signal (sig, handler)#endif#if ! HAVE_SIGPROCMASK# define sigset_t int# define sigemptyset(s) (*(s) = 0)# ifndef sigmask# define sigmask(sig) (1 << ((sig) - 1))# endif# define sigaddset(s, sig) (*(s) |= sigmask (sig))# ifndef SIG_BLOCK# define SIG_BLOCK 0# endif# ifndef SIG_SETMASK# define SIG_SETMASK (! SIG_BLOCK)# endif# define sigprocmask(how, n, o) \ ((how) == SIG_BLOCK ? *(o) = sigblock (*(n)) : sigsetmask (*(n)))#endifstatic bool diraccess (char const *);static int temporary_file (void);/* Options: *//* Name of output file if -o specified. */static char const *output;/* Do not print common lines. */static bool suppress_common_lines;/* Value for the long option that does not have single-letter equivalents. */enum{ DIFF_PROGRAM_OPTION = CHAR_MAX + 1, HELP_OPTION, STRIP_TRAILING_CR_OPTION, TABSIZE_OPTION};static struct option const longopts[] ={ {"diff-program", 1, 0, DIFF_PROGRAM_OPTION}, {"expand-tabs", 0, 0, 't'}, {"help", 0, 0, HELP_OPTION}, {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */ {"ignore-blank-lines", 0, 0, 'B'}, {"ignore-case", 0, 0, 'i'}, {"ignore-matching-lines", 1, 0, 'I'}, {"ignore-space-change", 0, 0, 'b'}, {"ignore-tab-expansion", 0, 0, 'E'}, {"left-column", 0, 0, 'l'}, {"minimal", 0, 0, 'd'}, {"output", 1, 0, 'o'}, {"speed-large-files", 0, 0, 'H'}, {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION}, {"suppress-common-lines", 0, 0, 's'}, {"tabsize", 1, 0, TABSIZE_OPTION}, {"text", 0, 0, 'a'}, {"version", 0, 0, 'v'}, {"width", 1, 0, 'w'}, {0, 0, 0, 0}};static void try_help (char const *, char const *) __attribute__((noreturn));static voidtry_help (char const *reason_msgid, char const *operand){ if (reason_msgid) error (0, 0, _(reason_msgid), operand); error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."), program_name); abort ();}static voidcheck_stdout (void){ if (ferror (stdout)) fatal ("write failed"); else if (fclose (stdout) != 0) perror_fatal (_("standard output"));}static char const * const option_help_msgid[] = { N_("-o FILE --output=FILE Operate interactively, sending output to FILE."), "", N_("-i --ignore-case Consider upper- and lower-case to be the same."), N_("-E --ignore-tab-expansion Ignore changes due to tab expansion."), N_("-b --ignore-space-change Ignore changes in the amount of white space."), N_("-W --ignore-all-space Ignore all white space."), N_("-B --ignore-blank-lines Ignore changes whose lines are all blank."), N_("-I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE."), N_("--strip-trailing-cr Strip trailing carriage return on input."), N_("-a --text Treat all files as text."), "", N_("-w NUM --width=NUM Output at most NUM (default 130) print columns."), N_("-l --left-column Output only the left column of common lines."), N_("-s --suppress-common-lines Do not output common lines."), "", N_("-t --expand-tabs Expand tabs to spaces in output."), N_("--tabsize=NUM Tab stops are every NUM (default 8) print columns."), "", N_("-d --minimal Try hard to find a smaller set of changes."), N_("-H --speed-large-files Assume large files and many scattered small changes."), N_("--diff-program=PROGRAM Use PROGRAM to compare files."), "", N_("-v --version Output version info."), N_("--help Output this help."), 0};static voidusage (void){ char const * const *p; printf (_("Usage: %s [OPTION]... FILE1 FILE2\n"), program_name); printf ("%s\n\n", _("Side-by-side merge of file differences.")); for (p = option_help_msgid; *p; p++) if (**p) printf (" %s\n", _(*p)); else putchar ('\n'); printf ("\n%s\n%s\n\n%s\n", _("If a FILE is `-', read standard input."), _("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."), _("Report bugs to <bug-gnu-utils@gnu.org>."));}/* Clean up after a signal or other failure. This function is async-signal-safe. */static voidcleanup (int signo __attribute__((unused))){#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK if (0 < diffpid) kill (diffpid, SIGPIPE);#endif if (tmpname) unlink (tmpname);}static void exiterr (void) __attribute__((noreturn));static voidexiterr (void){ cleanup (0); untrapsig (0); checksigs (); exit (EXIT_TROUBLE);}static voidfatal (char const *msgid){ error (0, 0, "%s", _(msgid)); exiterr ();}static voidperror_fatal (char const *msg){ int e = errno; checksigs (); error (0, e, "%s", msg); exiterr ();}static voidcheck_child_status (int werrno, int wstatus, int max_ok_status, char const *subsidiary_program){ int status = (! werrno && WIFEXITED (wstatus) ? WEXITSTATUS (wstatus) : INT_MAX); if (max_ok_status < status) { error (0, werrno, _(status == 126 ? "subsidiary program `%s' could not be invoked" : status == 127 ? "subsidiary program `%s' not found" : status == INT_MAX ? "subsidiary program `%s' failed" : "subsidiary program `%s' failed (exit status %d)"), subsidiary_program, status); exiterr (); }}static FILE *ck_fopen (char const *fname, char const *type){ FILE *r = fopen (fname, type); if (! r) perror_fatal (fname); return r;}static voidck_fclose (FILE *f){ if (fclose (f)) perror_fatal ("fclose");}static size_tck_fread (char *buf, size_t size, FILE *f){ size_t r = fread (buf, sizeof (char), size, f); if (r == 0 && ferror (f)) perror_fatal (_("read failed")); return r;}static voidck_fwrite (char const *buf, size_t size, FILE *f){ if (fwrite (buf, sizeof (char), size, f) != size) perror_fatal (_("write failed"));}static voidck_fflush (FILE *f){ if (fflush (f) != 0) perror_fatal (_("write failed"));}static char const *expand_name (char *name, bool is_dir, char const *other_name){ if (strcmp (name, "-") == 0) fatal ("cannot interactively merge standard input"); if (! is_dir) return name; else { /* Yield NAME/BASE, where BASE is OTHER_NAME's basename. */ char const *base = base_name (other_name); size_t namelen = strlen (name), baselen = strlen (base); bool insert_slash = *base_name (name) && name[namelen - 1] != '/'; char *r = xmalloc (namelen + insert_slash + baselen + 1); memcpy (r, name, namelen); r[namelen] = '/'; memcpy (r + namelen + insert_slash, base, baselen + 1); return r; }}struct line_filter { FILE *infile; char *bufpos; char *buffer; char *buflim;};static voidlf_init (struct line_filter *lf, FILE *infile){ lf->infile = infile; lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1); lf->buflim[0] = '\n';}/* Fill an exhausted line_filter buffer from its INFILE */static size_tlf_refill (struct line_filter *lf){ size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile); lf->bufpos = lf->buffer; lf->buflim = lf->buffer + s; lf->buflim[0] = '\n'; checksigs (); return s;}/* Advance LINES on LF's infile, copying lines to OUTFILE */static voidlf_copy (struct line_filter *lf, lin lines, FILE *outfile){ char *start = lf->bufpos; while (lines) { lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos); if (! lf->bufpos) { ck_fwrite (start, lf->buflim - start, outfile); if (! lf_refill (lf)) return; start = lf->bufpos; } else { --lines; ++lf->bufpos; } } ck_fwrite (start, lf->bufpos - start, outfile);}/* Advance LINES on LF's infile without doing output */static voidlf_skip (struct line_filter *lf, lin lines){ while (lines) { lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos); if (! lf->bufpos) { if (! lf_refill (lf)) break; } else { --lines; ++lf->bufpos; } }}/* Snarf a line into a buffer. Return EOF if EOF, 0 if error, 1 if OK. */static intlf_snarf (struct line_filter *lf, char *buffer, size_t bufsize){ for (;;) { char *start = lf->bufpos; char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start); size_t s = next - start; if (bufsize <= s) return 0; memcpy (buffer, start, s); if (next < lf->buflim) { buffer[s] = 0; lf->bufpos = next + 1; return 1; } if (! lf_refill (lf)) return s ? 0 : EOF; buffer += s; bufsize -= s; }}intmain (int argc, char *argv[]){ int opt; char const *prog; exit_failure = EXIT_TROUBLE; initialize_main (&argc, &argv); program_name = argv[0]; setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); c_stack_action (cleanup); prog = getenv ("EDITOR"); if (prog) editor_program = prog; diffarg (DEFAULT_DIFF_PROGRAM); /* parse command line args */ while ((opt = getopt_long (argc, argv, "abBdEHiI:lo:stvw:W", longopts, 0)) != -1) { switch (opt) { case 'a': diffarg ("-a"); break; case 'b': diffarg ("-b"); break; case 'B': diffarg ("-B"); break; case 'd': diffarg ("-d"); break; case 'E': diffarg ("-E"); break; case 'H': diffarg ("-H"); break; case 'i': diffarg ("-i"); break; case 'I': diffarg ("-I"); diffarg (optarg); break; case 'l': diffarg ("--left-column"); break; case 'o': output = optarg; break; case 's': suppress_common_lines = true; break; case 't': diffarg ("-t"); break; case 'v': version_etc (stdout, "sdiff", PACKAGE_NAME, PACKAGE_VERSION, "Thomas Lord", (char *) 0); check_stdout (); return EXIT_SUCCESS; case 'w': diffarg ("-W"); diffarg (optarg); break; case 'W': diffarg ("-w"); break; case DIFF_PROGRAM_OPTION: diffargv[0] = optarg; break; case HELP_OPTION: usage (); check_stdout (); return EXIT_SUCCESS; case STRIP_TRAILING_CR_OPTION: diffarg ("--strip-trailing-cr"); break; case TABSIZE_OPTION: diffarg ("--tabsize"); diffarg (optarg); break; default: try_help (0, 0); } } if (argc - optind != 2) { if (argc - optind < 2) try_help ("missing operand after `%s'", argv[argc - 1]); else try_help ("extra operand `%s'", argv[optind + 2]); } if (! output) { /* easy case: diff does everything for us */ if (suppress_common_lines) diffarg ("--suppress-common-lines"); diffarg ("-y"); diffarg ("--"); diffarg (argv[optind]); diffarg (argv[optind + 1]); diffarg (0); execvp (diffargv[0], (char **) diffargv); perror_fatal (diffargv[0]); } else { char const *lname, *rname; FILE *left, *right, *out, *diffout; bool interact_ok; struct line_filter lfilt; struct line_filter rfilt; struct line_filter diff_filt; bool leftdir = diraccess (argv[optind]); bool rightdir = diraccess (argv[optind + 1]); if (leftdir & rightdir) fatal ("both files to be compared are directories"); lname = expand_name (argv[optind], leftdir, argv[optind + 1]); left = ck_fopen (lname, "r"); rname = expand_name (argv[optind + 1], rightdir, argv[optind]); right = ck_fopen (rname, "r"); out = ck_fopen (output, "w"); diffarg ("--sdiff-merge-assist"); diffarg ("--"); diffarg (argv[optind]); diffarg (argv[optind + 1]); diffarg (0); trapsigs ();#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) { size_t cmdsize = 1; char *p, *command;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -