nmh-workers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Nmh-workers] New new/fn/fp/unseen program


From: Eric Gillespie
Subject: [Nmh-workers] New new/fn/fp/unseen program
Date: Mon, 21 Jul 2008 16:55:09 -0700

I've used Luke Mewburn's new (http://www.mewburn.net/luke/src/new)

for as long as I've used mh, and I've used this much faster
reimplementation in my local mh tree for the last couple years.
Since I'm trying to get back to using stock nmh, I'm hoping you
will accept it for inclusion.

/*
 * new.c -- as new,    list all folders with unseen messages
 *       -- as fn,     move to next folder with unseen messages
 *       -- as fp,     move to previous folder with unseen messages
 *       -- as unseen, scan all unseen messages
 * $Id$
 *
 * This code is Copyright (c) 2008, by the authors of nmh.  See the
 * COPYRIGHT file in the root directory of the nmh distribution for
 * complete copyright information.
 *
 * Inspired by Luke Mewburn's new: http://www.mewburn.net/luke/src/new
 */

#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>

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

static enum { NEW, FN, FP, UNSEEN } run_mode = NEW;

/* configurable name of unseen sequence */
static char *unseen;
/* could be made configurable, too */
static char *folders = ".folders";

/* Return the number of unseen messages in a string list of message numbers. */
static int
count_unseen(char *field)
{
    int total = 0;
    int j, k;
    char *cp, **ap;

    field = strdup (field);

    /* copied from seq_read.c:seq_init */
    for (ap = brkstring (field, " ", "\n"); *ap; ap++) {
        if ((cp = strchr(*ap, '-')))
            *cp++ = '\0';
        if ((j = m_atoi (*ap)) > 0) {
            k = cp ? m_atoi (cp) : j;

            total += k - j + 1;
        }
    }

    free (field);

    return total;
}

/* Return the string list of unseen message numbers from the sequences
 * file, or NULL if none. */
static char *
get_unseen(char *folder)
{
    int state;
    char *cp;
    char *seqfile = concat (m_maildir (folder), "/", mh_seq, NULL);
    char name[NAMESZ], field[BUFSIZ];
    FILE *fp = fopen (seqfile, "r");

    /* no sequences file -> no unseen messages */
    if (fp == NULL) {
        return NULL;
    }

    /* copied from seq_read.c:seq_public */
    for (state = FLD;;) {
        switch (state = m_getfld (state, name, field, sizeof(field), fp)) {
            case FLD:
            case FLDPLUS:
            case FLDEOF:
                if (state == FLDPLUS) {
                    cp = getcpy (field);
                    while (state == FLDPLUS) {
                        state = m_getfld (state, name, field,
                                          sizeof(field), fp);
                        cp = add (field, cp);
                    }

                    /* Here's where we differ from seq_public: if it's the
                     * unseen sequence, return the list of messages. */
                    if (strcmp(name, unseen) == 0) {
                        char *result = trimcpy (cp);
                        free (cp);
                        fclose (fp);
                        return result;
                    }
                    free (cp);
                } else {
                    if (strcmp(name, unseen) == 0) {
                        fclose (fp);
                        return trimcpy (field);
                    }
                }

                if (state == FLDEOF)
                    break;
                continue;

            case BODY:
            case BODYEOF:
                adios (NULL, "no blank lines are permitted in %s", seqfile);
                /* fall */

            case FILEEOF:
                break;

            default:
                adios (NULL, "%s is poorly formatted", seqfile);
        }
        break;  /* break from for loop */
    }

    fclose (fp);

    /* If we get here, we found no unseen messages. */
    return NULL;
}

/* Scan the folders listed in '.folders', returning:

 * first -- list of nodes for all folders which have unseen messages; if
 *          the current folder is listed in .folders, it is also in the
 *          list regardless of whether it has any unseen messages
 * cur_node -- node of current folder, if listed in .folders
 * maxlen -- length of longest folder name
 */
static void
check_folders(struct node **first, struct node **last,
              struct node **cur_node, size_t *maxlen,
              char *cur)
{
    char *fn = m_maildir (folders);
    FILE *fp = fopen (fn, "r");
    char *line;
    size_t len;
    int is_cur;
    char *unseen_list = NULL;
    struct node *node;

    if (fp  == NULL) {
        adios (NULL, "failed to read .folders");
    }

    *first = *cur_node = NULL;
    *maxlen = 0;
    while (vfgets (fp, &line) == OK) {
        len = strlen(line) - 1;
        line[len] = '\0';

        is_cur = strcmp(line, cur) == 0;
        unseen_list = get_unseen(line);
        if (is_cur || unseen_list != NULL) {
            if (*first == NULL) {
                *first = node = malloc(sizeof(*node));
            } else {
                node->n_next = malloc(sizeof(*node));
                node = node->n_next;
            }
            node->n_name = strdup(line);
            node->n_field = unseen_list;

            if (*maxlen < len) {
                *maxlen = len;
            }
        }

        /* Save the node for the current folder, so we can fall back to it. */
        if (is_cur) {
            *cur_node = node;
        }
    }

    if (*first == NULL) {
        adios (NULL, ".folders is empty");
    }

    node->n_next = NULL;
    *last = node;
}

/* Return a struct node for the folder to change to.  This is the next
 * (previous, if FP mode) folder with unseen messages, or the current
 * folder if no folders have unseen.  If NEW or UNSEEN mode, print the
 * output but don't change folders.
 *
 * n_name is the folder to change to, and n_field is the string list of
 * unseen message numbers.
 */
static struct node *
doit(char *cur)
{
    struct node *first, *cur_node, *node, *last, *prev;
    size_t folder_len;
    char *command;
    int count, total = 0;

    if (cur[0] == '\0') {
        cur = "inbox";
    }

    check_folders(&first, &last, &cur_node, &folder_len, cur);

    /* If we have only one node, go ahead and return it.  The only
     * node is by definition the current folder. */
    if (first->n_next == NULL) {
        return first;
    }

    /* If the current folder is not listed in .folders, cur_node will
     * still be NULL.  If fn or fp, just move to first folder.  For new
     * and unseen, we'll go ahead and return cur_node (NULL). */
    if (cur_node == NULL && (run_mode == FN || run_mode == FP)) {
        return first;
    }

    for (node = first, prev = NULL;
         node != NULL;
         prev = node, node = node->n_next) {
        if (run_mode == FN) {
            /* If we have a previous node and it is the current
             * folder, return this node. */
            if (prev != NULL && strcmp(prev->n_name, cur) == 0) {
                return node;
            }
        } else if (run_mode == FP) {
            if (strcmp(node->n_name, cur) == 0) {
                /* Found current folder in fp mode; if we have a
                 * previous node in the list, return it; else return
                 * the last node. */
                if (prev == NULL) {
                    return last;
                }
                return prev;
            }
        } else if (run_mode == UNSEEN) {
            if (node->n_field == NULL) {
                continue;
            }

            printf("\n%d unseen messages in %s",
                   count_unseen(node->n_field),
                   node->n_name);
            if (strcmp(node->n_name, cur) == 0) {
                puts(" (*: current folder)");
            } else {
                puts("");
            }
            fflush(stdout);

            command = concat ("scan +", node->n_name, " unseen", NULL);
            system(command);
            free(command);
        } else {
            if (node->n_field == NULL) {
                continue;
            }

            count = count_unseen(node->n_field);
            total += count;

            printf("%-*s %6d.%c %s\n",
                   folder_len, node->n_name,
                   count,
                   (strcmp(node->n_name, cur) == 0 ? '*' : ' '),
                   node->n_field);
        }
    }

    /* If we're fn, we haven't checked the last node yet.  If it's the
     * current folder, return the first node. */
    if (run_mode == FN && strcmp(last->n_name, cur) == 0) {
        return first;
    }

    if (run_mode == NEW) {
        printf("%-*s %6d.\n", folder_len, " total", total);
    }

    return cur_node;
}

int
main (int argc, char **argv)
{
    struct node *folder;

#ifdef LOCALE
    setlocale(LC_ALL, "");
#endif

    if (argv[1] != NULL) {
        invo_name = r1bindex (argv[1], '/');
    } else {
        invo_name = r1bindex (argv[0], '/');
    }

    if (strcmp(invo_name, "fn") == 0) {
        run_mode = FN;
    } else if (strcmp(invo_name, "fp") == 0) {
        run_mode = FP;
    } else if (strcmp(invo_name, "unseen") == 0) {
        run_mode = UNSEEN;
    }

    /* read user profile/context */
    context_read();

    unseen = context_find (usequence);
    if (unseen == NULL || unseen[0] == '\0') {
        unseen = "unseen";
    }

    folder = doit(context_find(pfolder));
    if (folder == NULL) {
        done (0);
        return 1;
    }

    if (run_mode == UNSEEN) {
        /* All the scan(1)s it runs change the current folder, so we
         * need to put it back.  Unfortunately, context_replace lamely
         * ignores the new value you give it if it is the same one it
         * has in memory.  So, we'll be lame, too.  I'm not sure if i
         * should just change context_replace... */
        context_replace (pfolder, "defeat_context_replace_optimization");
    }

    /* update current folder */
    context_replace (pfolder, folder->n_name);

    if (run_mode == FN || run_mode == FP) {
        printf("%s  %s\n", folder->n_name, folder->n_field);
    }

    context_save ();    /* save the context file */

    done (0);
    return 1;
}


Index: uip/Makefile.in
===================================================================
RCS file: /sources/nmh/nmh/uip/Makefile.in,v
retrieving revision 1.22
diff -a -u -r1.22 Makefile.in
--- uip/Makefile.in     24 Dec 2005 17:17:38 -0000      1.22
+++ uip/Makefile.in     21 Jul 2008 23:16:47 -0000
@@ -60,7 +60,7 @@
 # commands to build
 CMDS = ali anno burst comp dist flist folder forw install-mh mark mhbuild \
        mhlist mhmail mhn mhparam mhpath mhshow mhstore msgchk \
-       msh packf pick prompter refile repl rmf rmm scan send show \
+       msh new packf pick prompter refile repl rmf rmm scan send show \
        sortm whatnow whom
 
 ## removed this from CMDS until I can fix it
@@ -83,7 +83,7 @@
        mhbuildsbr.c mhcachesbr.c mhfree.c mhl.c mhlist.c mhlistsbr.c     \
        mhlsbr.c mhmail.c mhmisc.c mhn.c mhoutsbr.c mhparam.c mhparse.c   \
        mhpath.c mhshow.c mhshowsbr.c mhstore.c mhstoresbr.c mhtest.c     \
-       msgchk.c msh.c mshcmds.c packf.c pick.c picksbr.c popi.c popsbr.c \
+       msgchk.c msh.c mshcmds.c new.c packf.c pick.c picksbr.c popi.c popsbr.c 
\
        post.c prompter.c rcvdist.c rcvpack.c rcvstore.c rcvtty.c         \
        refile.c repl.c replsbr.c rmf.c rmm.c scan.c scansbr.c send.c     \
        sendsbr.c show.c slocal.c sortm.c spost.c termsbr.c viamail.c     \
@@ -183,6 +183,9 @@
 msh: msh.o mshcmds.o vmhsbr.o picksbr.o scansbr.o dropsbr.o mhlsbr.o termsbr.o 
$(LOCALLIBS)
        $(LINK) msh.o mshcmds.o vmhsbr.o picksbr.o scansbr.o dropsbr.o mhlsbr.o 
termsbr.o $(LINKLIBS) $(TERMLIB)
 
+new: new.o $(LOCALLIBS)
+       $(LINK) new.o $(LINKLIBS)
+
 packf: packf.o dropsbr.o $(LOCALLIBS)
        $(LINK) packf.o dropsbr.o $(LINKLIBS)
 
@@ -265,11 +268,17 @@
 install-lcmds:
        rm -f $(DESTDIR)$(bindir)/flists
        rm -f $(DESTDIR)$(bindir)/folders
+       rm -f $(DESTDIR)$(bindir)/fn
+       rm -f $(DESTDIR)$(bindir)/fp
+       rm -f $(DESTDIR)$(bindir)/unseen
        rm -f $(DESTDIR)$(bindir)/prev
        rm -f $(DESTDIR)$(bindir)/next
        rm -f $(DESTDIR)$(libdir)/install-mh
        $(LN) $(DESTDIR)$(bindir)/flist  $(DESTDIR)$(bindir)/flists
        $(LN) $(DESTDIR)$(bindir)/folder $(DESTDIR)$(bindir)/folders
+       $(LN) $(DESTDIR)$(bindir)/new    $(DESTDIR)$(bindir)/fn
+       $(LN) $(DESTDIR)$(bindir)/new    $(DESTDIR)$(bindir)/fp
+       $(LN) $(DESTDIR)$(bindir)/new    $(DESTDIR)$(bindir)/unseen
        $(LN) $(DESTDIR)$(bindir)/show   $(DESTDIR)$(bindir)/prev
        $(LN) $(DESTDIR)$(bindir)/show   $(DESTDIR)$(bindir)/next
 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]