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 source tree.
   8 */
   9//config:config CMP
  10//config:       bool "cmp (4.9 kb)"
  11//config:       default y
  12//config:       help
  13//config:       cmp is used to compare two files and returns the result
  14//config:       to standard output.
  15
  16//applet:IF_CMP(APPLET(cmp, BB_DIR_USR_BIN, BB_SUID_DROP))
  17
  18//kbuild:lib-$(CONFIG_CMP) += cmp.o
  19
  20//usage:#define cmp_trivial_usage
  21//usage:       "[-ls] [-n NUM] FILE1 [FILE2" IF_DESKTOP(" [SKIP1 [SKIP2]]") "]"
  22//usage:#define cmp_full_usage "\n\n"
  23//usage:       "Compare FILE1 with FILE2 (or stdin)\n"
  24//usage:     "\n        -l      Write the byte numbers (decimal) and values (octal)"
  25//usage:     "\n                for all differing bytes"
  26//usage:     "\n        -s      Quiet"
  27//usage:     "\n        -n NUM  Compare at most NUM bytes"
  28
  29/* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */
  30/* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */
  31
  32#include "libbb.h"
  33
  34static const char fmt_eof[] ALIGN1 = "cmp: EOF on %s\n";
  35static const char fmt_differ[] ALIGN1 = "%s %s differ: char %"OFF_FMT"u, line %u\n";
  36// This fmt_l_opt uses gnu-isms.  SUSv3 would be "%.0s%.0s%"OFF_FMT"u %o %o\n"
  37static const char fmt_l_opt[] ALIGN1 = "%.0s%.0s%"OFF_FMT"u %3o %3o\n";
  38
  39#define OPT_STR "sln:+"
  40#define CMP_OPT_s (1<<0)
  41#define CMP_OPT_l (1<<1)
  42#define CMP_OPT_n (1<<2)
  43
  44int cmp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  45int cmp_main(int argc UNUSED_PARAM, char **argv)
  46{
  47        FILE *fp1, *fp2, *outfile = stdout;
  48        const char *filename1, *filename2 = "-";
  49        off_t skip1 = 0, skip2 = 0, char_pos = 0;
  50        int line_pos = 1; /* Hopefully won't overflow... */
  51        const char *fmt;
  52        int c1, c2;
  53        unsigned opt;
  54        int retval = 0;
  55        int max_count = -1;
  56
  57        opt = getopt32(argv, "^"
  58                        OPT_STR
  59                        "\0" "-1"
  60                        IF_DESKTOP(":?4")
  61                        IF_NOT_DESKTOP(":?2")
  62                        ":l--s:s--l",
  63                        &max_count
  64        );
  65        argv += optind;
  66
  67        filename1 = *argv;
  68        if (*++argv) {
  69                filename2 = *argv;
  70                if (ENABLE_DESKTOP && *++argv) {
  71                        skip1 = XATOOFF(*argv);
  72                        if (*++argv) {
  73                                skip2 = XATOOFF(*argv);
  74                        }
  75                }
  76        }
  77
  78        xfunc_error_retval = 2;  /* missing file results in exitcode 2 */
  79        if (opt & CMP_OPT_s)
  80                logmode = 0;  /* -s suppresses open error messages */
  81        fp1 = xfopen_stdin(filename1);
  82        fp2 = xfopen_stdin(filename2);
  83        if (fp1 == fp2) {               /* Paranoia check... stdin == stdin? */
  84                /* Note that we don't bother reading stdin.  Neither does gnu wc.
  85                 * But perhaps we should, so that other apps down the chain don't
  86                 * get the input.  Consider 'echo hello | (cmp - - && cat -)'.
  87                 */
  88                return 0;
  89        }
  90        logmode = LOGMODE_STDIO;
  91
  92        if (opt & CMP_OPT_l)
  93                fmt = fmt_l_opt;
  94        else
  95                fmt = fmt_differ;
  96
  97        if (ENABLE_DESKTOP) {
  98                while (skip1) { getc(fp1); skip1--; }
  99                while (skip2) { getc(fp2); skip2--; }
 100        }
 101        do {
 102                if (max_count >= 0 && --max_count < 0)
 103                        break;
 104                c1 = getc(fp1);
 105                c2 = getc(fp2);
 106                ++char_pos;
 107                if (c1 != c2) {                 /* Remember: a read error may have occurred. */
 108                        retval = 1;             /* But assume the files are different for now. */
 109                        if (c2 == EOF) {
 110                                /* We know that fp1 isn't at EOF or in an error state.  But to
 111                                 * save space below, things are setup to expect an EOF in fp1
 112                                 * if an EOF occurred.  So, swap things around.
 113                                 */
 114                                fp1 = fp2;
 115                                filename1 = filename2;
 116                                c1 = c2;
 117                        }
 118                        if (c1 == EOF) {
 119                                die_if_ferror(fp1, filename1);
 120                                fmt = fmt_eof;  /* Well, no error, so it must really be EOF. */
 121                                outfile = stderr;
 122                                /* There may have been output to stdout (option -l), so
 123                                 * make sure we fflush before writing to stderr. */
 124                                fflush_all();
 125                        }
 126                        if (!(opt & CMP_OPT_s)) {
 127                                if (opt & CMP_OPT_l) {
 128                                        line_pos = c1;  /* line_pos is unused in the -l case. */
 129                                }
 130                                fprintf(outfile, fmt, filename1, filename2, char_pos, line_pos, c2);
 131                                if (opt) {      /* This must be -l since not -s. */
 132                                        /* If we encountered an EOF,
 133                                         * the while check will catch it. */
 134                                        continue;
 135                                }
 136                        }
 137                        break;
 138                }
 139                if (c1 == '\n') {
 140                        ++line_pos;
 141                }
 142        } while (c1 != EOF);
 143
 144        die_if_ferror(fp1, filename1);
 145        die_if_ferror(fp2, filename2);
 146
 147        fflush_stdout_and_exit(retval);
 148}
 149