📄 diff.c
字号:
/* diff - print differences between 2 files Author: Erik Baalbergen *//* Poor man's implementation of diff(1) - no options available* - may give more output than other diffs,* due to the straight-forward algorithm* - runs out of memory if the differing chunks become too large* - input line length should not exceed LINELEN; longer lines are* truncated, while only the first LINELEN characters are compared** - Bug fixes by Rick Thomas Sept. 1989** Please report bugs and suggestions to erikb@cs.vu.nl*------------------------------------------------------------------------------* Changed diff to conform to POSIX 1003.2 ( Draft 11) by Thomas Brupbacher* ( tobr@mw.lpc.ethz.ch).** To incorporate the context diff option -c in the program, the source code* for the program cdiff has been copied to the end of this program. Only* slight modifications for the cdiff code to work within the program diff* were made( e.g. main() -> context_diff()).** New options:* -c, -C n where n=0,1,...:* produces a context diff as the program cdiff. The default is to* print 3 lines of context, this value can be changed with -C* ( e.g. -C 5 prints five lines of context.)* -e : Prints an ed script, so you can convert <file1> to <file2> with* the command ed <file1> < `diff -e <file1> <file2>`.* -b : Causes trailing blanks to be ignored and spaces of multiple blanks* to be reduced to one blank before comparison.*-----------------------------------------------------------------------------*/#include <stdlib.h>#include <limits.h> /* NAME_MAX for maximal filename length */#include <string.h> /* string manipulation */#include <sys/types.h>#include <sys/stat.h>#include <sys/wait.h>#include <ctype.h>#include <time.h>#include <dirent.h>#include <unistd.h>#include <stdio.h>/* These definitions are needed only to suppress warning messages. */#define Nullfp ((FILE*)0)#define Nullch ((char*)0)#define NullStructLine ((struct line *)0)#define LINELEN 128 /* max line length included in diff */#define NOT_SET 0 /* Defines to characterise if a flag */#define SET 1 /* is set */ /* Indexes of the warning-message array */#define EXCLUSIVE_OPTIONS 0#define CANNOT_OPEN_FILE 1 /* Used to define the mode */typedef enum { undefined, context, ed_mode} MODE; /* Global variables for the 'normal' diff part */char *progname; /* program name (on command line) */int diffs = 0; /* number of differences */MODE mode; /* which mode is used */int severe_error; /* nonzero after severe, non-fatal error *//* The following global variables are used with the -r option: * for every pair of files that are different, a "command line" of the * form "diff <options> <oldfile> <newfile>" is printed before the real * output starts. */int firstoutput = 1; /* flag to print one time */char options_string[10]; /* string to hold command line options */char oldfile[PATH_MAX]; /* first file */char newfile[PATH_MAX]; /* second file */ /* Global variables for the command-line options */int trim_blanks = NOT_SET; /* SET if -b specified */int recursive_dir = NOT_SET; /* SET if -r specified */int context_lines = 3; /* numbers of lines in a context */static int offset; /* offset of the actual line number for -e */ /* Function prototypes for the functions in this file */struct f;_PROTOTYPE(int main, (int argc, char **argv ));_PROTOTYPE(void process_command_line, (int argc, char **argv ));_PROTOTYPE(void analyse_input_files, (char *arg1, char *arg2, char *input1, char *input2 ));_PROTOTYPE(void diff, (char *filename1, char *filename2 ));_PROTOTYPE(FILE *check_file, (char *name ));_PROTOTYPE(void build_option_string, (void ));_PROTOTYPE(void fatal_error, (char *fmt, char *s ));_PROTOTYPE(void warn, (int number, char *string ));_PROTOTYPE(void trimming_blanks, (char *l_text ));_PROTOTYPE(char *filename, (char *path_string));_PROTOTYPE(struct line *new_line, (int size ));_PROTOTYPE(void free_line, (struct line *l ));_PROTOTYPE(int equal_line, (struct line *l1, struct line *l2 ));_PROTOTYPE(int equal_3, (struct line *l1, struct line *l2 ));_PROTOTYPE(struct line *read_line, (FILE *fp ));_PROTOTYPE(void advance, (struct f *f ));_PROTOTYPE(void aside, (struct f *f, struct line *l ));_PROTOTYPE(struct line *next, (struct f *f ));_PROTOTYPE(void init_f, (struct f *f, FILE *fp ));_PROTOTYPE(void update, (struct f *f, char *s ));_PROTOTYPE(void __diff, (FILE *fp1, FILE *fp2 ));_PROTOTYPE(void differ, (struct f *f1, struct f *f2 ));_PROTOTYPE(int wlen, (struct f *f ));_PROTOTYPE(void range, (int a, int b ));_PROTOTYPE(void cdiff, (char *old, char *new, FILE *file1, FILE *file2 ));_PROTOTYPE(void dumphunk, (void ));_PROTOTYPE(char *getold, (int targ ));_PROTOTYPE(char *getnew, (int targ ));_PROTOTYPE(int isdir, (char *path ));_PROTOTYPE(void diff_recursive, (char *dir1, char *dir2 ));_PROTOTYPE(void file_type_error, (char *filename1, char *filename2, struct stat *statbuf1, struct stat *statbuf2 ));_PROTOTYPE(void *xmalloc, (size_t size));_PROTOTYPE(void *xrealloc, (void *ptr, size_t size));int main(argc, argv)int argc;char **argv;{ char file1[PATH_MAX], file2[PATH_MAX]; extern int optind; /* index of the current string in argv */ progname = argv[0]; process_command_line(argc, argv); analyse_input_files(argv[optind], argv[optind + 1], file1, file2); optind++; if (recursive_dir == SET) { build_option_string(); diff_recursive(file1, file2); } else { diff(file1, file2); } return(severe_error ? 2 : diffs > 0 ? 1 : 0);}/* Process the command line and set the flags for the different * options. the processing of the command line is done with the * getopt() library function. a minimal error processing is done * for the number of command line arguments. */void process_command_line(argc, argv)int argc; /* number of arguments on command line */char **argv; /* ** to arguments on command line */{ int c; extern char *optarg; /* points to string with options */ extern int optind; /* index of the current string in argv */ /* Are there enough arguments? */ if (argc < 3) { fatal_error("Usage: %s [-c|-e|-C n][-br] file1 file2\n", progname); } /* Process all options using getopt() */ while ((c = getopt(argc, argv, "ceC:br")) != -1) { switch (c) { case 'c': if (mode != undefined) warn(EXCLUSIVE_OPTIONS, "c"); mode = context; context_lines = 3; break; case 'e': if (mode != undefined) warn(EXCLUSIVE_OPTIONS, "e"); mode = ed_mode; break; case 'C': if (mode != undefined) warn(EXCLUSIVE_OPTIONS, "C"); mode = context; context_lines = atoi(optarg); break; case 'b': trim_blanks = SET; break; case 'r': recursive_dir = SET; break; case '?': exit(2); } } /* We should have two arguments left */ if ((argc - optind) != 2) fatal_error("Need exactly two input file-names!\n", "");}/* Analyse_input_files takes the two input files on the command line * and decides what to do. returns the (corrected) filenames that * can be used to call diff(). * if two directories are given, then a recursive diff is done. * one directory and one filename compares the file with <filename> * in the directory <directory> with <filename>. * if two filenames are specified, no special action takes place. */void analyse_input_files(arg1, arg2, input1, input2)char *arg1, *arg2; /* filenames on the command line */char *input1, *input2; /* filenames to be used with diff() */{ int stat1 = 0, stat2 = 0; if (strcmp(arg1, "-") != 0) stat1 = isdir(arg1); /* != 0 <-> arg1 is directory */ if (strcmp(arg2, "-") != 0) stat2 = isdir(arg2);#ifdef DEBUG fprintf(stderr, "%s, stat = %d\n", arg1, stat1); fprintf(stderr, "%s, stat = %d\n", arg2, stat2);#endif if (stat1 && stat2) { /* both arg1 and arg2 are directories */ recursive_dir = SET; strcpy(input1, arg1); strcpy(input2, arg2); return; } if (stat1 != 0) { /* arg1 is a dir, arg2 not */ if (strcmp(arg2, "-") != 0) { /* arg2 != stdin */ strcpy(input1, arg1); strcat(input1, "/"); strcat(input1, arg2); strcpy(input2, arg2); return; } else { fatal_error("cannot compare stdin (-) with a directory!", ""); } } if (stat2 != 0) { /* arg2 is a dir, arg1 not */ if (strcmp(arg1, "-") != 0) { /* arg1 != stdin */ strcpy(input1, arg1); strcpy(input2, arg2); strcat(input2, "/"); strcat(input2, arg1); return; } else { /* arg1 == stdin */ fatal_error("cannot compare stdin (-) with a directory!", ""); } } /* Both arg1 and arg2 are normal files */ strcpy(input1, arg1); strcpy(input2, arg2);}/* Diff() is the front end for all modes of the program diff, execpt * the recursive_dir option. * diff() expects the filenames of the two files to be compared as * arguments. the mode is determined from the global variable mode. */void diff(filename1, filename2)char *filename1, *filename2;{ FILE *file1 = check_file(filename1); FILE *file2 = check_file(filename2); struct stat statbuf1, statbuf2; if ((file1 != Nullfp) && (file2 != Nullfp)) { /* If we do a recursive diff, then we don't compare block * special, character special or FIFO special files to any * file. */ fstat(fileno(file1), &statbuf1); fstat(fileno(file2), &statbuf2); if ((((statbuf1.st_mode & S_IFREG) != S_IFREG) || ((statbuf2.st_mode & S_IFREG) != S_IFREG)) && (recursive_dir == SET)) { file_type_error(filename1, filename2, &statbuf1, &statbuf2); } else { switch (mode) { case context: cdiff(filename1, filename2, file1, file2); break; case ed_mode: case undefined: __diff(file1, file2); if (mode == ed_mode) printf("w\n"); break; } } } else severe_error = 1; if (file1 != Nullfp) fclose(file1); if (file2 != Nullfp) fclose(file2);}/* Check_file() opens the fileptr with name <filename>. if <filename> * equals "-" stdin is associated with the return value. */FILE *check_file(name)char *name;{ FILE *temp; if (strcmp(name, "-") == 0) { return(stdin); } else { temp = fopen(name, "r"); if (temp == Nullfp) warn(CANNOT_OPEN_FILE, name); return(temp); }}/* Build_option_string() is called before recursive_dir() is called * from the main() function. its purpose is to build the string that * is used on the command line to get the current operation mode. * e.g. "-C 6 -b". */void build_option_string(){ switch (mode) { case ed_mode:sprintf(options_string, "-e"); break; case context: if (context_lines == 3) sprintf(options_string, "-c"); else sprintf(options_string, "-C %d", context_lines); break; }}/* The fatal error handler. * Expects a format string and a string as arguments. The arguments * are printed to stderr and the program exits with an error code 2. */void fatal_error(fmt, s)char *fmt; /* the format sttring to be printed */char *s; /* string to be inserted into the format * string */{ fprintf(stderr, "%s: ", progname); fprintf(stderr, fmt, s); fprintf(stderr, "\n"); exit(2);}/* This function prints non fatal error messages to stderr. * Expects the index of the message to be printed and a pointer * to the (optional) string to be printed. * Returns no value. */void warn(number, string)int number; /* index of the warning */char *string; /* string to be inserted to the warning */{ static char *warning[] = { "%s: The options -c, -e, -C n are mutually exclusive! Assuming -%c\n", "%s: cannot open file %s for reading\n", }; fprintf(stderr, warning[number], progname, string);}/* Function used with the optione -b, trims the blanks in a input line: * - blanks between words are reduced to one * - trailing blanks are eliminated. */void trimming_blanks(l_text)char *l_text; /* begin of the char array */{ char *line = l_text; char *copy_to, *copy_from; do { if (*line == ' ') { copy_from = line; copy_to = line; while (*(++copy_from) == ' '); if (*copy_from != '\n') copy_to++; while (*copy_from != '\0') *(copy_to++) = *(copy_from++); *copy_to = '\0'; } } while (*(++line) != '\0');}/* Filename separates the filename and the relative path in path_string. * Returns the filename with a leading / */char *filename(path_string)char *path_string;{ char name[NAME_MAX + 2]; /* filename plus / */ char *ptr; name[0] = '/'; ptr = strrchr(path_string, '/'); if (ptr == 0) { /* no / in path_string, only a filename */ strcat(name, path_string); } else { strcat(name, ptr); } return(name);}/* The line module: one member in a linked list of lines. */struct line { struct line *l_next; /* pointer to the next line */ char l_eof; /* == 0 if last line in file */ char *l_text; /* array with the text */};struct line *freelist = 0;#define stepup(ll) ( ((ll) && ((ll)->l_eof==0)) ? (ll)->l_next : (ll) )/* Function to allocate space for a new line containing SIZE chars */struct line *new_line(size)int size;{ register struct line *l;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -