logo

utils-std

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

split.c (6901B)


  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. int err = 0;
  68. off_t wrote = 0;
  69. int split_id = 0;
  70. while(wrote < fd_in_stat.st_size)
  71. {
  72. char name_out[NAME_MAX] = "";
  73. if(base26(split_id++, name_out) < 0) return 1;
  74. int fd_out = open(name_out, O_WRONLY | O_NOCTTY | O_CREAT, 0644);
  75. if(fd_out < 0)
  76. {
  77. fprintf(
  78. stderr, "%s: error: Failed opening '%s' file: %s\n", argv0, name_out, strerror(errno));
  79. err = 1;
  80. break;
  81. }
  82. int ret = auto_file_copy(fd_in, fd_out, bytes, 0);
  83. if(ret < 0)
  84. {
  85. fprintf(stderr,
  86. "%s: error: Failed copying from file '%s' to file '%s': %s\n",
  87. argv0,
  88. name_in ? name_in : "<stdin>",
  89. name_out,
  90. strerror(errno));
  91. close(fd_out);
  92. err = 1;
  93. break;
  94. }
  95. wrote += ret;
  96. if(close(fd_out) < 0)
  97. {
  98. fprintf(
  99. stderr, "%s: error: Failed closing file '%s': %s\n", argv0, name_out, strerror(errno));
  100. err = 1;
  101. break;
  102. }
  103. }
  104. if(name_in != NULL) close(fd_in);
  105. return err;
  106. }
  107. static int
  108. split_lines(void)
  109. {
  110. FILE *in = stdin;
  111. if(name_in != NULL)
  112. {
  113. in = fopen(name_in, "r");
  114. if(in == NULL)
  115. {
  116. fprintf(stderr, "%s: error: Failed opening '%s' file: %s\n", argv0, name_in, strerror(errno));
  117. return 1;
  118. }
  119. }
  120. int err = 0;
  121. char *line = NULL;
  122. size_t line_len = 0;
  123. int split_id = 0;
  124. while(true)
  125. {
  126. if(feof(in)) break;
  127. if(ferror(in))
  128. {
  129. fprintf(stderr,
  130. "%s: error: Failed reading line from file '%s': %s\n",
  131. argv0,
  132. name_in,
  133. strerror(errno));
  134. err = 1;
  135. break;
  136. }
  137. char name_out[NAME_MAX] = "";
  138. if(base26(split_id++, name_out) < 0)
  139. {
  140. err = 1;
  141. break;
  142. }
  143. FILE *out = NULL;
  144. for(size_t i = 0; i < lines; i++)
  145. {
  146. ssize_t nread = getline(&line, &line_len, in);
  147. if(nread < 0)
  148. {
  149. if(errno != 0)
  150. {
  151. fprintf(stderr,
  152. "%s: error: Failed reading line from file '%s': %s\n",
  153. argv0,
  154. name_in,
  155. strerror(errno));
  156. err = 1;
  157. }
  158. break;
  159. }
  160. if(out == NULL)
  161. {
  162. out = fopen(name_out, "w");
  163. if(out == NULL)
  164. {
  165. fprintf(stderr,
  166. "%s: error: Failed opening '%s' file: %s\n",
  167. argv0,
  168. name_out,
  169. strerror(errno));
  170. err = 1;
  171. break;
  172. }
  173. }
  174. if(fwrite(line, nread, 1, out) == 0)
  175. {
  176. fprintf(stderr,
  177. "%s: error: Failed writing line to file '%s': %s\n",
  178. argv0,
  179. name_out,
  180. strerror(errno));
  181. err = 1;
  182. break;
  183. }
  184. }
  185. if(out != NULL)
  186. {
  187. if(fclose(out) < 0)
  188. {
  189. fprintf(
  190. stderr, "%s: error: Failed closing file '%s': %s\n", argv0, name_out, strerror(errno));
  191. err = 1;
  192. break;
  193. }
  194. }
  195. if(err != 0) break;
  196. }
  197. if(line_len > 0) free(line);
  198. if(name_in != NULL) fclose(in);
  199. return err;
  200. }
  201. static const char *error_opt_b_l = "%s: error: Options -b and -l are mutually exclusive\n";
  202. int
  203. main(int argc, char *argv[])
  204. {
  205. for(int c = -1; (c = getopt_nolong(argc, argv, ":a:b:l:")) != -1;)
  206. {
  207. char *endptr = NULL;
  208. switch(c)
  209. {
  210. case 'a':
  211. suffix_len = strtoul(optarg, &endptr, 0);
  212. if(suffix_len == 0)
  213. {
  214. fprintf(stderr, "%s: error: Failed parsing '-a %s': %s\n", argv0, optarg, strerror(errno));
  215. return 1;
  216. }
  217. if(endptr != NULL && *endptr != '\0')
  218. {
  219. fprintf(stderr,
  220. "%s: error: Invalid trailing characters in '-a %s': %s\n",
  221. argv0,
  222. optarg,
  223. endptr);
  224. return 1;
  225. }
  226. break;
  227. case 'b':
  228. {
  229. if(lines != 0)
  230. {
  231. fprintf(stderr, error_opt_b_l, argv0);
  232. return 1;
  233. }
  234. unsigned long opt_b = strtoul(optarg, &endptr, 0);
  235. if(opt_b == 0)
  236. {
  237. fprintf(stderr, "%s: error: Failed parsing '-b %s': %s\n", argv0, optarg, strerror(errno));
  238. return 1;
  239. }
  240. if(endptr != NULL && *endptr != 0)
  241. if(apply_size_suffix(&opt_b, endptr) != 0) return 1;
  242. bytes = opt_b;
  243. lines = 0;
  244. break;
  245. }
  246. case 'l':
  247. if(bytes != 0)
  248. {
  249. fprintf(stderr, error_opt_b_l, argv0);
  250. return 1;
  251. }
  252. lines = strtoul(optarg, &endptr, 0);
  253. if(lines == 0)
  254. {
  255. fprintf(stderr, "%s: error: Failed parsing '-l %s': %s\n", argv0, optarg, strerror(errno));
  256. return 1;
  257. }
  258. if(endptr != NULL && *endptr != '\0')
  259. {
  260. fprintf(stderr,
  261. "%s: error: Invalid trailing characters in '-l %s': %s\n",
  262. argv0,
  263. optarg,
  264. endptr);
  265. return 1;
  266. }
  267. break;
  268. case ':':
  269. fprintf(stderr, "%s: error: Option '-%c' requires an operand\n", argv0, optopt);
  270. return 1;
  271. default:
  272. fprintf(stderr, "%s: error: Unhandled option '-%c'\n", argv0, optopt);
  273. return 1;
  274. }
  275. }
  276. argc -= optind;
  277. argv += optind;
  278. if(lines == 0 && bytes == 0) lines = 1000;
  279. if(argc > 2 || argc < 0)
  280. {
  281. fprintf(stderr, "%s: error: Expected 0, 1, or 2 arguments, got %d\n", argv0, argc);
  282. return 1;
  283. }
  284. else if(argc >= 1)
  285. {
  286. name_in = argv[0];
  287. if(argc == 2) name = argv[1];
  288. }
  289. name_len = strlen(name);
  290. if(name_len + suffix_len > NAME_MAX)
  291. {
  292. fprintf(stderr,
  293. "%s: error: name(%zd bytes) + suffix_length(%zd bytes) > NAME_MAX(%d bytes)\n",
  294. argv0,
  295. name_len,
  296. suffix_len,
  297. NAME_MAX);
  298. return 1;
  299. }
  300. if(bytes != 0) return split_bytes();
  301. return split_lines();
  302. }