/*
** refile.c -- move or link message(s) from a source folder
**          -- into one or more destination folders
**
** This code is Copyright (c) 2002, by the authors of nmh.  See the
** COPYRIGHT file in the root directory of the nmh distribution for
** complete copyright information.
*/

#include <h/mh.h>
#include <h/utils.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <locale.h>
#include <sysexits.h>

static struct swit switches[] = {
#define LINKSW  0
	{ "link", 0 },
#define NLINKSW  1
	{ "nolink", 2 },
#define SRCSW  2
	{ "src +folder", 0 },
#define FILESW  3
	{ "file file", 0 },
#define VERSIONSW  4
	{ "Version", 0 },
#define HELPSW  5
	{ "help", 0 },
	{ NULL, 0 }
};

char *version=VERSION;

static char maildir[BUFSIZ];

struct st_fold {
	char *f_name;
	struct msgs *f_mp;
};

/*
** static prototypes
*/
static void opnfolds(struct st_fold *, int);
static void clsfolds(struct st_fold *, int);
static int m_file(char *, struct st_fold *, int, int);


int
main(int argc, char **argv)
{
	int linkf = 0, filep = 0;
	int foldp = 0;
	int i, msgnum;
	char *cp, *folder = NULL, buf[BUFSIZ];
	char **argp, **arguments;
	char *filevec[NFOLDERS + 1];
	char **files = filevec;
	struct st_fold folders[NFOLDERS + 1];
	struct msgs_array msgs = { 0, 0, NULL };
	struct msgs *mp;

	setlocale(LC_ALL, "");
	invo_name = mhbasename(argv[0]);

	context_read();

	arguments = getarguments(invo_name, argc, argv, 1);
	argp = arguments;

	/*
	** Parse arguments
	*/
	while ((cp = *argp++)) {
		if (*cp == '-') {
			switch (smatch(++cp, switches)) {
			case AMBIGSW:
				ambigsw(cp, switches);
				exit(EX_USAGE);
			case UNKWNSW:
				adios(EX_USAGE, NULL, "-%s unknown\n", cp);

			case HELPSW:
				snprintf(buf, sizeof(buf), "%s [msgs] [switches] +folder ...", invo_name);
				print_help(buf, switches, 1);
				exit(argc == 2 ? EX_OK : EX_USAGE);
			case VERSIONSW:
				print_version(invo_name);
				exit(argc == 2 ? EX_OK : EX_USAGE);

			case LINKSW:
				linkf++;
				continue;
			case NLINKSW:
				linkf = 0;
				continue;

			case SRCSW:
				if (folder)
					adios(EX_USAGE, NULL, "only one source folder at a time!");
				if (!(cp = *argp++) || *cp == '-')
					adios(EX_USAGE, NULL, "missing argument to %s",
							argp[-2]);
				folder = mh_xstrdup(expandfol(cp));
				continue;
			case FILESW:
				if (filep > NFOLDERS)
					adios(EX_USAGE, NULL, "only %d files allowed!",
							NFOLDERS);
				if (!(cp = *argp++) || *cp == '-')
					adios(EX_USAGE, NULL, "missing argument to %s",
							argp[-2]);
				files[filep++] = mh_xstrdup(expanddir(cp));
				continue;
			}
		}
		if (*cp == '+' || *cp == '@') {
			if (foldp > NFOLDERS)
				adios(EX_USAGE, NULL, "only %d folders allowed!",
						NFOLDERS);
			folders[foldp++].f_name = mh_xstrdup(expandfol(cp));
		} else
			app_msgarg(&msgs, cp);
	}

	if (foldp == 0)
		adios(EX_USAGE, NULL, "no folder specified");

	if (filep > 0) {
		/*
		** We are refiling one or more files (-file) to the folders
		*/
		if (msgs.size) {
			adios(EX_USAGE, NULL, "use -file or msgs, not both");
		}
		if (folder) {
			adios(EX_USAGE, NULL, "use -file or -src, not both");
		}
		opnfolds(folders, foldp);
		for (i = 0; i < filep; i++) {
			if (m_file(files[i], folders, foldp, 0)) {
				exit(EX_IOERR);
			}
		}
		/* If -nolink, then unlink files */
		if (!linkf) {
			int i;
			char **files = filevec;

			for (i = 0; i < filep; i++) {
				if (unlink(files[i]) == NOTOK) {
					admonish(files[i], "unable to unlink");
				}
			}
		}
		exit(EX_OK);
	}

	/*
	** We are refiling messages to the folders
	*/
	if (!msgs.size) {
		app_msgarg(&msgs, seq_cur);
	}
	if (!folder) {
		folder = getcurfol();
	}

	strncpy(maildir, toabsdir(folder), sizeof(maildir));
	if (chdir(maildir) == NOTOK) {
		adios(EX_OSERR, maildir, "unable to change directory to");
	}

	/* read source folder and create message structure */
	if (!(mp = folder_read(folder))) {
		adios(EX_IOERR, NULL, "unable to read folder %s", folder);
	}

	if (mp->nummsg == 0) {
		adios(EX_DATAERR, NULL, "no messages in %s", folder);
	}

	/* parse the message range/sequence/name and set SELECTED */
	for (msgnum = 0; msgnum < msgs.size; msgnum++) {
		if (!m_convert(mp, msgs.msgs[msgnum])) {
			exit(EX_SOFTWARE);
		}
	}
	seq_setprev(mp);

	/* create folder structures for each destination folder */
	opnfolds(folders, foldp);

	/*
	** Link all the selected messages into destination folders.
	**
	** This causes the add hook to be run for messages that are
	** linked into another folder.  The refile hook is run for
	** messages that are moved to another folder.
	*/
	for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
		if (is_selected(mp, msgnum)) {
			cp = mh_xstrdup(m_name(msgnum));
			if (m_file(cp, folders, foldp, !linkf)) {
				exit(EX_IOERR);
			}
			mh_free0(&cp);
		}
	}

	/*
	** If -nolink, then remove (= unlink) messages from source folder.
	**
	** Note that folder_delmsgs does not call the delete hook
	** because the message has already been handled above.
	*/
	if (!linkf) {
		folder_delmsgs(mp, 0);
	}

	clsfolds(folders, foldp);

	if (linkf) {
		seq_setcur(mp, mp->hghsel);
	}
	seq_save(mp);

	context_save();
	folder_free(mp);
	return 0;
}


/*
** Read all the destination folders and
** create folder structures for all of them.
*/
static void
opnfolds(struct st_fold *folders, int nfolders)
{
	char nmaildir[BUFSIZ];
	struct st_fold *fp, *ep;
	struct msgs *mp;

	for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
		chdir(toabsdir("+"));
		strncpy(nmaildir, toabsdir(fp->f_name), sizeof(nmaildir));

		create_folder(nmaildir, 0, exit);

		if (chdir(nmaildir) == NOTOK) {
			adios(EX_OSERR, nmaildir, "unable to change directory to");
		}
		if (!(mp = folder_read(fp->f_name))) {
			adios(EX_IOERR, NULL, "unable to read folder %s", fp->f_name);
		}
		mp->curmsg = 0;

		fp->f_mp = mp;

		chdir(maildir);
	}
}


/*
** Set the Previous-Sequence and then sychronize the
** sequence file, for each destination folder.
*/
static void
clsfolds(struct st_fold *folders, int nfolders)
{
	struct st_fold *fp, *ep;
	struct msgs *mp;

	for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
		mp = fp->f_mp;
		seq_setprev(mp);
		seq_save(mp);
	}
}


/*
** Link (or copy) the message into each of
** the destination folders.
*/
static int
m_file(char *msgfile, struct st_fold *folders, int nfolders, int refile)
{
	int msgnum;
	struct st_fold *fp, *ep;

	for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
		if ((msgnum = folder_addmsg(&fp->f_mp, msgfile, 1, 0, 0,
				nfolders == 1 && refile, maildir)) == -1) {
			return 1;
		}
	}
	return 0;
}
