⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 diff.c

📁 WinMerge可以显示两个文件的不同之处
💻 C
📖 第 1 页 / 共 2 页
字号:
/* GNU DIFF main routine.
   Copyright (C) 1988, 1989, 1992, 1993 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 GNU DIFF; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* GNU DIFF was written by Mike Haertel, David Hayes,
   Richard Stallman, Len Tower, and Paul Eggert.  */

#define GDIFF_MAIN
#include "diff.h"
#include "getopt.h"
#include "fnmatch.h"

#ifndef DEFAULT_WIDTH
#define DEFAULT_WIDTH 130
#endif

#ifndef GUTTER_WIDTH_MINIMUM
#define GUTTER_WIDTH_MINIMUM 3
#endif

static char const *filetype PARAMS((struct stat const *));
static char *option_list PARAMS((char **, int));
static int add_exclude_file PARAMS((char const *));
static int ck_atoi PARAMS((char const *, int *));
int compare_files PARAMS((char const *, char const *, char const *, char const *, int));
static int specify_format PARAMS((char **, char *));
static void add_exclude PARAMS((char const *));
static void add_regexp PARAMS((struct regexp_list **, char const *));
static void specify_style PARAMS((enum output_style));
static void usage PARAMS((void));

/* Nonzero for -r: if comparing two directories,
   compare their common subdirectories recursively.  */

int recursive;

/* For debugging: don't do discard_confusing_lines.  */

int no_discards;

/* Return a string containing the command options with which diff was invoked.
   Spaces appear between what were separate ARGV-elements.
   There is a space at the beginning but none at the end.
   If there were no options, the result is an empty string.

   Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
   the length of that vector.  */

static char *
option_list (optionvec, count)
     char **optionvec;  /* Was `vector', but that collides on Alliant.  */
     int count;
{
  int i;
  size_t length = 0;
  char *result;

  for (i = 0; i < count; i++)
    length += strlen (optionvec[i]) + 1;

  result = xmalloc (length + 1);
  result[0] = 0;

  for (i = 0; i < count; i++)
    {
      strcat (result, " ");
      strcat (result, optionvec[i]);
    }

  return result;
}

/* Convert STR to a positive integer, storing the result in *OUT.
   If STR is not a valid integer, return -1 (otherwise 0). */
static int
ck_atoi (str, out)
     char const *str;
     int *out;
{
  char const *p;
  for (p = str; *p; p++)
    if (*p < '0' || *p > '9')
      return -1;

  *out = atoi (optarg);
  return 0;
}

/* Keep track of excluded file name patterns.  */

static char const **exclude;
static int exclude_alloc, exclude_count;

int
excluded_filename (f)
     char const *f;
{
  int i;
  for (i = 0;  i < exclude_count;  i++)
    if (fnmatch (exclude[i], f, 0) == 0)
      return 1;
  return 0;
}

static void
add_exclude (pattern)
     char const *pattern;
{
  if (exclude_alloc <= exclude_count)
    exclude = (char const **)
	      (exclude_alloc == 0
	       ? xmalloc ((exclude_alloc = 64) * sizeof (*exclude))
	       : xrealloc (exclude, (exclude_alloc *= 2) * sizeof (*exclude)));

  exclude[exclude_count++] = pattern;
}

static int
add_exclude_file (name)
     char const *name;
{
  struct file_data f;
  char *p, *q, *lim;

  f.name = optarg;
  f.desc = (strcmp (optarg, "-") == 0
	    ? STDIN_FILENO
	    : open (optarg, O_RDONLY, 0));
  if (f.desc < 0 || fstat (f.desc, &f.stat) != 0)
    return -1;

  sip (&f, 1);
  slurp (&f);

  for (p = (char *)f.buffer, lim = p + f.buffered_chars;  p < lim;  p = q)
    {
      q = (char *) memchr (p, '\n', lim - p);
      if (!q)
	q = lim;
      *q++ = 0;
      add_exclude (p);
    }

  return close (f.desc);
}

/* The numbers 129- that appear in the fourth element of some entries
   tell the big switch in `main' how to process those options.  */

static struct option const longopts[] =
{
  {"ignore-blank-lines", 0, 0, 'B'},
  {"context", 2, 0, 'C'},
  {"ifdef", 1, 0, 'D'},
  {"show-function-line", 1, 0, 'F'},
  {"speed-large-files", 0, 0, 'H'},
  {"ignore-matching-lines", 1, 0, 'I'},
  {"label", 1, 0, 'L'},
  {"file-label", 1, 0, 'L'},	/* An alias, no longer recommended */
  {"new-file", 0, 0, 'N'},
  {"entire-new-file", 0, 0, 'N'},	/* An alias, no longer recommended */
  {"unidirectional-new-file", 0, 0, 'P'},
  {"starting-file", 1, 0, 'S'},
  {"initial-tab", 0, 0, 'T'},
  {"width", 1, 0, 'W'},
  {"text", 0, 0, 'a'},
  {"ascii", 0, 0, 'a'},		/* An alias, no longer recommended */
  {"ignore-space-change", 0, 0, 'b'},
  {"minimal", 0, 0, 'd'},
  {"ed", 0, 0, 'e'},
  {"forward-ed", 0, 0, 'f'},
  {"ignore-case", 0, 0, 'i'},
  {"paginate", 0, 0, 'l'},
  {"print", 0, 0, 'l'},		/* An alias, no longer recommended */
  {"rcs", 0, 0, 'n'},
  {"show-c-function", 0, 0, 'p'},
  {"binary", 0, 0, 'q'},	/* An alias, no longer recommended */
  {"brief", 0, 0, 'q'},
  {"recursive", 0, 0, 'r'},
  {"report-identical-files", 0, 0, 's'},
  {"expand-tabs", 0, 0, 't'},
  {"version", 0, 0, 'v'},
  {"ignore-all-space", 0, 0, 'w'},
  {"exclude", 1, 0, 'x'},
  {"exclude-from", 1, 0, 'X'},
  {"side-by-side", 0, 0, 'y'},
  {"unified", 2, 0, 'U'},
  {"left-column", 0, 0, 129},
  {"suppress-common-lines", 0, 0, 130},
  {"sdiff-merge-assist", 0, 0, 131},
  {"old-line-format", 1, 0, 132},
  {"new-line-format", 1, 0, 133},
  {"unchanged-line-format", 1, 0, 134},
  {"line-format", 1, 0, 135},
  {"old-group-format", 1, 0, 136},
  {"new-group-format", 1, 0, 137},
  {"unchanged-group-format", 1, 0, 138},
  {"changed-group-format", 1, 0, 139},
  {"horizon-lines", 1, 0, 140},
  {0, 0, 0, 0}
};

/*
int
main (argc, argv)
     int argc;
     char *argv[];
{
  int val;
  int c;
  int prev = -1;
  int width = DEFAULT_WIDTH;

  // Do our initializations.  
  program = argv[0];
  output_style = OUTPUT_NORMAL;
  context = -1;
  line_end_char = '\n';

  // Decode the options.  

  while ((c = getopt_long (argc, argv,
			   "0123456789abBcC:dD:efF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y",
			   longopts, 0)) != EOF)
    {
      switch (c)
	{
	  // All digits combine in decimal to specify the context-size.  
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	case '0':
	  if (context == -1)
	    context = 0;
	  // If a context length has already been specified,
	     more digits allowed only if they follow right after the others.
	     Reject two separate runs of digits, or digits after -C.  
	  else if (prev < '0' || prev > '9')
	    fatal ("context length specified twice");

	  context = context * 10 + c - '0';
	  break;

	case 'a':
	  // Treat all files as text files; never treat as binary.  
	  always_text_flag = 1;
	  setmode(0, O_BINARY);
	  setmode(1, O_BINARY);

	  break;

	case 'b':
	  // Ignore changes in amount of white space.  
	  ignore_space_change_flag = 1;
	  length_varies = 1;
	  ignore_some_changes = 1;
	  break;

	case 'B':
	  // Ignore changes affecting only blank lines.  
	  ignore_blank_lines_flag = 1;
	  ignore_some_changes = 1;
	  break;

	case 'C':		// +context[=lines] 
	case 'U':		// +unified[=lines] 
	  if (optarg)
	    {
	      if (context >= 0)
		fatal ("context length specified twice");

	      if (ck_atoi (optarg, &context))
		fatal ("invalid context length argument");
	    }

	  // Falls through.  
	case 'c':
	  // Make context-style output.  
	  specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
	  break;

	case 'd':
	  // Don't discard lines.  This makes things slower (sometimes much
	     slower) but will find a guaranteed minimal set of changes.  
	  no_discards = 1;
	  break;

	case 'D':
	  // Make merged #ifdef output.  
	  specify_style (OUTPUT_IFDEF);
	  {
	    int i, err = 0;
	    static char const C_ifdef_group_formats[] =
	      "#ifndef %s\n%%<#endif // not %s \n%c#ifdef %s\n%%>#endif // %s \n%c%%=%c#ifndef %s\n%%<#else // %s \n%%>#endif // %s \n";
	    char *b = xmalloc (sizeof (C_ifdef_group_formats)
			       + 7 * strlen(optarg) - 14 // 7*"%s" 
			       - 8 // 5*"%%" + 3*"%c" );
	    sprintf (b, C_ifdef_group_formats,
		     optarg, optarg, 0,
		     optarg, optarg, 0, 0,
		     optarg, optarg, optarg);
	    for (i = 0; i < 4; i++)
	      {
		err |= specify_format (&group_format[i], b);
		b += strlen (b) + 1;
	      }
	    if (err)
	      error ("conflicting #ifdef formats", 0, 0);
	  }
	  break;

	case 'e':
	  // Make output that is a valid `ed' script.  
	  specify_style (OUTPUT_ED);
	  break;

	case 'f':
	  // Make output that looks vaguely like an `ed' script
	     but has changes in the order they appear in the file.  
	  specify_style (OUTPUT_FORWARD_ED);
	  break;

	case 'F':
	  // Show, for each set of changes, the previous line that
	     matches the specified regexp.  Currently affects only
	     context-style output.  
	  add_regexp (&function_regexp_list, optarg);
	  break;

	case 'h':
	  // Split the files into chunks of around 1500 lines
	     for faster processing.  Usually does not change the result.

	     This currently has no effect.  
	  break;

	case 'H':
	  // Turn on heuristics that speed processing of large files
	     with a small density of changes.  
	  heuristic = 1;
	  break;

	case 'i':
	  // Ignore changes in case.  
	  ignore_case_flag = 1;
	  ignore_some_changes = 1;
	  break;

	case 'I':
	  // Ignore changes affecting only lines that match the
	     specified regexp.  
	  add_regexp (&ignore_regexp_list, optarg);
	  ignore_some_changes = 1;
	  break;

	case 'l':
	  // Pass the output through `pr' to paginate it.  
	  paginate_flag = 1;
	  break;

	case 'L':
	  // Specify file labels for `-c' output headers.  
	  if (!file_label[0])
	    file_label[0] = optarg;
	  else if (!file_label[1])
	    file_label[1] = optarg;
	  else
	    fatal ("too many file label options");
	  break;

	case 'n':
	  // Output RCS-style diffs, like `-f' except that each command
	     specifies the number of lines affected.  
	  specify_style (OUTPUT_RCS);
	  break;

	case 'N':
	  // When comparing directories, if a file appears only in one
	     directory, treat it as present but empty in the other.  
	  entire_new_file_flag = 1;
	  break;

	case 'p':
	  // Make context-style output and show name of last C function.  
	  specify_style (OUTPUT_CONTEXT);
	  add_regexp (&function_regexp_list, "^[_a-zA-Z$]");
	  break;

	case 'P':
	  // When comparing directories, if a file appears only in
	     the second directory of the two,
	     treat it as present but empty in the other.  
	  unidirectional_new_file_flag = 1;
	  break;

	case 'q':
	  no_details_flag = 1;
	  break;

	case 'r':
	  // When comparing directories,
	     recursively compare any subdirectories found.  
	  recursive = 1;
	  break;

	case 's':
	  // Print a message if the files are the same.  
	  print_file_same_flag = 1;
	  break;

	case 'S':
	  // When comparing directories, start with the specified
	     file name.  This is used for resuming an aborted comparison.  
	  dir_start_file = optarg;
	  break;

	case 't':
	  // Expand tabs to spaces in the output so that it preserves
	     the alignment of the input files.  
	  tab_expand_flag = 1;
	  break;

	case 'T':
	  // Use a tab in the output, rather than a space, before the
	     text of an input line, so as to keep the proper alignment
	     in the input line without changing the characters in it.  
	  tab_align_flag = 1;
	  break;

	case 'u':
	  // Output the context diff in unidiff format.  
	  specify_style (OUTPUT_UNIFIED);
	  break;

	case 'v':
	  fprintf (stderr, "GNU diff version %s\n", version_string);
	  break;

	case 'w':
	  // Ignore horizontal white space when comparing lines.  
	  ignore_all_space_flag = 1;
	  ignore_some_changes = 1;
	  length_varies = 1;
	  break;

	case 'x':
	  add_exclude (optarg);
	  break;

	case 'X':
	  if (add_exclude_file (optarg) != 0)
	    pfatal_with_name (optarg);
	  break;

	case 'y':
	  // Use side-by-side (sdiff-style) columnar output. 
	  specify_style (OUTPUT_SDIFF);
	  break;

	case 'W':
	  // Set the line width for OUTPUT_SDIFF.  
	  if (ck_atoi (optarg, &width) || width <= 0)
	    fatal ("column width must be a positive integer");
	  break;

	case 129:
	  sdiff_left_only = 1;
	  break;

	case 130:
	  sdiff_skip_common_lines = 1;
	  break;

	case 131:
	  // sdiff-style columns output. 
	  specify_style (OUTPUT_SDIFF);
	  sdiff_help_sdiff = 1;
	  break;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -