logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git

cmp.c (3754B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include <assert.h>
  6. #include <errno.h>
  7. #include <stdbool.h>
  8. #include <stdio.h> // fopen, fprintf, getline
  9. #include <stdlib.h> // abort, strtoul
  10. #include <string.h> // strerror
  11. #include <unistd.h> // getopt
  12. static bool opt_s = false, opt_l = false;
  13. static unsigned long max_bytes = 0;
  14. const char *argv0 = "cmp";
  15. #undef MIN
  16. #define MIN(a, b) (((a) < (b)) ? (a) : (b))
  17. static int
  18. do_cmp(FILE *file1, const char *name1, FILE *file2, const char *name2)
  19. {
  20. char *line1 = NULL, *line2 = NULL;
  21. size_t len1 = 0, len2 = 0;
  22. unsigned long pos = 1, ln = 1;
  23. while(true)
  24. {
  25. ssize_t nread1 = getline(&line1, &len1, file1);
  26. if(nread1 < 0)
  27. {
  28. if(!ferror(file1)) return 0;
  29. fprintf(stderr,
  30. "%s: error: Failed to read line %ld from file '%s': %s\n",
  31. argv0,
  32. ln,
  33. name1,
  34. strerror(errno));
  35. return 1;
  36. }
  37. ssize_t nread2 = getline(&line2, &len2, file2);
  38. if(nread2 < 0)
  39. {
  40. if(!ferror(file2))
  41. {
  42. if(!opt_s) fprintf(stderr, "%s: error: EOF on %s line %ld\n", argv0, name2, ln);
  43. return 1;
  44. }
  45. fprintf(stderr,
  46. "%s: error: Failed to read line %ld from file '%s': %s\n",
  47. argv0,
  48. ln,
  49. name1,
  50. strerror(errno));
  51. return 2;
  52. }
  53. for(ssize_t i = 0; i < MIN(nread1, nread2); i++)
  54. {
  55. if(max_bytes != 0 && pos + i >= max_bytes) return 0;
  56. if(line1[i] != line2[i])
  57. {
  58. if(opt_s) return 1;
  59. if(opt_l)
  60. printf("%ld %o %o\n", pos + i, line1[i], line2[i]);
  61. else
  62. printf("%s %s differ: char %zd, line %ld\n", name1, name2, i + 1, ln);
  63. return 1;
  64. }
  65. }
  66. assert(nread1 == nread2);
  67. pos += nread1;
  68. ln++;
  69. }
  70. return 0;
  71. }
  72. static void
  73. usage(void)
  74. {
  75. fprintf(stderr, "Usage: cmp [-l|-s] [-n max_bytes] file1 file2\n");
  76. }
  77. int
  78. main(int argc, char *argv[])
  79. {
  80. char *endptr = NULL;
  81. int c = -1;
  82. while((c = getopt(argc, argv, ":ln:s")) != -1)
  83. {
  84. switch(c)
  85. {
  86. case 'l':
  87. opt_l = true;
  88. break;
  89. case 's':
  90. opt_s = true;
  91. break;
  92. case 'n':
  93. errno = 0;
  94. max_bytes = strtoul(optarg, &endptr, 0);
  95. if(errno != 0)
  96. {
  97. fprintf(stderr, "%s: error: Failed parsing '-n %s': %s\n", argv0, optarg, strerror(errno));
  98. return 1;
  99. }
  100. if(endptr != NULL && endptr[0] != 0)
  101. {
  102. fprintf(stderr,
  103. "%s: error: Non-numeric characters passed to '-n %s': %s\n",
  104. argv0,
  105. optarg,
  106. endptr);
  107. return 1;
  108. }
  109. break;
  110. case ':':
  111. fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
  112. usage();
  113. return 1;
  114. case '?':
  115. fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
  116. usage();
  117. return 1;
  118. default:
  119. abort();
  120. }
  121. }
  122. argc -= optind;
  123. argv += optind;
  124. if(argc != 2)
  125. {
  126. fprintf(stderr, "%s: error: Expected 2 arguments, got %d arguments\n", argv0, argc);
  127. return 1;
  128. }
  129. if(strcmp(argv[0], argv[1]) == 0) return 0;
  130. assert(errno == 0);
  131. FILE *file1 = NULL;
  132. if(argv[0][0] == '-' && argv[0][1] == 0)
  133. file1 = stdin;
  134. else
  135. {
  136. file1 = fopen(argv[0], "r");
  137. if(file1 == NULL)
  138. {
  139. fprintf(stderr, "%s: error: Failed opening file '%s': %s\n", argv0, argv[0], strerror(errno));
  140. return 1;
  141. }
  142. }
  143. FILE *file2 = NULL;
  144. if(argv[1][0] == '-' && argv[1][1] == 0)
  145. file2 = stdin;
  146. else
  147. {
  148. file2 = fopen(argv[1], "r");
  149. if(file2 == NULL)
  150. {
  151. fprintf(stderr, "%s: error: Failed opening file '%s': %s\n", argv0, argv[1], strerror(errno));
  152. return 1;
  153. }
  154. }
  155. int ret = do_cmp(file1, argv[0], file2, argv[1]);
  156. fclose(file1);
  157. fclose(file2);
  158. return ret;
  159. }