logo

utils-std

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

comm.c (5830B)


  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 "../libutils/getopt_nolong.h"
  6. #include <errno.h>
  7. #include <locale.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <unistd.h>
  12. enum comm_out
  13. {
  14. COMM_OUT_NONE = 0,
  15. COMM_OUT_FILE1 = 1 << 0,
  16. COMM_OUT_FILE2 = 1 << 1,
  17. COMM_OUT_COMM = 1 << 2,
  18. COMM_OUT_ALL = COMM_OUT_FILE1 | COMM_OUT_FILE2 | COMM_OUT_COMM,
  19. };
  20. const char *argv0 = "comm";
  21. enum comm_out c_out = COMM_OUT_ALL;
  22. static void
  23. usage(void)
  24. {
  25. fprintf(stderr, "Usage: comm [-123] file1 file2\n");
  26. }
  27. static int
  28. drain1(FILE *in, const char *fname, char *buf, size_t *bufsiz)
  29. {
  30. errno = 0;
  31. if(!(c_out & COMM_OUT_FILE1))
  32. {
  33. fflush(stdout);
  34. return 0;
  35. }
  36. if(buf) printf("%s", buf);
  37. for(;;)
  38. {
  39. size_t nread = fread(buf, 1, *bufsiz, in);
  40. if(errno != 0)
  41. {
  42. fprintf(
  43. stderr, "comm: error: Failed reading from file1 \"%s\": %s\n", fname, strerror(errno));
  44. return 1;
  45. }
  46. if(nread == 0) break; // EOF
  47. fwrite(buf, 1, nread, stdout);
  48. if(errno != 0)
  49. {
  50. fprintf(stderr,
  51. "comm: error: Failed writing file1 \"%s\" data to stdout: %s\n",
  52. fname,
  53. strerror(errno));
  54. return 1;
  55. }
  56. }
  57. fflush(stdout);
  58. return 0;
  59. }
  60. static int
  61. drain2(FILE *in, const char *fname, char *buf, size_t *bufsiz)
  62. {
  63. errno = 0;
  64. if(!(c_out & COMM_OUT_FILE2))
  65. {
  66. fflush(stdout);
  67. return 0;
  68. }
  69. if(buf) printf("\t%s", buf);
  70. for(;;)
  71. {
  72. ssize_t nread = getline(&buf, bufsiz, in);
  73. if(nread < 0)
  74. {
  75. if(errno == 0) break; // EOF
  76. fprintf(stderr,
  77. "comm: error: Failed reading line from file2 \"%s\": %s\n",
  78. fname,
  79. strerror(errno));
  80. return 1;
  81. }
  82. errno = 0;
  83. fputc('\t', stdout);
  84. fwrite(buf, 1, nread, stdout);
  85. if(errno != 0)
  86. {
  87. fprintf(stderr,
  88. "comm: error: Failed writing file2 \"%s\" data to stdout: %s\n",
  89. fname,
  90. strerror(errno));
  91. return 1;
  92. }
  93. }
  94. fflush(stdout);
  95. return 0;
  96. }
  97. int
  98. main(int argc, char *argv[])
  99. {
  100. if(setlocale(LC_ALL, "") == NULL)
  101. {
  102. fprintf(stderr,
  103. "%s: warning: Failed loading locales. setlocale(LC_ALL, \"\"): %s\n",
  104. argv0,
  105. strerror(errno));
  106. }
  107. errno = 0;
  108. for(int c = -1; (c = getopt_nolong(argc, argv, ":123")) != -1;)
  109. {
  110. switch(c)
  111. {
  112. case '1':
  113. c_out &= ~COMM_OUT_FILE1;
  114. break;
  115. case '2':
  116. c_out &= ~COMM_OUT_FILE2;
  117. break;
  118. case '3':
  119. c_out &= ~COMM_OUT_COMM;
  120. break;
  121. case '?':
  122. GETOPT_UNKNOWN_OPT
  123. usage();
  124. return 1;
  125. default:
  126. abort();
  127. }
  128. }
  129. argc -= optind;
  130. argv += optind;
  131. if(argc != 2)
  132. {
  133. fprintf(stderr, "comm: error: Expected 2 arguments, got %d\n", argc);
  134. usage();
  135. return 1;
  136. }
  137. const char *fname1 = argv[0];
  138. const char *fname2 = argv[1];
  139. FILE *file1 = NULL, *file2 = NULL;
  140. if(fname1[0] == '-' && !fname1[1])
  141. {
  142. file1 = stdin;
  143. }
  144. else
  145. {
  146. file1 = fopen(fname1, "r");
  147. if(!file1)
  148. {
  149. fprintf(stderr, "comm: error: Failed opening file1 \"%s\": %s\n", fname1, strerror(errno));
  150. return 1;
  151. }
  152. }
  153. if(fname2[0] == '-' && !fname2[1])
  154. {
  155. if(fname1[0] == '-' && !fname1[1])
  156. {
  157. fprintf(stderr, "comm: error: file1 and file2 cannot be both stdin\n");
  158. usage();
  159. return 1;
  160. }
  161. file2 = stdin;
  162. }
  163. else
  164. {
  165. file2 = fopen(fname2, "r");
  166. if(!file2)
  167. {
  168. fprintf(stderr, "comm: error: Failed opening file2 \"%s\": %s\n", fname2, strerror(errno));
  169. return 1;
  170. }
  171. }
  172. char *f1 = NULL, *f2 = NULL;
  173. size_t f1n = 0, f2n = 0;
  174. errno = 0;
  175. if(getline(&f1, &f1n, file1) < 0)
  176. {
  177. if(errno != 0)
  178. {
  179. fprintf(stderr,
  180. "comm: error: Failed reading line from file1 \"%s\": %s\n",
  181. fname1,
  182. strerror(errno));
  183. return 1;
  184. }
  185. return drain2(file2, fname2, f2, &f2n);
  186. }
  187. errno = 0;
  188. if(getline(&f2, &f2n, file2) < 0)
  189. {
  190. if(errno != 0)
  191. {
  192. fprintf(stderr,
  193. "comm: error: Failed reading line from file2 \"%s\": %s\n",
  194. fname2,
  195. strerror(errno));
  196. return 1;
  197. }
  198. return drain1(file1, fname1, f1, &f1n);
  199. }
  200. int d = strcoll(f1, f2);
  201. for(;;)
  202. {
  203. // TODO: Compare newly obtained line with it's previous one, to check if the file is sorted
  204. while(d == 0)
  205. {
  206. if(c_out & COMM_OUT_COMM) printf("\t\t%s", f1);
  207. f1 = NULL;
  208. f2 = NULL;
  209. errno = 0;
  210. if(getline(&f1, &f1n, file1) < 0)
  211. {
  212. if(errno != 0)
  213. {
  214. fprintf(stderr,
  215. "comm: error: Failed reading line from file1 \"%s\": %s\n",
  216. fname1,
  217. strerror(errno));
  218. return 1;
  219. }
  220. return drain2(file2, fname2, f2, &f2n);
  221. }
  222. errno = 0;
  223. if(getline(&f2, &f2n, file2) < 0)
  224. {
  225. if(errno != 0)
  226. {
  227. fprintf(stderr,
  228. "comm: error: Failed reading line from file2 \"%s\": %s\n",
  229. fname2,
  230. strerror(errno));
  231. return 1;
  232. }
  233. return drain1(file1, fname1, f1, &f1n);
  234. }
  235. d = strcoll(f1, f2);
  236. }
  237. while(d > 0)
  238. {
  239. if(c_out & COMM_OUT_FILE2) printf("\t%s", f2);
  240. f2 = NULL;
  241. errno = 0;
  242. if(getline(&f2, &f2n, file2) < 0)
  243. {
  244. if(errno != 0)
  245. {
  246. fprintf(stderr,
  247. "comm: error: Failed reading line from file2 \"%s\": %s\n",
  248. fname2,
  249. strerror(errno));
  250. return 1;
  251. }
  252. return drain1(file1, fname1, f1, &f1n);
  253. }
  254. d = strcoll(f1, f2);
  255. }
  256. while(d < 0)
  257. {
  258. if(c_out & COMM_OUT_FILE1) printf("%s", f1);
  259. f1 = NULL;
  260. errno = 0;
  261. if(getline(&f1, &f1n, file1) < 0)
  262. {
  263. if(errno != 0)
  264. {
  265. fprintf(stderr,
  266. "comm: error: Failed reading line from file1 \"%s\": %s\n",
  267. fname1,
  268. strerror(errno));
  269. return 1;
  270. }
  271. return drain2(file2, fname2, f2, &f2n);
  272. }
  273. d = strcoll(f1, f2);
  274. }
  275. }
  276. return 0;
  277. }