BSD 4_3_Tahoe release
[unix-history] / usr / src / bin / cmp.c
index 3798b8b..dd3dd3e 100644 (file)
-static char *sccsid = "@(#)cmp.c       4.2 (Berkeley) 4/29/83";
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1987 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmp.c      4.9 (Berkeley) 6/18/88";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <stdio.h>
 #include <ctype.h>
+#include <errno.h>
 
 
-FILE   *file1,*file2;
-int    eflg;
-int    lflg    = 1;
-long   line    = 1;
-long   chr     = 0;
-long   skip1;
-long   skip2;
+#define DIFF   1                       /* found differences */
+#define ERR    2                       /* error during run */
+#define NO     0                       /* no/false */
+#define OK     0                       /* didn't find differences */
+#define YES    1                       /* yes/true */
 
 
-long   otoi();
+static int     fd1, fd2,               /* file descriptors */
+               silent = NO;            /* if silent run */
+static short   all = NO;               /* if report all differences */
+static u_char  buf1[MAXBSIZE],         /* read buffers */
+               buf2[MAXBSIZE];
+static char    *file1, *file2;         /* file names */
 
 main(argc, argv)
 
 main(argc, argv)
-char **argv;
+       int     argc;
+       char    **argv;
 {
 {
-       register c1, c2;
-       char *arg;
-
-       if(argc < 3)
-               goto narg;
-       arg = argv[1];
-       if(arg[0] == '-' && arg[1] == 's') {
-               lflg--;
-               argv++;
-               argc--;
-       }
-       arg = argv[1];
-       if(arg[0] == '-' && arg[1] == 'l') {
-               lflg++;
-               argv++;
-               argc--;
-       }
-       if(argc < 3)
-               goto narg;
-       arg = argv[1];
-       if( arg[0]=='-' && arg[1]==0 )
-               file1 = stdin;
-       else if((file1 = fopen(arg, "r")) == NULL)
-               goto barg;
-       arg = argv[2];
-       if((file2 = fopen(arg, "r")) == NULL)
-               goto barg;
-       if (argc>3)
-               skip1 = otoi(argv[3]);
-       if (argc>4)
-               skip2 = otoi(argv[4]);
-       while (skip1) {
-               if ((c1 = getc(file1)) == EOF) {
-                       arg = argv[1];
-                       goto earg;
+       extern char     *optarg;
+       extern int      optind;
+       int     ch;
+       u_long  otoi();
+
+       while ((ch = getopt(argc, argv, "ls")) != EOF)
+               switch(ch) {
+               case 'l':               /* print all differences */
+                       all = YES;
+                       break;
+               case 's':               /* silent run */
+                       silent = YES;
+                       break;
+               case '?':
+               default:
+                       usage();
                }
                }
-               skip1--;
+       argv += optind;
+       argc -= optind;
+
+       if (argc < 2 || argc > 4)
+               usage();
+
+       /* open up files; "-" is stdin */
+       file1 = argv[0];
+       if (strcmp(file1, "-") && (fd1 = open(file1, O_RDONLY, 0)) < 0)
+               error(file1);
+       file2 = argv[1];
+       if ((fd2 = open(file2, O_RDONLY, 0)) < 0)
+               error(file2);
+
+       /* handle skip arguments */
+       if (argc > 2) {
+               skip(otoi(argv[2]), fd1, file1);
+               if (argc == 4)
+                       skip(otoi(argv[3]), fd2, file2);
        }
        }
-       while (skip2) {
-               if ((c2 = getc(file2)) == EOF) {
-                       arg = argv[2];
-                       goto earg;
+       cmp();
+       /*NOTREACHED*/
+}
+
+/*
+ * skip --
+ *     skip first part of file
+ */
+static
+skip(dist, fd, fname)
+       register u_long dist;           /* length in bytes, to skip */
+       register int    fd;             /* file descriptor */
+       char    *fname;                 /* file name for error */
+{
+       register int    rlen;           /* read length */
+       register int    nread;
+
+       for (; dist; dist -= rlen) {
+               rlen = MIN(dist, sizeof(buf1));
+               if ((nread = read(fd, buf1, rlen)) != rlen) {
+                       if (nread < 0)
+                               error(fname);
+                       else
+                               endoffile(fname);
                }
                }
-               skip2--;
        }
        }
+}
+
+static
+cmp()
+{
+       register u_char *C1, *C2;       /* traveling pointers */
+       register int    cnt,            /* counter */
+                       len1, len2;     /* read lengths */
+       register long   byte,           /* byte count */
+                       line;           /* line count */
+       short   dfound = NO;            /* if difference found */
 
 
-loop:
-       chr++;
-       c1 = getc(file1);
-       c2 = getc(file2);
-       if(c1 == c2) {
-               if (c1 == '\n')
-                       line++;
-               if(c1 == EOF) {
-                       if(eflg)
-                               exit(1);
-                       exit(0);
+       for (byte = 0, line = 1;;) {
+               switch(len1 = read(fd1, buf1, MAXBSIZE)) {
+               case -1:
+                       error(file1);
+               case 0:
+                       /*
+                        * read of file 1 just failed, find out
+                        * if there's anything left in file 2
+                        */
+                       switch(read(fd2, buf2, 1)) {
+                               case -1:
+                                       error(file2);
+                               case 0:
+                                       exit(dfound ? DIFF : OK);
+                               default:
+                                       endoffile(file1);
+                       }
                }
                }
-               goto loop;
-       }
-       if(lflg == 0)
-               exit(1);
-       if(c1 == EOF) {
-               arg = argv[1];
-               goto earg;
+               /*
+                * file1 might be stdio, which means that a read of less than
+                * MAXBSIZE might not mean an EOF.  So, read whatever we read
+                * from file1 from file2.
+                */
+               if ((len2 = read(fd2, buf2, len1)) == -1)
+                       error(file2);
+               if (bcmp(buf1, buf2, len2)) {
+                       if (silent)
+                               exit(DIFF);
+                       if (all) {
+                               dfound = YES;
+                               for (C1 = buf1, C2 = buf2, cnt = len2; cnt--; ++C1, ++C2) {
+                                       ++byte;
+                                       if (*C1 != *C2)
+                                               printf("%6ld %3o %3o\n", byte, *C1, *C2);
+                               }
+                       }
+                       else for (C1 = buf1, C2 = buf2;; ++C1, ++C2) {
+                               ++byte;
+                               if (*C1 != *C2) {
+                                       printf("%s %s differ: char %ld, line %ld\n", file1, file2, byte, line);
+                                       exit(DIFF);
+                               }
+                               if (*C1 == '\n')
+                                       ++line;
+                       }
+               }
+               else {
+                       byte += len2;
+                       /*
+                        * here's the real performance problem, we've got to
+                        * count the stupid lines, which means that -l is a
+                        * *much* faster version, i.e., unless you really
+                        * *want* to know the line number, run -s or -l.
+                        */
+                       if (!silent && !all)
+                               for (C1 = buf1, cnt = len2; cnt--;)
+                                       if (*C1++ == '\n')
+                                               ++line;
+               }
+               /*
+                * couldn't read as much from file2 as from file1; checked
+                * here because there might be a difference before we got
+                * to this point, which would have precedence.
+                */
+               if (len2 < len1)
+                       endoffile(file2);
        }
        }
-       if(c2 == EOF)
-               goto earg;
-       if(lflg == 1) {
-               printf("%s %s differ: char %ld, line %ld\n", argv[1], arg,
-                       chr, line);
-               exit(1);
+}
+
+/*
+ * otoi --
+ *     octal/decimal string to u_long
+ */
+static u_long
+otoi(C)
+       register char   *C;             /* argument string */
+{
+       register u_long val;            /* return value */
+       register int    base;           /* number base */
+
+       base = (*C == '0') ? 8 : 10;
+       for (val = 0; isdigit(*C); ++C)
+               val = val * base + *C - '0';
+       return(val);
+}
+
+/*
+ * error --
+ *     print I/O error message and die
+ */
+static
+error(filename)
+       char *filename;
+{
+       extern int errno;
+       int sverrno;
+
+       if (!silent) {
+               sverrno = errno;
+               (void)fprintf(stderr, "cmp: %s: ", filename);
+               errno = sverrno;
+               perror((char *)NULL);
        }
        }
-       eflg = 1;
-       printf("%6ld %3o %3o\n", chr, c1, c2);
-       goto loop;
-
-narg:
-       printf("cmp: arg count\n");
-       exit(2);
-
-barg:
-       if (lflg)
-       perror(arg);
-       exit(2);
-
-earg:
-       printf("cmp: EOF on %s\n", arg);
-       exit(1);
+       exit(ERR);
+}
+
+/*
+ * endoffile --
+ *     print end-of-file message and exit indicating the files were different
+ */
+static
+endoffile(filename)
+       char *filename;
+{
+       /* 32V put this message on stdout, S5 does it on stderr. */
+       if (!silent)
+               (void)fprintf(stderr, "cmp: EOF on %s\n", filename);
+       exit(DIFF);
 }
 
 }
 
-long otoi(s)
-char *s;
+/*
+ * usage --
+ *     print usage and die
+ */
+static
+usage()
 {
 {
-       long v;
-       int base;
-
-       v = 0;
-       base = 10;
-       if (*s == '0')
-               base = 8;
-       while(isdigit(*s))
-               v = v*base + *s++ - '0';
-       return(v);
+       fputs("usage: cmp [-ls] file1 file2 [skip1] [skip2]\n", stderr);
+       exit(ERR);
 }
 }