busybox/editors/cmp.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini cmp implementation for busybox
   4 *
   5 * Copyright (C) 2000,2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
   8 */
   9
  10/* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */
  11/* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */
  12
  13/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
  14 *
  15 * Original version majorly reworked for SUSv3 compliance, bug fixes, and
  16 * size optimizations.  Changes include:
  17 * 1) Now correctly distinguishes between errors and actual file differences.
  18 * 2) Proper handling of '-' args.
  19 * 3) Actual error checking of i/o.
  20 * 4) Accept SUSv3 -l option.  Note that we use the slightly nicer gnu format
  21 *    in the '-l' case.
  22 */
  23
  24#include "libbb.h"
  25
  26static const char fmt_eof[] ALIGN1 = "cmp: EOF on %s\n";
  27static const char fmt_differ[] ALIGN1 = "%s %s differ: char %"OFF_FMT"d, line %d\n";
  28// This fmt_l_opt uses gnu-isms.  SUSv3 would be "%.0s%.0s%"OFF_FMT"d %o %o\n"
  29static const char fmt_l_opt[] ALIGN1 = "%.0s%.0s%"OFF_FMT"d %3o %3o\n";
  30
  31static const char opt_chars[] ALIGN1 = "sl";
  32#define CMP_OPT_s (1<<0)
  33#define CMP_OPT_l (1<<1)
  34
  35int cmp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  36int cmp_main(int argc UNUSED_PARAM, char **argv)
  37{
  38        FILE *fp1, *fp2, *outfile = stdout;
  39        const char *filename1, *filename2 = "-";
  40        IF_DESKTOP(off_t skip1 = 0, skip2 = 0;)
  41        off_t char_pos = 0;
  42        int line_pos = 1; /* Hopefully won't overflow... */
  43        const char *fmt;
  44        int c1, c2;
  45        unsigned opt;
  46        int retval = 0;
  47
  48        xfunc_error_retval = 2; /* 1 is returned if files are different. */
  49
  50        opt_complementary = "-1"
  51                        IF_DESKTOP(":?4")
  52                        IF_NOT_DESKTOP(":?2")
  53                        ":l--s:s--l";
  54        opt = getopt32(argv, opt_chars);
  55        argv += optind;
  56
  57        filename1 = *argv;
  58        fp1 = xfopen_stdin(filename1);
  59
  60        if (*++argv) {
  61                filename2 = *argv;
  62#if ENABLE_DESKTOP
  63                if (*++argv) {
  64                        skip1 = XATOOFF(*argv);
  65                        if (*++argv) {
  66                                skip2 = XATOOFF(*argv);
  67                        }
  68                }
  69#endif
  70        }
  71
  72        fp2 = xfopen_stdin(filename2);
  73        if (fp1 == fp2) {               /* Paranoia check... stdin == stdin? */
  74                /* Note that we don't bother reading stdin.  Neither does gnu wc.
  75                 * But perhaps we should, so that other apps down the chain don't
  76                 * get the input.  Consider 'echo hello | (cmp - - && cat -)'.
  77                 */
  78                return 0;
  79        }
  80
  81        if (opt & CMP_OPT_l)
  82                fmt = fmt_l_opt;
  83        else
  84                fmt = fmt_differ;
  85
  86#if ENABLE_DESKTOP
  87        while (skip1) { getc(fp1); skip1--; }
  88        while (skip2) { getc(fp2); skip2--; }
  89#endif
  90        do {
  91                c1 = getc(fp1);
  92                c2 = getc(fp2);
  93                ++char_pos;
  94                if (c1 != c2) {                 /* Remember: a read error may have occurred. */
  95                        retval = 1;             /* But assume the files are different for now. */
  96                        if (c2 == EOF) {
  97                                /* We know that fp1 isn't at EOF or in an error state.  But to
  98                                 * save space below, things are setup to expect an EOF in fp1
  99                                 * if an EOF occurred.  So, swap things around.
 100                                 */
 101                                fp1 = fp2;
 102                                filename1 = filename2;
 103                                c1 = c2;
 104                        }
 105                        if (c1 == EOF) {
 106                                die_if_ferror(fp1, filename1);
 107                                fmt = fmt_eof;  /* Well, no error, so it must really be EOF. */
 108                                outfile = stderr;
 109                                /* There may have been output to stdout (option -l), so
 110                                 * make sure we fflush before writing to stderr. */
 111                                xfflush_stdout();
 112                        }
 113                        if (!(opt & CMP_OPT_s)) {
 114                                if (opt & CMP_OPT_l) {
 115                                        line_pos = c1;  /* line_pos is unused in the -l case. */
 116                                }
 117                                fprintf(outfile, fmt, filename1, filename2, char_pos, line_pos, c2);
 118                                if (opt) {      /* This must be -l since not -s. */
 119                                        /* If we encountered an EOF,
 120                                         * the while check will catch it. */
 121                                        continue;
 122                                }
 123                        }
 124                        break;
 125                }
 126                if (c1 == '\n') {
 127                        ++line_pos;
 128                }
 129        } while (c1 != EOF);
 130
 131        die_if_ferror(fp1, filename1);
 132        die_if_ferror(fp2, filename2);
 133
 134        fflush_stdout_and_exit(retval);
 135}
 136