cmp.c 3.7 KB

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