logo

utils-std

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

uniq.c (5746B)


  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 <ctype.h> // isblank
  6. #include <errno.h>
  7. #include <stdbool.h>
  8. #include <stdio.h> // getline
  9. #include <stdlib.h> // atoi
  10. #include <string.h> // strncmp
  11. #include <unistd.h> // getopt
  12. enum uniq_mode
  13. {
  14. UNIQ, // default
  15. COUNT,
  16. ONLY_REPEAT,
  17. NO_REPEAT,
  18. };
  19. const char *argv0 = "uniq";
  20. int
  21. main(int argc, char *argv[])
  22. {
  23. enum uniq_mode mode = UNIQ;
  24. unsigned long field = 0, shift = 0;
  25. char *endptr = NULL;
  26. int c = -1;
  27. while((c = getopt(argc, argv, ":cdf:s:u")) != -1)
  28. {
  29. switch(c)
  30. {
  31. case 'c':
  32. if(mode != UNIQ)
  33. {
  34. fprintf(stderr, "%s: error: can only pass one of [-c|-d|-u]\n", argv0);
  35. return 1;
  36. }
  37. mode = COUNT;
  38. break;
  39. case 'd':
  40. if(mode != UNIQ)
  41. {
  42. fprintf(stderr, "%s: error: can only pass one of [-c|-d|-u]\n", argv0);
  43. return 1;
  44. }
  45. mode = ONLY_REPEAT;
  46. break;
  47. case 'f':
  48. errno = 0;
  49. field = strtoul(optarg, &endptr, 0);
  50. if(errno != 0)
  51. {
  52. fprintf(stderr, "%s: error: Failed parsing '-f %s': %s\n", argv0, optarg, strerror(errno));
  53. return 1;
  54. }
  55. if(endptr != NULL && endptr[0] != 0)
  56. {
  57. fprintf(stderr,
  58. "%s: error: Non-numeric characters passed to '-f %s': %s\n",
  59. argv0,
  60. optarg,
  61. endptr);
  62. return 1;
  63. }
  64. break;
  65. case 's':
  66. errno = 0;
  67. shift = strtoul(optarg, &endptr, 0);
  68. if(errno != 0)
  69. {
  70. fprintf(stderr, "%s: error: Failed parsing '-f %s': %s\n", argv0, optarg, strerror(errno));
  71. return 1;
  72. }
  73. if(endptr != NULL && endptr[0] != 0)
  74. {
  75. fprintf(stderr,
  76. "%s: error: Non-numeric characters passed to '-f %s': %s\n",
  77. argv0,
  78. optarg,
  79. endptr);
  80. return 1;
  81. }
  82. break;
  83. case 'u':
  84. if(mode != UNIQ)
  85. {
  86. fprintf(stderr, "%s: error: can only pass one of [-c|-d|-u]\n", argv0);
  87. return 1;
  88. }
  89. mode = NO_REPEAT;
  90. break;
  91. case ':':
  92. fprintf(stderr, "%s: error: Option '-%c' requires an operand\n", argv0, optopt);
  93. return 1;
  94. case '?':
  95. fprintf(stderr, "%s: error: Unhandled option '-%c'\n", argv0, optopt);
  96. return 1;
  97. default:
  98. fprintf(stderr, "%s: error: Unhandled getopt case '%c'\n", argv0, c);
  99. abort();
  100. }
  101. }
  102. argc -= optind;
  103. argv += optind;
  104. FILE *input = stdin;
  105. char *input_name = NULL;
  106. FILE *output = stdout;
  107. char *output_name = NULL;
  108. switch(argc)
  109. {
  110. case 0:
  111. break;
  112. case 1:
  113. input = fopen(argv[0], "r");
  114. input_name = argv[0];
  115. if(input == NULL)
  116. {
  117. fprintf(
  118. stderr, "uniq: error: Failed opening input file '%s': %s\n", argv[0], strerror(errno));
  119. return 1;
  120. }
  121. break;
  122. case 2:
  123. input = fopen(argv[0], "r");
  124. input_name = argv[0];
  125. if(input == NULL)
  126. {
  127. fprintf(
  128. stderr, "uniq: error: Failed opening input file '%s': %s\n", argv[0], strerror(errno));
  129. return 1;
  130. }
  131. output = fopen(argv[1], "w");
  132. output_name = argv[1];
  133. if(output == NULL)
  134. {
  135. fprintf(
  136. stderr, "uniq: error: Failed opening output file '%s': %s\n", argv[1], strerror(errno));
  137. if(fclose(input) != 0)
  138. fprintf(stderr,
  139. "uniq: error: Failed closing input file '%s': %s\n",
  140. input_name,
  141. strerror(errno));
  142. return 1;
  143. }
  144. break;
  145. default:
  146. fprintf(stderr, "uniq: error: Invalid number of arguments (%d), expected [0..2]\n", argc);
  147. return 1;
  148. }
  149. char *first = NULL;
  150. ssize_t first_len = 0;
  151. size_t first_shift = 0;
  152. unsigned counter = 1;
  153. errno = 0;
  154. while(true)
  155. {
  156. char *cur = NULL;
  157. size_t cur_size = 0;
  158. ssize_t cur_len = getline(&cur, &cur_size, input);
  159. size_t cur_shift = shift;
  160. if(cur_len > 0 && cur[cur_len - 1] == '\n')
  161. {
  162. cur[cur_len - 1] = 0;
  163. cur_len--;
  164. }
  165. if(field != 0)
  166. {
  167. ssize_t field_shift = 0;
  168. for(unsigned long i = 0; i < field; i++)
  169. {
  170. while(field_shift < cur_len && isblank(cur[field_shift]))
  171. field_shift++;
  172. while(field_shift < cur_len && !isblank(cur[field_shift]))
  173. field_shift++;
  174. }
  175. cur_shift += field_shift;
  176. }
  177. if(cur_shift > cur_len)
  178. {
  179. free(cur);
  180. cur_size = 0;
  181. cur = NULL;
  182. break;
  183. }
  184. //fprintf(stderr, "[debug] {cur_shift:%d} <%s>\n", cur_shift, cur+cur_shift);
  185. if(first != NULL)
  186. {
  187. if(cur != NULL && (cur_len - cur_shift == first_len - first_shift) &&
  188. strncmp(cur + cur_shift, first + first_shift, cur_len - cur_shift) == 0)
  189. {
  190. counter += 1;
  191. }
  192. else
  193. {
  194. switch(mode)
  195. {
  196. case UNIQ:
  197. fwrite(first, first_len, 1, output);
  198. fprintf(output, "\n");
  199. break;
  200. case ONLY_REPEAT:
  201. if(counter > 1)
  202. {
  203. fwrite(first, first_len, 1, output);
  204. fprintf(output, "\n");
  205. }
  206. break;
  207. case NO_REPEAT:
  208. if(counter == 1)
  209. {
  210. fwrite(first, first_len, 1, output);
  211. fprintf(output, "\n");
  212. }
  213. break;
  214. case COUNT:
  215. fprintf(output, "%d %s\n", counter, first);
  216. break;
  217. }
  218. counter = 1;
  219. free(first);
  220. }
  221. }
  222. if(cur_len < 0)
  223. {
  224. if(cur_size > 0) free(cur);
  225. break;
  226. }
  227. if(counter == 1)
  228. {
  229. first = cur;
  230. first_len = cur_len;
  231. first_shift = cur_shift;
  232. }
  233. }
  234. int ret = 0;
  235. if(errno != 0)
  236. {
  237. fprintf(stderr, "uniq: error: Failed reading: %s\n", strerror(errno));
  238. ret = 1;
  239. }
  240. if(input != stdin)
  241. {
  242. if(fclose(input) != 0)
  243. {
  244. fprintf(
  245. stderr, "uniq: error: Failed closing input file '%s': %s\n", input_name, strerror(errno));
  246. ret = 1;
  247. }
  248. }
  249. if(output != stdout)
  250. {
  251. if(fclose(output) != 0)
  252. {
  253. fprintf(stderr,
  254. "uniq: error: Failed closing output file '%s': %s\n",
  255. output_name,
  256. strerror(errno));
  257. ret = 1;
  258. }
  259. }
  260. return ret;
  261. }