
/*
 * m_gmsg.c -- read a folder
 *
 * $Id$
 */

#include <h/mh.h>

/*
 * PLEASE be non-trivial...
 */
#define	NINFO  (MAXFOLDER / 5)

struct info {
    int msgno;
    int stats;
};

static int len = 0;
static struct info *head;

/*
 * static prototypes
 */
static void m_getatr (struct msgs *);
static int m_setatr (struct msgs *, char *, char *);


struct msgs *
m_gmsg (char *name)
{
    register int i, j;
    register struct info *rover, *tail;
    register struct msgs *mp;
    register struct dirent *dp;
    DIR *dd;
    struct stat st;

    if (!(dd = opendir (name = m_mailpath (name)))) {
	free (name);
	return NULL;
    }
    fstat (dd->dd_fd, &st);

    if (!(mp = (struct msgs *) malloc (MHSIZE (mp, 0, 0))))
	adios (NULL, "unable to allocate folder storage");
    mp->lowmsg = 0;
    mp->hghmsg = 0;
    mp->nummsg = 0;
    mp->curmsg = 0;
    mp->lowsel = 0;
    mp->hghsel = 0;
    mp->numsel = 0;
    mp->msgflags = 0;
    mp->foldpath = name;
    if (st.st_uid != getuid () || access (name, W_OK) == NOTOK)
	mp->msgflags |= READONLY;
    j = strlen(BACKUP_PREFIX);
    if (!head) {
	len = NINFO;
	if (!(head = (struct info *) malloc ((size_t) (len * sizeof(*head)))))
	    adios (NULL, "unable to allocate info storage");
    }

    rover = head;
    tail  = head + len;

    while ((dp = readdir (dd)))
	if ((i = m_atoi (dp->d_name))) {
	    if (rover >= tail) {
		register int curlen = tail - head;

		if (!(tail = (struct info *) realloc ((char *) head,
                             (size_t) ((len += NINFO) * sizeof(*head))))) {
		    adios (NULL, "unable to allocate info storage");
		} else {
		    rover = tail + curlen;
		    head  = tail;
		    tail += len;
		}
	    }
	    if (i > mp->hghmsg)
		mp->hghmsg = i;
	    mp->nummsg++;
	    if (mp->lowmsg == 0 || i < mp->lowmsg)
		mp->lowmsg = i;
	    rover->msgno = i;
	    rover->stats = EXISTS;
#ifdef	notdef
	    rover->stats &= ~DELETED;
#endif /* notdef */
	    rover++;
	}
	else
	    switch (dp->d_name[0]) {
		case '.': 
		    continue;

		case ',': 
#ifdef	notdef
		    if ((i = m_atoi (dp->d_name + 1)) {
			register struct info *l;

			for (l = head; l < rover; l++)
			    if (l->msgno == i) {
				if (!(l->stats & EXISTS))
				    l->stats |= DELETED;
				break;
			    }
		    }
#endif /* notdef */
		    continue;

#ifdef MHE
		case '+': 
		    continue;
#endif /* MHE */

#ifdef UCI
		case '_': 
		case '#': 
		    continue;
#endif /* UCI */

		default: 
		    if (strcmp(dp->d_name, LINK) == 0
			    || strncmp(dp->d_name, BACKUP_PREFIX, j) == 0)
			continue;
		    mp->msgflags |= OTHERS;
		    continue;
	    }

    closedir (dd);

#ifndef	MTR
    mp->lowoff = 1;
#else /* MTR */
    mp->lowoff = mp->lowmsg;
#endif /* MTR */
    mp->hghoff = mp->hghmsg + 1;/* for "new" in m_convert */

    mp = (struct msgs  *)
		realloc ((char *) mp, MHSIZE (mp, mp->lowoff, mp->hghoff));
    if (mp == NULL)
	adios (NULL, "unable to allocate folder storage");
#ifndef	MTR
    for (i = mp->lowmsg; i <= mp->hghmsg; i++)
	mp->msgstats[i] = 0;
#else /* MTR */
    mp->msgstats = (int *)
		calloc ((size_t) 1, MHSIZEX (mp, mp->lowmsg, mp->hghmsg));
    if (mp->msgstats == NULL)
	adios (NULL, "unable to allocate messages storage");
    mp->msgstats = (mp->msgbase = mp->msgstats) - mp->lowoff;
    if (mp->msgstats < (int *)0)	/* non portable */
	adios (NULL, "m_gmsg() botch -- you lose big");
#endif /* MTR */
    for (tail = head; tail < rover; tail++)
	mp->msgstats[tail->msgno] = tail->stats;
    m_getatr (mp);

    return mp;
}

static void
m_getatr (struct msgs *mp)
{
    int alen, bits, i, j, plen, state;
    register char *cp;
    char name[NAMESZ], field[BUFSIZ * 2];
    register struct node *np;
    register FILE *fp;

    bits = FFATTRSLOT;

    mp->msgattrs[i = 0] = getcpy (current);
    mp->msgattrs[++i] = '\0';
    mp->attrstats = 0;	/* initially, all public */

    m_getdefs ();
    if (mh_seq == '\0' || *mh_seq == '\0')
	goto private_only;

    sprintf (field, "%s/%s", mp->foldpath, mh_seq);
    if ((fp = fopen (field, "r"))) {
	for (state = FLD;;) {
	    switch (state = m_getfld (state, name, field, sizeof(field), fp)) {
		case FLD: 
		case FLDEOF: 
		    m_setatr (mp, getcpy (name), trimcpy (field));
		    if (state == FLDEOF)
			break;
		    continue;

		case BODY: 
		case BODYEOF: 
		    adios (NULL,
			    "no blank lines are permitted in %s/%s",
			    mp->foldpath, mh_seq);/* fall */

		case FILEEOF:
		    break;

		default: 
		    adios (NULL, "%s/%s is poorly formatted", mp->foldpath, mh_seq);
	    }
	    break;
	}
	fclose (fp);
    }

private_only: ;
    alen = strlen ("atr-");
    plen = strlen (mp->foldpath) + 1;

    for (np = m_defs; np; np = np->n_next)
	if (ssequal ("atr-", np->n_name)
		&& (j = strlen (np->n_name) - plen) > alen
		&& *(np->n_name + j) == '-'
		&& strcmp (mp->foldpath, np->n_name + j + 1) == 0) {
	    cp = getcpy (np->n_name + alen);
	    *(cp + j - alen) = '\0';
	    if ((i = m_setatr (mp, cp, getcpy (np->n_field))) != NOTOK)
		mp->attrstats |= 1 << (bits + i);/* private */
	}
}

static int
m_setatr (struct msgs *mp, char *name, char *field)
{
    int bits, hack;
    register int i, j, k;
    register char *cp, **ap;

    bits = FFATTRSLOT;
    hack = strcmp (current, name) == 0;/* hack... */
    /* if we're going to use UNSEEN, it should be set here! */

    for (i = 0; mp->msgattrs[i]; i++)
	if (strcmp (mp->msgattrs[i], name) == 0) {
	    for (j = mp->lowmsg; j <= mp->hghmsg; j++)
		mp->msgstats[j] &= ~(1 << (bits + i));
	    break;
	}
    if (i >= NUMATTRS) {
	free (name);
	free (field);
	return NOTOK;
    }

    if (!mp->msgattrs[i]) {
	mp->msgattrs[i] = name;
	mp->msgattrs[i + 1] = NULL;
    }
    else
	free (name);

    for (ap = brkstring (field, " ", "\n"); *ap; ap++) {
	if ((cp = strchr(*ap, '-')))
	    *cp++ = '\0';
	if ((j = m_atoi (*ap)) > 0) {
#ifdef	notdef
	    if (hack && j >= mp->lowmsg && j <= mp->hghmsg
		    && (mp->msgstats[j] & EXISTS))
		mp->curmsg = j;
#else /* not notdef */
	    if (hack)
		mp->curmsg = j;
#endif /* not notdef */
	    for (k = cp ? m_atoi (cp) : j; j <= k; j++)
		if (j >= mp->lowmsg && j <= mp->hghmsg
			&& (mp->msgstats[j] & EXISTS))
		    mp->msgstats[j] |= 1 << (bits + i);
	}
    }
    free (field);

    return i;
}
