
/*
 * folder(s).c -- set/list the current folder/message
 *
 * $Id$
 */

#include <h/mh.h>
#include <errno.h>

static struct swit switches[] = {
#define	ALLSW           0
    { "all", 0 },
#define	CREATSW         1
    { "create", 0 },
#define	NCREATSW        2
    { "nocreate", 0 },
#define	FASTSW          3
    { "fast", 0 },
#define	NFASTSW         4
    { "nofast", 0 },
#define	HDRSW           5
    { "header", 0 },
#define	NHDRSW          6
    { "noheader", 0 },
#define	PACKSW          7
    { "pack", 0 },
#define	NPACKSW         8
    { "nopack", 0 },
#define	VERBSW          9
    { "verbose", 0 },
#define	NVERBSW        10
    { "noverbose", 0 },
#define	RECURSW        11
    { "recurse", 0 },
#define	NRECRSW        12
    { "norecurse", 0 },
#define	TOTALSW        13
    { "total", 0 },
#define	NTOTLSW        14
    { "nototal", 0 },
#define	PRNTSW         15
    { "print", 0 },
#define	NPRNTSW        16
    { "noprint", -4 },
#define	LISTSW         17
    { "list", 0 },
#define	NLISTSW        18
    { "nolist", 0 },
#define	PUSHSW         19
    { "push", 0 },
#define	POPSW          20
    { "pop", 0 },
#define VERSIONSW      21
    { "version", 0 },
#define	HELPSW         22
    { "help", 4 },
    { NULL, 0 }
};

extern int errno;

static int fshort = 0;
static int fcreat = 0;
static int fpack = 0;
static int fverb = 0;
static int fheader = 0;
static int frecurse = 0;
static int ftotonly = 0;
static int msgtot = 0;
static int foldtot = 0;
static int start = 0;
static int foldp = 0;

static char *mhdir;
static char *stack = "Folder-Stack";
static char folder[BUFSIZ];
static char *folds[NFOLDERS + 1];

/*
 * prototypes
 */
static void dodir (char *);
static int pfold (char *, char *);
static int sfold (struct msgs *, char *);
static void addir (char *);
static void addfold (char *);
static int compare (char *, char *);
static void dother (void);

struct msgs *tfold (struct msgs *);


main (int argc, char **argv)
{
    int all = 0, printsw = 0, listsw = 0;
    int pushsw = 0, popsw = 0;
    char *cp, *dp, *msg = NULL, *argfolder = NULL;
    char **ap, **argp, buf[100], *arguments[MAXARGS];
    struct stat st;

#ifdef LOCALE
	setlocale(LC_ALL, "");
#endif
    invo_name = r1bindex (argv[0], '/');
    if (argv[0][strlen (argv[0]) - 1] == 's')
	all++;
    if ((cp = m_find (invo_name))) {
	ap = brkstring (cp = getcpy (cp), " ", "\n");
	ap = copyip (ap, arguments);
    }
    else
	ap = arguments;
    copyip (argv + 1, ap);
    argp = arguments;

    while ((cp = *argp++)) {
	if (*cp == '-')
	    switch (smatch (++cp, switches)) {
		case AMBIGSW: 
		    ambigsw (cp, switches);
		    done (1);
		case UNKWNSW: 
		    adios (NULL, "-%s unknown", cp);

		case HELPSW: 
		    sprintf (buf, "%s [+folder] [msg] [switches]", invo_name);
		    print_help (buf, switches);
		    done (1);
		case VERSIONSW:
		    print_version(invo_name);
		    done (1);

		case ALLSW: 
		    all++;
		    continue;

		case CREATSW: 
		    fcreat = 1;
		    continue;
		case NCREATSW: 
		    fcreat = -1;
		    continue;

		case FASTSW: 
		    fshort++;
		    continue;
		case NFASTSW: 
		    fshort = 0;
		    continue;

		case HDRSW: 
		    fheader = -1;
		    continue;
		case NHDRSW: 
		    fheader++;
		    continue;

		case PACKSW: 
		    fpack++;
		    continue;
		case NPACKSW: 
		    fpack = 0;
		    continue;

		case VERBSW:
		    fverb++;
		    continue;
		case NVERBSW:
		    fverb = 0;
		    continue;

		case RECURSW: 
		    frecurse++;
		    continue;
		case NRECRSW: 
		    frecurse = 0;
		    continue;

		case TOTALSW: 
		    all++;
		    ftotonly++;
		    continue;
		case NTOTLSW: 
		    if (ftotonly)
			all = 0;
		    ftotonly = 0;
		    continue;

		case PRNTSW: 
		    printsw++;
		    continue;
		case NPRNTSW: 
		    printsw = 0;
		    continue;

		case LISTSW: 
		    listsw++;
		    continue;
		case NLISTSW: 
		    listsw = 0;
		    continue;

		case PUSHSW: 
		    pushsw++;
		    listsw++;
		    popsw = 0;
		    continue;
		case POPSW: 
		    popsw++;
		    listsw++;
		    pushsw = 0;
		    continue;
	    }
	if (*cp == '+' || *cp == '@')
	    if (argfolder)
		adios (NULL, "only one folder at a time!");
	    else
		argfolder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
	else
	    if (msg)
		adios (NULL, "only one (current) message at a time!");
	    else
		msg = cp;
    }

    if (!m_find ("path"))
	free (path ("./", TFOLDER));
    mhdir = concat (m_maildir (""), "/", NULL);

    if (pushsw == 0 && popsw == 0 && listsw == 0)
	printsw++;
    if (pushsw) {
	if (!argfolder) {
	    /* If no folder is given, the current folder and */
	    /* the top of the folder stack are swapped.      */
	    if ((cp = m_find (stack))) {
		dp = getcpy (cp);
		ap = brkstring (dp, " ", "\n");
		argfolder = getcpy(*ap++);
	    } else {
		adios (NULL, "no other folder");
	    }
	    for (cp = getcpy (m_getfolder ()); *ap; ap++)
		cp = add (*ap, add (" ", cp));
	    free (dp);
	    m_replace (stack, cp);
	}
	else
	    m_replace (stack,
		    (cp = m_find (stack))
		    ? concat (m_getfolder (), " ", cp, NULL)
		    : getcpy (m_getfolder ()));
    }
    if (popsw) {
	if (argfolder)
	    adios (NULL, "sorry, no folders allowed with -pop");
	if ((cp = m_find (stack))) {
	    dp = getcpy (cp);
	    ap = brkstring (dp, " ", "\n");
	    argfolder = getcpy(*ap++);
	} else {
	    adios (NULL, "folder stack empty");
	}
	if (*ap) {
	    /* if there's anything left in the stack */
	    cp = getcpy (*ap++);
	    for (; *ap; ap++)
		cp = add (*ap, add (" ", cp));
	    m_replace (stack, cp);
	} else {
	    /* else just delete that entry */
	    m_delete (stack);
	}
	free (dp);
    }
    if (pushsw || popsw) {
	cp = m_maildir(argfolder);
	if (access (cp, F_OK) == NOTOK)
	    adios (cp, "unable to find folder");
	m_replace (pfolder, argfolder);
	m_update ();
	argfolder = NULL;
    }
    if (listsw) {
	printf ("%s", argfolder ? argfolder : m_getfolder ());
	if ((cp = m_find (stack))) {
	    dp = getcpy (cp);
	    for (ap = brkstring (dp, " ", "\n"); *ap; ap++)
		printf (" %s", *ap);
	    free (dp);
	}
	printf ("\n");

	if (!printsw)
	    done (0);
    }

    if (all) {
	fheader = 0;
	if (argfolder) {
	    strcpy (folder, argfolder);
	    if (pfold (argfolder, msg)) {
		m_replace (pfolder, argfolder);
		m_update ();
	    }
	    if (!frecurse)	/* recurse not done in pfold(), */
		dodir (folder);	/* so just list all level-1 sub-folders */
	}
	else {
	    if (msg)
		admonish (NULL, "no folder given for message %s", msg);
	    dother ();

	    strcpy (folder, (cp = m_find (pfolder)) ? cp : "");
	    dodir (".");
	}

	if (!fshort) {
	    if (!ftotonly)
		printf ("\n\t\t     ");
	    printf ("TOTAL= %*d message%c in %d folder%s.\n",
		    DMAXFOLDER, msgtot, msgtot != 1 ? 's' : ' ',
		    foldtot, foldtot != 1 ? "s" : "");
	}
    }
    else {
	fheader++;

	strcpy (folder, argfolder ? argfolder : m_getfolder ());
	if (stat (strcpy (buf, m_maildir (folder)), &st) == NOTOK) {
	    if (errno != ENOENT)
		adios (buf, "error on folder");
	    switch (fcreat) {
		case 0:			/* ask before create */
		    cp = concat ("Create folder \"", buf, "\"? ", NULL);
		    if (!getanswer (cp))
			done (1);
		    free (cp);
		    break;
		case -1:		/* do not create */
		    done (1);
		    break;
	    }
	    if (!makedir (buf))
		adios (NULL, "unable to create folder %s", buf);
	}

	if (pfold (folder, msg) && argfolder)
	    m_replace (pfolder, argfolder);
    }

    m_update ();
    done (0);
}

static void
dodir (char *dir)
{
    int i;
    int os = start;
    int of = foldp;
    char buffer[BUFSIZ];

    start = foldp;
    if (chdir (mhdir) == NOTOK)
	adios (mhdir, "unable to change directory to");

    addir (strcpy (buffer, dir));
    for (i = start; i < foldp; i++) {
	pfold (folds[i], NULL);
	fflush (stdout);
    }

    start = os;
    foldp = of;
}

static int
pfold (char *fold, char *msg)
{
    int	hack, others, retval = 1;
    register char *mailfile;
    register struct msgs   *mp = NULL;

    mailfile = m_maildir (fold);
    if (chdir (mailfile) == NOTOK) {
	if (errno != EACCES)
	    admonish (mailfile, "unable to change directory to");
	else
	    printf ("%22s%c unreadable\n",
		    fold, strcmp (folder, fold) ? ' ' : '+');
	return 0;
    }

    if (fshort) {
	printf ("%s\n", fold);

	if (!msg && !fpack) {
	    if (frecurse)
		dodir (fold);
	    return retval;
	}
    }

    if (!(mp = m_gmsg (fold))) {
	admonish (NULL, "unable to read folder %s", fold);
	return 0;
    }

    if (msg && !sfold (mp, msg))
	retval = 0;
    if (fpack)
	mp = tfold (mp);

    if (fshort)
	goto out;
    foldtot++;
    msgtot += mp->nummsg;

    if (ftotonly)
	goto out;

    if (!fheader++)
	printf ("\t\tFolder  %*s# of messages (%*srange%*s); cur%*smsg  (other files)\n",
	    DMAXFOLDER, "", DMAXFOLDER - 2, "", DMAXFOLDER - 2, "",
	    DMAXFOLDER - 2, "");

    printf ("%22s%c ", fold, strcmp (folder, fold) ? ' ' : '+');

    hack = 0;
    if (mp->hghmsg == 0)
	printf ("has   no messages%*s",
		mp->msgflags & OTHERS ? DMAXFOLDER * 2 + 4 : 0, "");
    else {
	printf ("has %*d message%s (%*d-%*d)",
		DMAXFOLDER, mp->nummsg, (mp->nummsg == 1) ? " " : "s",
		DMAXFOLDER, mp->lowmsg, DMAXFOLDER, mp->hghmsg);
	if (mp->curmsg >= mp->lowmsg && mp->curmsg <= mp->hghmsg)
	    printf ("; cur=%*d", DMAXFOLDER, hack = mp->curmsg);
    }

    if (mp->msgflags & OTHERS)
	printf (";%*s (others)", hack ? 0 : DMAXFOLDER + 6, "");
    printf (".\n");

out: ;
    others = mp->msgflags & OTHERS;
    m_fmsg (mp);

    if (frecurse && others)
	dodir (fold);

    return retval;
}

static int
sfold (struct msgs *mp, char *msg)
{
    if (!m_convert (mp, msg))
	return 0;

    if (mp->numsel > 1) {
	admonish (NULL, "only one message at a time!");
	return 0;
    }
    m_setseq (mp);
    m_setcur (mp, mp->lowsel);
    m_sync (mp);
    m_update ();

    return 1;
}


struct msgs *
tfold (struct msgs *mp)
{
    register int hole, msgnum;
    char newmsg[BUFSIZ], oldmsg[BUFSIZ];

    if (mp->lowmsg > 1 && (mp = m_remsg (mp, 1, mp->hghmsg)) == NULL)
	adios (NULL, "unable to allocate folder storage");

    for (msgnum = mp->lowmsg, hole = 1; msgnum <= mp->hghmsg; msgnum++)
	if (mp->msgstats[msgnum] & EXISTS) {
	    if (msgnum != hole) {
		strcpy (newmsg, m_name (hole));
		strcpy (oldmsg, m_name (msgnum));
		if (fverb)
		    printf ("message %s becomes %s\n", oldmsg, newmsg);
		if (rename (oldmsg, newmsg) == NOTOK)
		    adios (newmsg, "unable to rename %s to", oldmsg);
		if (msgnum == mp->curmsg)
		    m_setcur (mp, mp->curmsg = hole);
		mp->msgstats[hole] = mp->msgstats[msgnum];
		mp->msgflags |= SEQMOD;
		if (msgnum == mp->lowsel)
		    mp->lowsel = hole;
		if (msgnum == mp->hghsel)
		    mp->hghsel = hole;
	    }
	    hole++;
	}
    if (mp->nummsg > 0) {
	mp->lowmsg = 1;
	mp->hghmsg = hole - 1;
    }
    m_sync (mp);
    m_update ();

    return mp;
}

static void
addir (char *name)
{
    register char *base, *cp;
    struct stat st;
    register struct dirent *dp;
    register DIR * dd;

    cp = name + strlen (name);
    *cp++ = '/';
    *cp = '\0';

    base = strcmp (name, "./") ? name : name + 2; /* hack */

   /* short-cut to see if directory has any sub-directories */
    if (stat (name, &st) != NOTOK && st.st_nlink == 2)
        return;
 
    if (!(dd = opendir (name))) {
	admonish (name, "unable to read directory ");
	return;
    }
    while ((dp = readdir (dd)))
	if (strcmp (dp->d_name, ".") && strcmp (dp->d_name, "..")) {
	    if (cp + NLENGTH(dp) + 2 >= name + BUFSIZ)
		continue;
	    strcpy (cp, dp->d_name);
	    if (stat (name, &st) != NOTOK && S_ISDIR(st.st_mode))
		addfold (base);
	}
    closedir (dd);

    *--cp = '\0';
}

static void
addfold (char *fold)
{
    register int i, j;
    register char *cp;

    if (foldp > NFOLDERS)
	adios (NULL, "more than %d folders to report on", NFOLDERS);

    cp = getcpy (fold);
    for (i = start; i < foldp; i++)
	if (compare (cp, folds[i]) < 0) {
	    for (j = foldp - 1; j >= i; j--)
		folds[j + 1] = folds[j];
	    foldp++;
	    folds[i] = cp;
	    return;
	}

    folds[foldp++] = cp;
}

static int
compare (char *s1, char *s2)
{
    register int i;

    while (*s1 || *s2)
	if ((i = *s1++ - *s2++))
	    return i;

    return 0;
}

static void
dother (void)
{
    int	atrlen;
    char atrcur[BUFSIZ];
    register struct node *np;

    sprintf (atrcur, "atr-%s-", current);
    atrlen = strlen (atrcur);

    m_getdefs ();
    for (np = m_defs; np; np = np->n_next)
	if (ssequal (atrcur, np->n_name)
		&& !ssequal (mhdir, np->n_name + atrlen))
	    pfold (np->n_name + atrlen, NULL);
}
