1/* See LICENSE file for copyright and license details. */
2
3#include <ctype.h>
4#include <stdint.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9#include "util.h"
10
11static int base = 26, start = 'a';
12
13static int
14itostr(char *str, int x, int n)
15{
16 str[n] = '\0';
17 while (n-- > 0) {
18 str[n] = start + (x % base);
19 x /= base;
20 }
21
22 return x ? -1 : 0;
23}
24
25static FILE *
26nextfile(FILE *f, char *buf, int plen, int slen)
27{
28 static int filecount = 0;
29
30 if (f)
31 fshut(f, "<file>");
32 if (itostr(buf + plen, filecount++, slen) < 0)
33 return NULL;
34
35 if (!(f = fopen(buf, "w")))
36 eprintf("'%s':", buf);
37
38 return f;
39}
40
41static void
42usage(void)
43{
44 eprintf(
45 "usage: %s [-a num] [-b num[k|m|g] | -l num] [-d] "
46 "[file [prefix]]\n",
47 argv0
48 );
49}
50
51// ?man split: split file into pieces
52// ?man arguments: | -l num
53// ?man split a file into fixed-size pieces
54int
55main(int argc, char *argv[])
56{
57 FILE *in = stdin, *out = NULL;
58 off_t size = 1000, n;
59 int ret = 0, ch, plen, slen = 2, always = 0;
60 char name[NAME_MAX + 1], *prefix = "x", *file = NULL;
61
62 ARGBEGIN
63 {
64 // ?man -a:num: print or show all entries
65 case 'a':
66 slen = estrtonum(EARGF(usage()), 0, INT_MAX);
67 break;
68 // ?man -b:str: specify block size or base directory
69 case 'b':
70 always = 1;
71 if ((size = parseoffset(EARGF(usage()))) < 0)
72 return 1;
73 if (!size)
74 eprintf("size needs to be positive\n");
75 break;
76 // ?man -d: specify directory
77 case 'd':
78 base = 10;
79 start = '0';
80 break;
81 // ?man -l:num: list in long format
82 case 'l':
83 always = 0;
84 size = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SSIZE_MAX));
85 break;
86 default:
87 usage();
88 }
89 ARGEND
90
91 if (*argv)
92 file = *argv++;
93 if (*argv)
94 prefix = *argv++;
95 if (*argv)
96 usage();
97
98 plen = strlen(prefix);
99 if (plen + slen > NAME_MAX)
100 eprintf("names cannot exceed %d bytes\n", NAME_MAX);
101 estrlcpy(name, prefix, sizeof(name));
102
103 if (file && strcmp(file, "-")) {
104 if (!(in = fopen(file, "r")))
105 eprintf("fopen %s:", file);
106 }
107
108 n = 0;
109 while ((ch = getc(in)) != EOF) {
110 if (!out || n >= size) {
111 if (!(out = nextfile(out, name, plen, slen)))
112 eprintf("fopen: %s:", name);
113 n = 0;
114 }
115 n += (always || ch == '\n');
116 putc(ch, out);
117 }
118
119 ret |= (in != stdin) && fshut(in, "<infile>");
120 ret |= out && (out != stdout) && fshut(out, "<outfile>");
121 ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
122
123 return ret;
124}