logo

utils-std

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

cmp.c (5271B)


  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 "../lib/getopt_nolong.h"
  6. #include <assert.h>
  7. #include <errno.h>
  8. #include <fcntl.h> // open, posix_fadvise
  9. #include <stdbool.h>
  10. #include <stdio.h> // fopen, fprintf, getline
  11. #include <stdlib.h> // abort, strtoul
  12. #include <string.h> // strerror
  13. #include <sys/stat.h> // fstat
  14. #include <unistd.h> // getopt
  15. static bool opt_s = false, opt_l = false;
  16. static unsigned long max_bytes = 0;
  17. const char *argv0 = "cmp";
  18. #undef MIN
  19. #define MIN(a, b) (((a) < (b)) ? (a) : (b))
  20. static int
  21. do_cmp(FILE *file1, const char *name1, FILE *file2, const char *name2)
  22. {
  23. char *line1 = NULL, *line2 = NULL;
  24. size_t len1 = 0, len2 = 0;
  25. unsigned long pos = 1, ln = 1;
  26. errno = 0;
  27. while(true)
  28. {
  29. ssize_t nread1 = getline(&line1, &len1, file1);
  30. if(nread1 < 0)
  31. {
  32. if(!ferror(file1)) return 0;
  33. fprintf(stderr,
  34. "%s: error: Failed to read line %ld from file '%s': %s\n",
  35. argv0,
  36. ln,
  37. name1,
  38. strerror(errno));
  39. return 2;
  40. }
  41. ssize_t nread2 = getline(&line2, &len2, file2);
  42. if(nread2 < 0)
  43. {
  44. if(!ferror(file2))
  45. {
  46. if(!opt_s) fprintf(stderr, "%s: error: EOF on %s line %ld\n", argv0, name2, ln);
  47. return 2;
  48. }
  49. fprintf(stderr,
  50. "%s: error: Failed to read line %ld from file '%s': %s\n",
  51. argv0,
  52. ln,
  53. name1,
  54. strerror(errno));
  55. return 2;
  56. }
  57. for(ssize_t i = 0; i < MIN(nread1, nread2); i++)
  58. {
  59. if(max_bytes != 0 && pos + i >= max_bytes) return 0;
  60. if(line1[i] != line2[i])
  61. {
  62. if(opt_s) return 1;
  63. if(opt_l)
  64. printf("%ld %o %o\n", pos + i, line1[i], line2[i]);
  65. else
  66. printf("%s %s differ: char %zd, line %ld\n", name1, name2, i + 1, ln);
  67. return 1;
  68. }
  69. }
  70. assert(nread1 == nread2);
  71. pos += nread1;
  72. ln++;
  73. }
  74. return 0;
  75. }
  76. static bool
  77. fstat_cmp(int fd1, char *name1, int fd2, char *name2)
  78. {
  79. struct stat buf1, buf2;
  80. errno = 0;
  81. if(fstat(fd1, &buf1) != 0)
  82. {
  83. fprintf(stderr,
  84. "%s: warning: Failed getting status for file '%s': %s\n",
  85. argv0,
  86. name1,
  87. strerror(errno));
  88. return false;
  89. }
  90. errno = 0;
  91. if(fstat(fd2, &buf2) != 0)
  92. {
  93. fprintf(stderr,
  94. "%s: warning: Failed getting status for file '%s': %s\n",
  95. argv0,
  96. name2,
  97. strerror(errno));
  98. return false;
  99. }
  100. return buf1.st_ino == buf2.st_ino && buf1.st_dev == buf2.st_dev;
  101. }
  102. static void
  103. usage(void)
  104. {
  105. fprintf(stderr, "Usage: cmp [-l|-s] [-n max_bytes] file1 file2\n");
  106. }
  107. int
  108. main(int argc, char *argv[])
  109. {
  110. char *endptr = NULL;
  111. for(int c = -1; (c = getopt_nolong(argc, argv, ":ln:s")) != -1;)
  112. {
  113. switch(c)
  114. {
  115. case 'l':
  116. opt_l = true;
  117. break;
  118. case 's':
  119. opt_s = true;
  120. break;
  121. case 'n':
  122. errno = 0;
  123. max_bytes = strtoul(optarg, &endptr, 0);
  124. if(errno != 0)
  125. {
  126. fprintf(stderr, "%s: error: Failed parsing '-n %s': %s\n", argv0, optarg, strerror(errno));
  127. return 2;
  128. }
  129. if(endptr != NULL && endptr[0] != 0)
  130. {
  131. fprintf(stderr,
  132. "%s: error: Non-numeric characters passed to '-n %s': %s\n",
  133. argv0,
  134. optarg,
  135. endptr);
  136. return 2;
  137. }
  138. break;
  139. case ':':
  140. fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
  141. usage();
  142. return 2;
  143. case '?':
  144. GETOPT_UNKNOWN_OPT
  145. usage();
  146. return 2;
  147. default:
  148. abort();
  149. }
  150. }
  151. argc -= optind;
  152. argv += optind;
  153. if(argc != 2)
  154. {
  155. fprintf(stderr, "%s: error: Expected 2 arguments, got %d arguments\n", argv0, argc);
  156. return 2;
  157. }
  158. if(strcmp(argv[0], argv[1]) == 0) return 0;
  159. int fd1 = STDIN_FILENO;
  160. if(!(argv[0][0] == '-' && argv[0][1] == 0))
  161. {
  162. errno = 0;
  163. fd1 = open(argv[0], O_RDONLY);
  164. if(errno != 0)
  165. {
  166. fprintf(stderr, "%s: error: Failed opening file '%s': %s\n", argv0, argv[0], strerror(errno));
  167. return 2;
  168. }
  169. }
  170. int fd2 = STDIN_FILENO;
  171. if(!(argv[1][0] == '-' && argv[1][1] == 0))
  172. {
  173. errno = 0;
  174. fd2 = open(argv[1], O_RDONLY);
  175. if(errno != 0)
  176. {
  177. fprintf(stderr, "%s: error: Failed opening file '%s': %s\n", argv0, argv[1], strerror(errno));
  178. return 2;
  179. }
  180. }
  181. if(fstat_cmp(fd1, argv[0], fd2, argv[1]))
  182. {
  183. if(fd1 != STDIN_FILENO) close(fd1);
  184. if(fd2 != STDIN_FILENO) close(fd2);
  185. return 0;
  186. }
  187. FILE *file1 = stdin;
  188. FILE *file2 = stdin;
  189. if(fd1 != STDIN_FILENO)
  190. {
  191. errno = 0;
  192. file1 = fdopen(fd1, "r");
  193. if(file1 == (FILE *)NULL)
  194. {
  195. fprintf(stderr,
  196. "%s: error: Failed associating stream for file '%s': %s\n",
  197. argv0,
  198. argv[0],
  199. strerror(errno));
  200. return 2;
  201. }
  202. posix_fadvise(fd1, 0, 0, POSIX_FADV_SEQUENTIAL);
  203. }
  204. if(fd2 != STDIN_FILENO)
  205. {
  206. errno = 0;
  207. file2 = fdopen(fd2, "r");
  208. if(file2 == (FILE *)NULL)
  209. {
  210. fprintf(stderr,
  211. "%s: error: Failed associating stream for file '%s': %s\n",
  212. argv0,
  213. argv[1],
  214. strerror(errno));
  215. return 2;
  216. }
  217. posix_fadvise(fd2, 0, 0, POSIX_FADV_SEQUENTIAL);
  218. }
  219. int ret = do_cmp(file1, argv[0], file2, argv[1]);
  220. if(file1 != stdin) fclose(file1);
  221. if(file2 != stdin) fclose(file2);
  222. return ret;
  223. }