9
#include "string-list.h"
12
static const char git_mailsplit_usage[] =
13
"git mailsplit [-d<prec>] [-f<n>] [-b] [--keep-cr] -o<directory> [(<mbox>|<Maildir>)...]";
15
static int is_from_line(const char *line, int len)
19
if (len < 20 || memcmp("From ", line, 5))
22
colon = line + len - 2;
31
if (!isdigit(colon[-4]) ||
32
!isdigit(colon[-2]) ||
33
!isdigit(colon[-1]) ||
34
!isdigit(colon[ 1]) ||
39
if (strtol(colon+3, NULL, 10) <= 90)
46
static struct strbuf buf = STRBUF_INIT;
50
static int is_gtfrom(const struct strbuf *buf)
52
size_t min = strlen(">From ");
58
ngt = strspn(buf->buf, ">");
59
return ngt && starts_with(buf->buf + ngt, "From ");
67
static int split_one(FILE *mbox, const char *name, int allow_bare)
72
int is_bare = !is_from_line(buf.buf, buf.len);
74
if (is_bare && !allow_bare) {
75
fprintf(stderr, "corrupt mailbox\n");
78
fd = xopen(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
79
output = xfdopen(fd, "w");
85
if (!keep_cr && buf.len > 1 && buf.buf[buf.len-1] == '\n' &&
86
buf.buf[buf.len-2] == '\r') {
87
strbuf_setlen(&buf, buf.len-2);
88
strbuf_addch(&buf, '\n');
91
if (mboxrd && is_gtfrom(&buf))
92
strbuf_remove(&buf, 0, 1);
94
if (fwrite(buf.buf, 1, buf.len, output) != buf.len)
95
die_errno("cannot write output");
97
if (strbuf_getwholeline(&buf, mbox, '\n')) {
102
die_errno("cannot read mbox");
104
if (!is_bare && is_from_line(buf.buf, buf.len))
111
static int populate_maildir_list(struct string_list *list, const char *path)
116
const char *subs[] = { "cur", "new", NULL };
120
for (sub = subs; *sub; ++sub) {
122
name = xstrfmt("%s/%s", path, *sub);
123
if (!(dir = opendir(name))) {
126
error_errno("cannot opendir %s", name);
130
while ((dent = readdir(dir)) != NULL) {
131
if (dent->d_name[0] == '.')
134
name = xstrfmt("%s/%s", *sub, dent->d_name);
135
string_list_insert(list, name);
148
static int maildir_filename_cmp(const char *a, const char *b)
151
if (isdigit(*a) && isdigit(*b)) {
153
na = strtol(a, (char **)&a, 10);
154
nb = strtol(b, (char **)&b, 10);
161
return (unsigned char)*a - (unsigned char)*b;
166
return (unsigned char)*a - (unsigned char)*b;
169
static int split_maildir(const char *maildir, const char *dir,
170
int nr_prec, int skip)
176
struct string_list list = STRING_LIST_INIT_DUP;
178
list.cmp = maildir_filename_cmp;
180
if (populate_maildir_list(&list, maildir) < 0)
183
for (i = 0; i < list.nr; i++) {
187
file = xstrfmt("%s/%s", maildir, list.items[i].string);
189
f = fopen(file, "r");
191
error_errno("cannot open mail %s", file);
195
if (strbuf_getwholeline(&buf, f, '\n')) {
196
error_errno("cannot read mail %s", file);
200
name = xstrfmt("%s/%0*d", dir, nr_prec, ++skip);
201
split_one(f, name, 1);
213
string_list_clear(&list, 1);
217
static int split_mbox(const char *file, const char *dir, int allow_bare,
218
int nr_prec, int skip)
223
FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
226
if (isatty(fileno(f)))
227
warning(_("reading patches from stdin/tty..."));
230
error_errno("cannot open mbox %s", file);
242
error(_("empty mbox: '%s'"), file);
246
} while (isspace(peek));
249
if (strbuf_getwholeline(&buf, f, '\n')) {
252
error("cannot read mbox %s", file);
259
char *name = xstrfmt("%s/%0*d", dir, nr_prec, ++skip);
260
file_done = split_one(f, name, allow_bare);
272
int cmd_mailsplit(int argc, const char **argv, const char *prefix)
274
int nr = 0, nr_prec = 4, num = 0;
276
const char *dir = NULL;
278
static const char *stdin_only[] = { "-", NULL };
280
BUG_ON_NON_EMPTY_PREFIX(prefix);
282
for (argp = argv+1; *argp; argp++) {
283
const char *arg = *argp;
288
if ( arg[1] == 'd' ) {
289
nr_prec = strtol(arg+2, NULL, 10);
290
if (nr_prec < 3 || 10 <= nr_prec)
291
usage(git_mailsplit_usage);
293
} else if ( arg[1] == 'f' ) {
294
nr = strtol(arg+2, NULL, 10);
295
} else if ( arg[1] == 'h' ) {
296
usage(git_mailsplit_usage);
297
} else if ( arg[1] == 'b' && !arg[2] ) {
299
} else if (!strcmp(arg, "--keep-cr")) {
301
} else if ( arg[1] == 'o' && arg[2] ) {
303
} else if (!strcmp(arg, "--mboxrd")) {
305
} else if ( arg[1] == '-' && !arg[2] ) {
309
die("unknown option: %s", arg);
316
switch (argc - (argp-argv)) {
322
stdin_only[0] = argp[0];
327
usage(git_mailsplit_usage);
336
const char *arg = *argp++;
340
if (arg[0] == '-' && arg[1] == 0) {
341
ret = split_mbox(arg, dir, allow_bare, nr_prec, nr);
343
error("cannot split patches from stdin");
351
if (stat(arg, &argstat) == -1) {
352
error_errno("cannot stat %s", arg);
356
if (S_ISDIR(argstat.st_mode))
357
ret = split_maildir(arg, dir, nr_prec, nr);
359
ret = split_mbox(arg, dir, allow_bare, nr_prec, nr);
362
error("cannot split patches from %s", arg);