logo

utils-std

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

split.c (7034B)


  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/fs.h" // auto_file_copy
  6. #include "../lib/getopt_nolong.h"
  7. #include "../lib/truncation.h" // apply_size_suffix
  8. #include <errno.h>
  9. #include <fcntl.h> // open
  10. #include <limits.h> // NAME_MAX
  11. #include <stdio.h> // fprintf
  12. #include <stdlib.h> // strtoul
  13. #include <string.h> // strerror
  14. #include <sys/stat.h> // fstat
  15. #include <unistd.h> // getopt
  16. const char *argv0 = "split";
  17. const char *name = "x";
  18. size_t name_len = 1;
  19. size_t suffix_len = 2, bytes = 0, lines = 0;
  20. char *name_in = NULL;
  21. static int
  22. base26(int id, char *str)
  23. {
  24. memcpy(str, name, name_len);
  25. memset(str + name_len, 'a', suffix_len);
  26. size_t id_p = name_len + suffix_len;
  27. do
  28. {
  29. str[id_p--] = 'a' + (id % 26);
  30. id /= 26;
  31. } while(id > 0 && id_p > name_len);
  32. if(id_p <= name_len)
  33. {
  34. fprintf(stderr,
  35. "%s: error: Failed representing %d into suffix of length %zu\n",
  36. argv0,
  37. id,
  38. suffix_len);
  39. return -1;
  40. }
  41. return 0;
  42. }
  43. static int
  44. split_bytes(void)
  45. {
  46. int fd_in = STDIN_FILENO;
  47. if(name_in != NULL)
  48. {
  49. fd_in = open(name_in, O_RDONLY | O_NOCTTY);
  50. if(fd_in < 0)
  51. {
  52. fprintf(stderr, "%s: error: Failed opening '%s' file: %s\n", argv0, name_in, strerror(errno));
  53. return 1;
  54. }
  55. }
  56. struct stat fd_in_stat;
  57. if(fstat(fd_in, &fd_in_stat) != 0)
  58. {
  59. fprintf(stderr,
  60. "%s: error: Failed getting status from file '%s': %s",
  61. argv0,
  62. name_in,
  63. strerror(errno));
  64. close(fd_in);
  65. return 1;
  66. }
  67. posix_fadvise(fd_in, 0, 0, POSIX_FADV_SEQUENTIAL);
  68. errno = 0;
  69. int err = 0;
  70. off_t wrote = 0;
  71. int split_id = 0;
  72. while(wrote < fd_in_stat.st_size)
  73. {
  74. char name_out[NAME_MAX] = "";
  75. if(base26(split_id++, name_out) < 0) return 1;
  76. int fd_out = open(name_out, O_WRONLY | O_NOCTTY | O_CREAT, 0644);
  77. if(fd_out < 0)
  78. {
  79. fprintf(
  80. stderr, "%s: error: Failed opening '%s' file: %s\n", argv0, name_out, strerror(errno));
  81. err = 1;
  82. break;
  83. }
  84. posix_fadvise(fd_out, 0, 0, POSIX_FADV_SEQUENTIAL);
  85. errno = 0;
  86. int ret = auto_file_copy(fd_in, fd_out, bytes, 0);
  87. if(ret < 0)
  88. {
  89. fprintf(stderr,
  90. "%s: error: Failed copying from file '%s' to file '%s': %s\n",
  91. argv0,
  92. name_in ? name_in : "<stdin>",
  93. name_out,
  94. strerror(errno));
  95. close(fd_out);
  96. err = 1;
  97. break;
  98. }
  99. wrote += ret;
  100. if(close(fd_out) < 0)
  101. {
  102. fprintf(
  103. stderr, "%s: error: Failed closing file '%s': %s\n", argv0, name_out, strerror(errno));
  104. err = 1;
  105. break;
  106. }
  107. }
  108. if(name_in != NULL) close(fd_in);
  109. return err;
  110. }
  111. static int
  112. split_lines(void)
  113. {
  114. FILE *in = stdin;
  115. if(name_in != NULL)
  116. {
  117. in = fopen(name_in, "r");
  118. if(in == NULL)
  119. {
  120. fprintf(stderr, "%s: error: Failed opening '%s' file: %s\n", argv0, name_in, strerror(errno));
  121. return 1;
  122. }
  123. }
  124. int err = 0;
  125. char *line = NULL;
  126. size_t line_len = 0;
  127. int split_id = 0;
  128. while(true)
  129. {
  130. if(feof(in)) break;
  131. if(ferror(in))
  132. {
  133. fprintf(stderr,
  134. "%s: error: Failed reading line from file '%s': %s\n",
  135. argv0,
  136. name_in,
  137. strerror(errno));
  138. err = 1;
  139. break;
  140. }
  141. char name_out[NAME_MAX] = "";
  142. if(base26(split_id++, name_out) < 0)
  143. {
  144. err = 1;
  145. break;
  146. }
  147. FILE *out = NULL;
  148. for(size_t i = 0; i < lines; i++)
  149. {
  150. ssize_t nread = getline(&line, &line_len, in);
  151. if(nread < 0)
  152. {
  153. if(errno != 0)
  154. {
  155. fprintf(stderr,
  156. "%s: error: Failed reading line from file '%s': %s\n",
  157. argv0,
  158. name_in,
  159. strerror(errno));
  160. err = 1;
  161. }
  162. break;
  163. }
  164. if(out == NULL)
  165. {
  166. out = fopen(name_out, "w");
  167. if(out == NULL)
  168. {
  169. fprintf(stderr,
  170. "%s: error: Failed opening '%s' file: %s\n",
  171. argv0,
  172. name_out,
  173. strerror(errno));
  174. err = 1;
  175. break;
  176. }
  177. }
  178. if(fwrite(line, nread, 1, out) == 0)
  179. {
  180. fprintf(stderr,
  181. "%s: error: Failed writing line to file '%s': %s\n",
  182. argv0,
  183. name_out,
  184. strerror(errno));
  185. err = 1;
  186. break;
  187. }
  188. }
  189. if(out != NULL)
  190. {
  191. if(fclose(out) < 0)
  192. {
  193. fprintf(
  194. stderr, "%s: error: Failed closing file '%s': %s\n", argv0, name_out, strerror(errno));
  195. err = 1;
  196. break;
  197. }
  198. }
  199. if(err != 0) break;
  200. }
  201. if(line_len > 0) free(line);
  202. if(name_in != NULL) fclose(in);
  203. return err;
  204. }
  205. static const char *error_opt_b_l = "%s: error: Options -b and -l are mutually exclusive\n";
  206. int
  207. main(int argc, char *argv[])
  208. {
  209. for(int c = -1; (c = getopt_nolong(argc, argv, ":a:b:l:")) != -1;)
  210. {
  211. char *endptr = NULL;
  212. switch(c)
  213. {
  214. case 'a':
  215. suffix_len = strtoul(optarg, &endptr, 0);
  216. if(suffix_len == 0)
  217. {
  218. fprintf(stderr, "%s: error: Failed parsing '-a %s': %s\n", argv0, optarg, strerror(errno));
  219. return 1;
  220. }
  221. if(endptr != NULL && *endptr != '\0')
  222. {
  223. fprintf(stderr,
  224. "%s: error: Invalid trailing characters in '-a %s': %s\n",
  225. argv0,
  226. optarg,
  227. endptr);
  228. return 1;
  229. }
  230. break;
  231. case 'b':
  232. {
  233. if(lines != 0)
  234. {
  235. fprintf(stderr, error_opt_b_l, argv0);
  236. return 1;
  237. }
  238. unsigned long opt_b = strtoul(optarg, &endptr, 0);
  239. if(opt_b == 0)
  240. {
  241. fprintf(stderr, "%s: error: Failed parsing '-b %s': %s\n", argv0, optarg, strerror(errno));
  242. return 1;
  243. }
  244. if(endptr != NULL && *endptr != 0)
  245. if(apply_size_suffix(&opt_b, endptr) != 0) return 1;
  246. bytes = opt_b;
  247. lines = 0;
  248. break;
  249. }
  250. case 'l':
  251. if(bytes != 0)
  252. {
  253. fprintf(stderr, error_opt_b_l, argv0);
  254. return 1;
  255. }
  256. lines = strtoul(optarg, &endptr, 0);
  257. if(lines == 0)
  258. {
  259. fprintf(stderr, "%s: error: Failed parsing '-l %s': %s\n", argv0, optarg, strerror(errno));
  260. return 1;
  261. }
  262. if(endptr != NULL && *endptr != '\0')
  263. {
  264. fprintf(stderr,
  265. "%s: error: Invalid trailing characters in '-l %s': %s\n",
  266. argv0,
  267. optarg,
  268. endptr);
  269. return 1;
  270. }
  271. break;
  272. case ':':
  273. fprintf(stderr, "%s: error: Option '-%c' requires an operand\n", argv0, optopt);
  274. return 1;
  275. default:
  276. fprintf(stderr, "%s: error: Unhandled option '-%c'\n", argv0, optopt);
  277. return 1;
  278. }
  279. }
  280. argc -= optind;
  281. argv += optind;
  282. if(lines == 0 && bytes == 0) lines = 1000;
  283. if(argc > 2 || argc < 0)
  284. {
  285. fprintf(stderr, "%s: error: Expected 0, 1, or 2 arguments, got %d\n", argv0, argc);
  286. return 1;
  287. }
  288. else if(argc >= 1)
  289. {
  290. name_in = argv[0];
  291. if(argc == 2) name = argv[1];
  292. }
  293. name_len = strlen(name);
  294. if(name_len + suffix_len > NAME_MAX)
  295. {
  296. fprintf(stderr,
  297. "%s: error: name(%zd bytes) + suffix_length(%zd bytes) > NAME_MAX(%d bytes)\n",
  298. argv0,
  299. name_len,
  300. suffix_len,
  301. NAME_MAX);
  302. return 1;
  303. }
  304. if(bytes != 0) return split_bytes();
  305. return split_lines();
  306. }