logo

utils-std

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

split.c (6869B)


  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/truncation.h" // apply_size_suffix
  7. #include <errno.h>
  8. #include <fcntl.h> // open
  9. #include <limits.h> // NAME_MAX
  10. #include <stdio.h> // fprintf
  11. #include <stdlib.h> // strtoul
  12. #include <string.h> // strerror
  13. #include <sys/stat.h> // fstat
  14. #include <unistd.h> // getopt
  15. const char *argv0 = "split";
  16. const char *name = "x";
  17. size_t name_len = 1;
  18. size_t suffix_len = 2, bytes = 0, lines = 0;
  19. char *name_in = NULL;
  20. static int
  21. base26(int id, char *str)
  22. {
  23. memcpy(str, name, name_len);
  24. memset(str + name_len, 'a', suffix_len);
  25. size_t id_p = name_len + suffix_len;
  26. do
  27. {
  28. str[id_p--] = 'a' + (id % 26);
  29. id /= 26;
  30. } while(id > 0 && id_p > name_len);
  31. if(id_p <= name_len)
  32. {
  33. fprintf(stderr,
  34. "%s: error: Failed representing %d into suffix of length %zu\n",
  35. argv0,
  36. id,
  37. suffix_len);
  38. return -1;
  39. }
  40. return 0;
  41. }
  42. static int
  43. split_bytes(void)
  44. {
  45. int fd_in = STDIN_FILENO;
  46. if(name_in != NULL)
  47. {
  48. fd_in = open(name_in, O_RDONLY | O_NOCTTY);
  49. if(fd_in < 0)
  50. {
  51. fprintf(stderr, "%s: error: Failed opening '%s' file: %s\n", argv0, name_in, strerror(errno));
  52. return 1;
  53. }
  54. }
  55. struct stat fd_in_stat;
  56. if(fstat(fd_in, &fd_in_stat) != 0)
  57. {
  58. fprintf(stderr,
  59. "%s: error: Failed getting status from file '%s': %s",
  60. argv0,
  61. name_in,
  62. strerror(errno));
  63. close(fd_in);
  64. return 1;
  65. }
  66. int err = 0;
  67. off_t wrote = 0;
  68. int split_id = 0;
  69. while(wrote < fd_in_stat.st_size)
  70. {
  71. char name_out[NAME_MAX] = "";
  72. if(base26(split_id++, name_out) < 0) return 1;
  73. int fd_out = open(name_out, O_WRONLY | O_NOCTTY | O_CREAT, 0644);
  74. if(fd_out < 0)
  75. {
  76. fprintf(
  77. stderr, "%s: error: Failed opening '%s' file: %s\n", argv0, name_out, strerror(errno));
  78. err = 1;
  79. break;
  80. }
  81. int ret = auto_file_copy(fd_in, fd_out, bytes, 0);
  82. if(ret < 0)
  83. {
  84. fprintf(stderr,
  85. "%s: error: Failed copying from file '%s' to file '%s': %s\n",
  86. argv0,
  87. name_in ? name_in : "<stdin>",
  88. name_out,
  89. strerror(errno));
  90. close(fd_out);
  91. err = 1;
  92. break;
  93. }
  94. wrote += ret;
  95. if(close(fd_out) < 0)
  96. {
  97. fprintf(
  98. stderr, "%s: error: Failed closing file '%s': %s\n", argv0, name_out, strerror(errno));
  99. err = 1;
  100. break;
  101. }
  102. }
  103. if(name_in != NULL) close(fd_in);
  104. return err;
  105. }
  106. static int
  107. split_lines(void)
  108. {
  109. FILE *in = stdin;
  110. if(name_in != NULL)
  111. {
  112. in = fopen(name_in, "r");
  113. if(in == NULL)
  114. {
  115. fprintf(stderr, "%s: error: Failed opening '%s' file: %s\n", argv0, name_in, strerror(errno));
  116. return 1;
  117. }
  118. }
  119. int err = 0;
  120. char *line = NULL;
  121. size_t line_len = 0;
  122. int split_id = 0;
  123. while(true)
  124. {
  125. if(feof(in)) break;
  126. if(ferror(in))
  127. {
  128. fprintf(stderr,
  129. "%s: error: Failed reading line from file '%s': %s\n",
  130. argv0,
  131. name_in,
  132. strerror(errno));
  133. err = 1;
  134. break;
  135. }
  136. char name_out[NAME_MAX] = "";
  137. if(base26(split_id++, name_out) < 0)
  138. {
  139. err = 1;
  140. break;
  141. }
  142. FILE *out = NULL;
  143. for(size_t i = 0; i < lines; i++)
  144. {
  145. ssize_t nread = getline(&line, &line_len, in);
  146. if(nread < 0)
  147. {
  148. if(errno != 0)
  149. {
  150. fprintf(stderr,
  151. "%s: error: Failed reading line from file '%s': %s\n",
  152. argv0,
  153. name_in,
  154. strerror(errno));
  155. err = 1;
  156. }
  157. break;
  158. }
  159. if(out == NULL)
  160. {
  161. out = fopen(name_out, "w");
  162. if(out == NULL)
  163. {
  164. fprintf(stderr,
  165. "%s: error: Failed opening '%s' file: %s\n",
  166. argv0,
  167. name_out,
  168. strerror(errno));
  169. err = 1;
  170. break;
  171. }
  172. }
  173. if(fwrite(line, nread, 1, out) < 0)
  174. {
  175. fprintf(stderr,
  176. "%s: error: Failed writing line to file '%s': %s\n",
  177. argv0,
  178. name_out,
  179. strerror(errno));
  180. err = 1;
  181. break;
  182. }
  183. }
  184. if(out != NULL)
  185. {
  186. if(fclose(out) < 0)
  187. {
  188. fprintf(
  189. stderr, "%s: error: Failed closing file '%s': %s\n", argv0, name_out, strerror(errno));
  190. err = 1;
  191. break;
  192. }
  193. }
  194. if(err != 0) break;
  195. }
  196. if(line_len > 0) free(line);
  197. if(name_in != NULL) fclose(in);
  198. return err;
  199. }
  200. static const char *error_opt_b_l = "%s: error: Options -b and -l are mutually exclusive\n";
  201. int
  202. main(int argc, char *argv[])
  203. {
  204. int c = -1;
  205. while((c = getopt(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. }