--- install.c.orig 2003-11-01 13:46:59.000000000 +0200 +++ install.c 2003-11-01 15:09:14.000000000 +0200 @@ -92,6 +92,8 @@ static void strip (const char *path); void usage (int status); +static int issame (const char *from, const char *to); + /* The name this program was run with, for error messages. */ char *program_name; @@ -119,9 +121,13 @@ /* If nonzero, install a directory instead of a regular file. */ static int dir_arg; +/* If nonzero, compare source and destination files before overwriting */ +static int compare_files; + static struct option const long_options[] = { {"backup", optional_argument, NULL, 'b'}, + {"compare", no_argument, NULL, 'C'}, {"directory", no_argument, NULL, 'd'}, {"group", required_argument, NULL, 'g'}, {"mode", required_argument, NULL, 'm'}, @@ -200,13 +206,14 @@ group_name = NULL; strip_files = 0; dir_arg = 0; + compare_files = 0; umask (0); /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless we'll actually use backup_suffix_string. */ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); - while ((optc = getopt_long (argc, argv, "bcsDdg:m:o:pvV:S:", long_options, + while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pvV:S:", long_options, NULL)) != -1) { switch (optc) @@ -228,6 +235,9 @@ break; case 'c': break; + case 'C': + compare_files = 1; + break; case 's': strip_files = 1; break; @@ -436,6 +446,9 @@ return 1; } + if (compare_files && issame(from,to)) + return 0; + fail = copy (from, to, nonexistent_dst, x, ©_into_self, NULL); return fail; @@ -510,6 +523,64 @@ return 0; } +/* Check if from and to are same + Return 0 if not, 1 if yes */ + +#define CMP_BLKSIZE 4096 +#ifndef EINTR +# define EINTR 0 +# define V_EINTR +#endif +static int +issame (const char *from, const char *to) +{ + struct stat buf1, buf2; + char p1[CMP_BLKSIZE], p2[CMP_BLKSIZE]; + ssize_t n_read1, n_read2; + int ret; + int fd1, fd2; + + if (lstat(from, &buf1) || lstat(to, &buf2)) + return 0; + + if (buf1.st_size != buf2.st_size) + return 0; + + if (!S_ISREG(buf1.st_mode) || !S_ISREG(buf2.st_mode)) + return 0; + + fd1=open(from, O_RDONLY); if (fd1<0) return 0; + fd2=open(to, O_RDONLY); if (fd2<0) { close (fd1); return 0; } + + for (;;) + { + do { + n_read1=read(fd1, p1, CMP_BLKSIZE); + if (n_read1<0 && errno!=EINTR) { ret=0; goto do_return; } + } while (n_read1<0 && errno==EINTR); + + do { + n_read2=read(fd2, p2, CMP_BLKSIZE); + if (n_read2<0 && errno!=EINTR) { ret=0; goto do_return; } + } while (n_read2<0 && errno==EINTR); + + if (n_read1 == 0 && n_read2 == 0) { ret=1; goto do_return; } + + if (n_read1!=n_read2) { ret=0; goto do_return; } + + if (n_read1>0 && memcmp(p1, p2, n_read1)) { ret=0; goto do_return; } + } + +do_return: + close(fd1); + close(fd2); + return(ret); +} +#ifdef V_EINTR +# undef EINTR +#endif +#undef CMP_BLKSIZE + /* Strip the symbol table from the file PATH. We could dig the magic number out of the file first to determine whether to strip it, but the header files and @@ -613,6 +684,7 @@ --backup[=CONTROL] make a backup of each existing destination file\n\ -b like --backup but does not accept an argument\n\ -c (ignored)\n\ + -C, --compare compare source and destination files before overwriting\n\ -d, --directory treat all arguments as directory names; create all\n\ components of the specified directories\n\ "), stdout);