#ifdef ournix /* { */ #include "ournix.h" #endif /* } */ char sccsID[] = "@(#) cmpd.c V1.15 Copyright Julian H. Stacey 14th May 1987\n"; /* Copyright: This code was written independently by Julian H. Stacey before being used or delivered with any code for Stacey/VSL clients. Clients who receive such code are permitted to use & resell code etc, but copyright to this file is Not assigned to the client. & no Liability is accepted, nor Warranty granted for this free code that the client/recipient has not paid for development of. */ /* MANUAL EXISTS - See cmpd.1 Synopsis: similar to unix cmp.c, but with much added functionality. */ #include #include #include #include #ifndef hpux #ifdef _M_SYS5 /* SCO */ #include #define PATH_MAX _POSIX_PATH_MAX #else /* BSD */ #ifndef scs /*{Not Symmetric S375 */ #include #endif /*}*/ #endif #endif #ifdef BSD /* { */ #include #else /* }{ */ #include #endif /* } */ #ifndef scs /*{Not Symmetric S375 */ #include #else /*}{*/ #include #define PATH_MAX MAXPATHLEN /* 1024 */ #endif /*}*/ #ifdef hpux /* define PATH_MAX */ #include /* see also sys/fs/vx_hpux.h, X11R5/X11/Xos.h */ #endif typedef char FLAG ; #define STRADR & typedef int FD ; #define strequ(a,b) (strcmp((a),(b)) == 0 ) static char **ARGV ; static int ARGC; static int err_fail = 0 ; /* if an open or unlink error occurs */ static int diff_no = 0 ; /* For scs & msdos: typedef struct _iobuf FILE ; or #define FILE struct _iobuf */ static FILE *err_fd ; static char txt_cant_stat[] = "cannot stat"; extern char *strcat() ; /* EMBEDDED is defined if the program is embedded into OCE/oceres */ #ifndef EMBEDDED /* { */ /* Only used with -m flag, skips past email/news header until a blank line is detected, Code could have been integrated, for efficiency, but wont often be needed, & clearer to read like this. I admit single char input is horrible ! Note blank line is "\n\n", "\n\020\n" is not recognised as a mail header/body separator, though it would be nice if it was. */ static int skip(descrip,name) FD descrip ; char *name ; { char this ; char last = '\n' ; int rslt ; while ( (rslt = read(descrip,&this,1)) == 1) { if ((this == '\n') && (last == '\n')) return(0) ; else last = this ; } if (rslt == 0) return(0) ; fprintf(err_fd,"%s: Read error while skipping %s mail header\n", *ARGV, name); err_fail = 1 ; diff_no++ ; return(1) ; } #endif /* EMBEDDED } */ static void syntax() { fprintf(err_fd, "Syntax:\n%s [-b] [-d] [-l[directory]] [-e] %s[-v] [--] file[s] %s\n", "reference_file_or_directory_if_not_stdin.", #ifndef EMBEDDED /* { */ "[-m] ", #else /* }{*/ " ", #endif /* EMBEDDED } */ *ARGV); } #if MSDOS /* MSDOS and some old unixes don't have symbolic links */ #define NO_SYMLINKS #endif #if BSD /* define DONT_USE_SYMLINK_SYS_CALL if you don't want to use the raw system call, and prefer to use system("....."); */ #undef DONT_USE_SYMLINK_SYS_CALL #endif int #ifdef EMBEDDED /* { */ cmpd(argc, argv) #else /* }{ */ main(argc, argv) #endif /* } */ int argc; char **argv; { char *pointer_rm, *pointer_ref; #define BUF_SIZE 0x2000 int buf_size = BUF_SIZE ; char buf_file_rm[BUF_SIZE] ; /* buffer of file that may be deleted */ char buf_file_ref[BUF_SIZE]; /* reference buffer */ FD fd_file_rm = (FD)0 ; /* file to compare & possibly rm */ FD fd_file_ref; /* reference file, never removed */ int length_rm, length_ref, buf_len; struct stat ref_stat ; /* stat() of reference file/directory */ struct stat rm_stat ; /* stat() & lstat() of file that may be removed */ char name_ref[1024]; /* comparison reference file */ FLAG is_dir = 0 ; /* if directory detected as last arg */ FLAG delete_f = 0 ; /* 1 to delete first of 2 equal files */ FLAG suppress_f = 0 ; /* 1 to silence complaints of absent reference files (when trying to delete duplicate trees) */ FLAG verbose_f = 0 ; /* 1 for verbose commentary */ #ifndef EMBEDDED /* { */ FLAG mail_f = 0 ; /* 1 if comparing email or news items ie dont start compare till after blank line */ #endif /* EMBEDDED } */ FLAG pipe_f = 0 ; /* 1 if comparing a file with a pipe */ long letter, lines, bytes ; /* line & byte count */ #ifndef NO_SYMLINKS FLAG link_f = 0 ; /* whether to establish a symbolic link */ char *link_dir = (char *)0 ; /* where to establish a symbolic link to (REF_DIR/file if unset ) ie delete file & establish file -> link_dir/file (only works if delete_f asserted) */ char link_target[PATH_MAX] ; #if DONT_USE_SYMLINK_SYS_CALL char link_str_cmd[20 + 2 * PATH_MAX] ; #endif int system_result ; #endif ARGV = argv ; #ifdef VSL /* { */ #include "../../include/vsl.h" #endif /* } */ ARGC = argc; err_fd = stderr ; for(argv++,argc--; argc > 0 ; ) { if (**argv != '-') break ; switch(*++*argv) { case 'd': delete_f = 1 ; break; case 's': suppress_f = 1 ;break; case 'v': verbose_f = 1; break; #ifndef EMBEDDED /* { */ case 'm': mail_f = 1; break; #endif /* EMBEDDED } */ case 'b': buf_size = 512; break; case '-': pipe_f = 1 ; break; case 'e': err_fd = stdout ; break ; #ifndef NO_SYMLINKS case 'l': link_f = 1 ; if (*++*argv != '\0') link_dir = *argv ; delete_f = 1 ; break ; #endif default: syntax() ; return(-1) ; break; } argv++ ; argc-- ; } if ( (argc < 1) || ((argc == 1 ) && (pipe_f == 0)) || ((argc > 1 ) && pipe_f )) { syntax() ; return(-1) ; } if (!pipe_f) { #define REF_DIR ( * (ARGV + ARGC - 1 ) ) /* scs accepts ARGV[ARGC-1], 486 doesnt */ if (stat(REF_DIR,STRADR ref_stat)) { fprintf(err_fd, "%s: %s %s\n", *ARGV, txt_cant_stat, REF_DIR ); syntax() ; } if (ref_stat.st_mode & S_IFDIR) { is_dir = 1; /* Detect & warn of construct such as "cmp fred jim ." (because == "cmp fred ./fred" && "cmp jim ./jim" == TRUE (always!) */ if (strequ(REF_DIR,".")) fprintf(err_fd, "%s: Warning: %s\n%s\n", *ARGV, "Comparison using \".\" as reference directory,", "will always result in equality.") ; } else if ( argc > 2 ) { fprintf(err_fd, "%s: Error: %s should be a directory\n", *ARGV, REF_DIR ); syntax() ; } } while (argc-- >= (2 - pipe_f)) { if (pipe_f) { fd_file_ref = fileno(stdin) ; #ifdef MSDOS /* { */ /* JJLATER For Msdos: I dont know whether stdin is already open in binary mode, or whether something extra should be done here */ #endif /* } */ } else { if (stat(*argv,STRADR rm_stat)) { fprintf(err_fd, "%s: Warning, cannot stat %s\n", /* JJLATER why is it Warning, cannot stat here but Error, cannot stat below */ *ARGV,*argv); err_fail = 1 ; diff_no++ ; argv++ ; continue ; } /* assemble name of reference file */ (void) strcpy(name_ref, REF_DIR ); if (is_dir) { (void)strcat(name_ref,"/"); (void)strcat(name_ref,*argv); } #ifndef MSDOS /* { */ /* Avoid deleting a single file pointed to by different paths, such as fred & ../freds_dir/fred, whether by direct naming or by symbolic link. Compare device major minor + inode numbers here, if all the same refuse to delete! Unfortunately Msdos doesnt offer inode numbers, so we cant check if we are referring to the same file by 2 different pathnames ( such as ../../freds_parents_dir/freds_dir/fred, & ../freds_dir/fred ) */ if (delete_f) { /* avoid deleting a file that is named by 2 different paths, but has only one link, such as cmp -d fred ../freds_dir/fred */ if (stat(name_ref,STRADR ref_stat)) { if (!suppress_f) fprintf(err_fd, "%s: Error, cannot stat %s\n", *ARGV, name_ref) ; err_fail = 1 ; diff_no++ ; argv++ ; continue ; } if ( (rm_stat.st_dev == ref_stat.st_dev) /* device (dev_t) */ && (rm_stat.st_ino == ref_stat.st_ino) /* inode (ino_t) */ && (rm_stat.st_nlink == 1 ) ) { /* both names point to same inode */ #ifdef SYM_LINKS_AVAIL /* { */ /* see if first name is just a symbolic link, if so allow deletion */ if (lstat(*argv,STRADR rm_stat)) { fprintf(err_fd, "%s: Error, cannot lstat %s\n", *ARGV, *argv) ; perror(*ARGV); err_fail = 1 ; diff_no++ ; argv++ ; continue ; } #endif /* } */ if ( #ifdef SYM_LINKS_AVAIL /* { */ (rm_stat.st_mode & S_IFMT) != S_IFLNK /* IFLNK == sym link */ #else /* }{ */ /* no symbolic linked files on non SYM_LINKS_AVAIL unix */ 1 #endif /* } */ ) { /* Not an innocuous symbolic link, so avoid deleting. */ fprintf(err_fd, #ifdef SYM_LINKS_AVAIL /* { */ "%s: %s %s & %s %s,\n\t%s, & %s %s; %s\n", #else /* }{ */ "%s: %s %s & %s %s,\n\t%s; %s\n", #endif /* } */ *ARGV, "Error: ", *argv, name_ref, "share same device (major & minor) & inode ", "link count is 1", #ifdef SYM_LINKS_AVAIL /* { */ *argv, "isn't a symbolic link", #endif /* } */ "Skipping." ); diff_no++ ; argv++ ; continue ; } } } #endif /* } */ if ((fd_file_ref = open(name_ref,O_RDONLY #ifdef MSDOS /* { */ | O_BINARY #endif /* } */ )) == -1) { fprintf(err_fd,"%s: Cannot open %s\n", *ARGV,name_ref); if (fd_file_rm != (FD)0) (void) close(fd_file_rm) ; err_fail = 1 ; diff_no++ ; argv++ ; continue ; } } /* now check if we are comparing device with device */ if (((rm_stat.st_mode & S_IFMT) != S_IFREG ) && ((rm_stat.st_mode & S_IFMT) == (ref_stat.st_mode & S_IFMT)) && (rm_stat.st_rdev == ref_stat.st_rdev) ) { /* JJ add code here, do something about comparing 2 sym links, & a sym link to a fifo */ /* 386bsd values: S_IFMT 0170000 type of file S_IFIFO 0010000 named pipe (fifo) S_IFCHR 0020000 character special S_IFDIR 0040000 directory S_IFBLK 0060000 block special S_IFREG 0100000 regular S_IFLNK 0120000 symbolic link S_IFSOCK 0140000 socket */ } if ((fd_file_rm = open(*argv,O_RDONLY #ifdef MSDOS /* { */ | O_BINARY #endif /* } */ )) == -1) { fprintf(err_fd,"%s: Cannot open %s\n",*ARGV,*argv); err_fail = 1 ; diff_no++ ; argv++ ; continue ; } letter = lines = bytes = 1 ; #ifndef EMBEDDED /* { */ if (mail_f) { skip(fd_file_ref,name_ref) ; skip(fd_file_rm, (pipe_f) ? "pipe input" :*argv); } #endif /* EMBEDDED } */ for (;;) { length_rm = 0 ; if ( (( length_ref = read(fd_file_ref,buf_file_ref,buf_size)) < 0) || ((length_rm = read(fd_file_rm,buf_file_rm, (length_ref == 0) ? buf_size : length_ref /* not just buf_size as read on a pipe cannot return more than 4K */ )) < 0) ) { fprintf(err_fd,"%s: Read error on ", *ARGV); if (length_rm < 0 ) fprintf(err_fd,"%s\n", (pipe_f) ? "pipe input" :*argv); else { if (is_dir) fprintf(err_fd,"%s/%s\n", REF_DIR , *argv); else fprintf(err_fd,"%s\n",*argv); } err_fail = 1 ; diff_no++ ; break ; } if ((length_rm == 0) && (length_ref == 0)) { if (verbose_f) { if (is_dir ) printf("%s ",*argv); printf( delete_f ? "Deleted\n": "Same\n"); } if (delete_f) { #ifndef NO_SYMLINKS if (link_f) (void) signal(SIGINT, SIG_IGN); #endif if (unlink(*argv)) { perror(*ARGV); diff_no++ ; err_fail = 1 ; } #ifndef NO_SYMLINKS /* { */ if (link_f) { /* create a sym. link */ if (link_dir == (char *)0) strcpy(link_target, name_ref ); else sprintf(link_target, "%s/%s",link_dir,*argv); #if DONT_USE_SYMLINK_SYS_CALL /* { */ sprintf(link_str_cmd, "ln -s %s %s\n", link_target,*argv); if (system_result = system(link_str_cmd)) #else /* }{ */ if ((system_result = symlink(link_target, *argv))) /* I put un-necessary brackets in to silence the gcc -Wall warning: "suggest parentheses around assignment used as truth value" */ #endif /* } */ { extern int errno; perror(*ARGV); printf("Error %d sym. linking %s -> %s\n", errno,*argv,link_target); err_fail = 1 ; } (void) signal(SIGINT, SIG_DFL); } #endif /* } */ } break ; } buf_len = (length_rm < length_ref ) ? length_rm : length_ref ; pointer_rm = buf_file_rm; pointer_ref = buf_file_ref ; while (buf_len && (*pointer_rm == *pointer_ref) ) { bytes++ ; letter++ ; if ((*pointer_rm & 0xFF) == '\n') { letter = 1 ; lines++ ; } buf_len--; pointer_rm++; pointer_ref++ ; } if ((buf_len == 0) && (length_rm == length_ref)) continue ; if (!delete_f || verbose_f) { if (is_dir) printf("%s ",*argv); printf( "Different: char %ld in line %ld, (byte %ld)\n", letter,lines,bytes); } diff_no++ ; break ; } if (!pipe_f) (void) close(fd_file_ref) ; (void) close(fd_file_rm); argv++ ; } if (err_fail) return(-diff_no) ; else return(diff_no) ; }