logo

utils-std

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

split.c (6237B)


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