/*
 * $Id: splitmbox.c,v 1.4 2005/05/13 02:35:30 jschauma Exp $
 *
 * Copyright (c) 2004,2005 Jan Schaumann
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void check_dir(void);
int do_split(FILE *);
void usage(void);

#define MAX_LINE_LENGTH		1024
#define NAME 			"splitmbox"
#define VERSION 		"0.1"

int mcount;
char *dir;

/* Split mbox input into individual mail files. */
int
main(int argc, char **argv)
{
	extern char *optarg;
	extern int optind;

	FILE *f;
	int ch, ret, cnt, num;

	setprogname(NAME);

	dir = ".";
	mcount = 0;

	while ((ch = getopt(argc, argv, "d:hv")) != -1) {
		switch(ch) {
		case 'd':
			dir = optarg;
			break;
		case 'h':
		case '?':
			usage();
			exit(EXIT_SUCCESS);
			/* NOTREACHED */
			break;
		case 'v':
			printf("%s: Version %s\n", getprogname(), VERSION);
			exit(EXIT_SUCCESS);
			/* NOTREACHED */
			break;
		default:
			usage();
			exit(EXIT_FAILURE);
			/* NOTREACHED */
		}
	}

	argc -= optind;
	argv += optind;

	cnt = argc;

	ret = 0;
			
	check_dir();

	if (cnt == 0) {
		return do_split(stdin);
	} else {
		for (num = 0; num < argc; num++) {
			char *file;
			file = argv[num];

			if (strcmp(file, "-") == 0)
				ret += do_split(stdin);
			else if ((f = fopen(file, "r")) == NULL) {
				(void)fprintf(stderr, "%s: %s: %s\n",
					      getprogname(), file,
					      strerror(errno));
			} else {
				ret += do_split(f);
				fclose(f);
			}
		}
	}

	return ret;
}

void
check_dir()
{
	struct stat sb;

	if (stat(dir, &sb) == -1) {
		if (mkdir(dir, S_IRUSR|S_IWUSR|S_IXUSR) == -1) {
			(void)fprintf(stderr, "%s: Unable to create directory %s: %s\n",
				      getprogname(), dir, strerror(errno));
			exit(EXIT_FAILURE);
			/* NOTREACHED */
		}
		return;
	}

	if (!(sb.st_mode & S_IFDIR)) {
		(void)fprintf(stderr, "%s: %s is not a directory!\n",
			      getprogname(), dir);
		exit(EXIT_FAILURE);
		/* NOTREACHED */
	}
}

void
usage() {
	printf("Usage: %s [-d dir] [-h] [-v]\n", getprogname());
	printf("Split mbox input into individual mail files.\n");
	printf("Supported options:\n");
	printf("\t-d dir \tspecify directory to create output in\n");
	printf("\t-h     \tdisplay this message and exit\n");
	printf("\t-v     \tdisplay version information and exit\n");
}


/* performs the actual splitting, returning 0 on success, 1 on error */
int
do_split(FILE *rfile) {
	char buf[MAX_LINE_LENGTH];
	char out[NAME_MAX];

	int retval;
	int i, wrflg, wfd;

	retval = wrflg = 0;
	wfd = -1;
	memset(&buf, '\0', MAX_LINE_LENGTH);
	memset(&out, '\0', NAME_MAX);

	while (fgets(buf, MAX_LINE_LENGTH, rfile) != NULL) {
		if ((i = strncmp(buf, "From ", 5)) == 0) {
			mcount++;
			if (wfd != -1) {
				(void)close(wfd);
				memset(&out, '\0', NAME_MAX);
				wfd = -1;
			}

			wrflg= 1;
			if (snprintf(out, NAME_MAX, "%s/%06d", dir, mcount) == -1) {
				(void)fprintf(stderr,
					 "%s: Unable to create filename: %s\n",
					 getprogname(), strerror(errno));
				retval = 1;
				continue;
			}
		}

		if (wfd == -1) {
			if ((wfd = open(out, O_WRONLY|O_CREAT|O_TRUNC,
						S_IRUSR|S_IWUSR)) == -1) {
				(void)fprintf(stderr,
					  "%s: Unable to create file %s: %s\n",
					  getprogname(), out, strerror(errno));
				retval = 1;
				continue;
			}
		}
		if (wrflg) {
			int written;
			int len = strlen(buf);

			while ((written = write(wfd, buf, len)) != -1 &&
					written != len) {
				len = len - written;
			}
			if (written == -1) {
				(void)fprintf(stderr,
					  "%s: Unable to write to %s: %s\n",
					  getprogname(), out, strerror(errno));
				retval = 1;
				continue;
			}
		}
	}
	(void)close(wfd);

	return retval;

}
