1/*2* Totally braindamaged mbox splitter program.3*4* It just splits a mbox into a list of files: "0001" "0002" ..5* so you can process them further from there.6*/7#include "cache.h"8#include "builtin.h"910static const char git_mailsplit_usage[] =11"git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>...";1213static int is_from_line(const char *line, int len)14{15const char *colon;1617if (len < 20 || memcmp("From ", line, 5))18return 0;1920colon = line + len - 2;21line += 5;22for (;;) {23if (colon < line)24return 0;25if (*--colon == ':')26break;27}2829if (!isdigit(colon[-4]) ||30!isdigit(colon[-2]) ||31!isdigit(colon[-1]) ||32!isdigit(colon[ 1]) ||33!isdigit(colon[ 2]))34return 0;3536/* year */37if (strtol(colon+3, NULL, 10) <= 90)38return 0;3940/* Ok, close enough */41return 1;42}4344/* Could be as small as 64, enough to hold a Unix "From " line. */45static char buf[4096];4647/* Called with the first line (potentially partial)48* already in buf[] -- normally that should begin with49* the Unix "From " line. Write it into the specified50* file.51*/52static int split_one(FILE *mbox, const char *name, int allow_bare)53{54FILE *output = NULL;55int len = strlen(buf);56int fd;57int status = 0;58int is_bare = !is_from_line(buf, len);5960if (is_bare && !allow_bare)61goto corrupt;6263fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);64if (fd < 0)65die("cannot open output file %s", name);66output = fdopen(fd, "w");6768/* Copy it out, while searching for a line that begins with69* "From " and having something that looks like a date format.70*/71for (;;) {72int is_partial = (buf[len-1] != '\n');7374if (fputs(buf, output) == EOF)75die("cannot write output");7677if (fgets(buf, sizeof(buf), mbox) == NULL) {78if (feof(mbox)) {79status = 1;80break;81}82die("cannot read mbox");83}84len = strlen(buf);85if (!is_partial && !is_bare && is_from_line(buf, len))86break; /* done with one message */87}88fclose(output);89return status;9091corrupt:92if (output)93fclose(output);94unlink(name);95fprintf(stderr, "corrupt mailbox\n");96exit(1);97}9899int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip)100{101char *name = xmalloc(strlen(dir) + 2 + 3 * sizeof(skip));102int ret = -1;103104while (*mbox) {105const char *file = *mbox++;106FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");107int file_done = 0;108109if ( !f ) {110error("cannot open mbox %s", file);111goto out;112}113114if (fgets(buf, sizeof(buf), f) == NULL) {115if (f == stdin)116break; /* empty stdin is OK */117error("cannot read mbox %s", file);118goto out;119}120121while (!file_done) {122sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);123file_done = split_one(f, name, allow_bare);124}125126if (f != stdin)127fclose(f);128}129ret = skip;130out:131free(name);132return ret;133}134int cmd_mailsplit(int argc, const char **argv, const char *prefix)135{136int nr = 0, nr_prec = 4, ret;137int allow_bare = 0;138const char *dir = NULL;139const char **argp;140static const char *stdin_only[] = { "-", NULL };141142for (argp = argv+1; *argp; argp++) {143const char *arg = *argp;144145if (arg[0] != '-')146break;147/* do flags here */148if ( arg[1] == 'd' ) {149nr_prec = strtol(arg+2, NULL, 10);150if (nr_prec < 3 || 10 <= nr_prec)151usage(git_mailsplit_usage);152continue;153} else if ( arg[1] == 'f' ) {154nr = strtol(arg+2, NULL, 10);155} else if ( arg[1] == 'b' && !arg[2] ) {156allow_bare = 1;157} else if ( arg[1] == 'o' && arg[2] ) {158dir = arg+2;159} else if ( arg[1] == '-' && !arg[2] ) {160argp++; /* -- marks end of options */161break;162} else {163die("unknown option: %s", arg);164}165}166167if ( !dir ) {168/* Backwards compatibility: if no -o specified, accept169<mbox> <dir> or just <dir> */170switch (argc - (argp-argv)) {171case 1:172dir = argp[0];173argp = stdin_only;174break;175case 2:176stdin_only[0] = argp[0];177dir = argp[1];178argp = stdin_only;179break;180default:181usage(git_mailsplit_usage);182}183} else {184/* New usage: if no more argument, parse stdin */185if ( !*argp )186argp = stdin_only;187}188189ret = split_mbox(argp, dir, allow_bare, nr_prec, nr);190if (ret != -1)191printf("%d\n", ret);192193return ret == -1;194}