diff --git a/Makefile.am b/Makefile.am index ffc4d65b..0a8f2b48 100644 --- a/Makefile.am +++ b/Makefile.am @@ -403,6 +403,7 @@ noinst_HEADERS = \ sbr/m_name.h \ sbr/m_popen.h \ sbr/m_rand.h \ + sbr/maildir_read_and_sort.h \ sbr/makedir.h \ sbr/message_id.h \ sbr/mime_type.h \ @@ -1120,6 +1121,7 @@ sbr_libmh_a_SOURCES = \ sbr/m_name.c \ sbr/m_popen.c \ sbr/m_rand.c \ + sbr/maildir_read_and_sort.c \ sbr/makedir.c \ sbr/message_id.c \ sbr/mf.c \ diff --git a/docs/pending-release-notes b/docs/pending-release-notes index 5a932160..a2c415c9 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -23,6 +23,7 @@ NEW FEATURES comment indicator. 3) Add a postproc entry that points to the post that you use. That can be viewed with "mhparam postproc". +- scan(1) -file argument can be a Maildir directory. ----------------- OBSOLETE FEATURES diff --git a/man/scan.man b/man/scan.man index 6d41cebb..f823d787 100644 --- a/man/scan.man +++ b/man/scan.man @@ -92,13 +92,25 @@ The .I filename switch allows the user to obtain a .B scan -listing of a mail drop file as produced by -.BR packf . +listing of a mail drop. This listing -includes every message in the file (you can't scan individual messages). +includes every message in the mail drop (you can't scan individual messages). The switch .B \-reverse is ignored with this option. +If +.I filename +is a file, it can be in +.B mbox +or +.B MMDF +format, as produced by +.BR packf . +If +.I filename +is a directory, it is considered to be in +.B Maildir +format. .PP The switch .B \-width diff --git a/sbr/maildir_read_and_sort.c b/sbr/maildir_read_and_sort.c new file mode 100644 index 00000000..bb67f47a --- /dev/null +++ b/sbr/maildir_read_and_sort.c @@ -0,0 +1,75 @@ +/* maildir_read_and_sort.c -- returns a sorted list of msgs in a Maildir. + * + * This code is Copyright (c) 2020, 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 "sbr/maildir_read_and_sort.h" + +#include "sbr/concat.h" +#include "sbr/error.h" + +static int maildir_srt(const void *va, const void *vb) PURE; + +static int +maildir_srt(const void *va, const void *vb) +{ + const struct Maildir_entry *a = va, *b = vb; + if (a->mtime > b->mtime) + return 1; + if (a->mtime < b->mtime) + return -1; + return 0; +} + +static void read_one_dir (char *dirpath, char *dirpath_base, + struct Maildir_entry **maildir, int *maildir_size, + int *nmsgs) { + DIR *md; + struct dirent *de; + struct stat ms; + char *cp; + + cp = concat (dirpath_base, dirpath, NULL); + if ((md = opendir(cp)) == NULL) + die("unable to open %s", cp); + while ((de = readdir (md)) != NULL) { + if (de->d_name[0] == '.') + continue; + if (*nmsgs >= *maildir_size) { + if ((*maildir = realloc(*maildir, sizeof(**maildir) * (2*(*nmsgs)+16))) == NULL) + die("not enough memory for %d messages", 2*(*nmsgs)+16); + *maildir_size = 2*(*nmsgs)+16; + } + (*maildir)[*nmsgs].filename = concat (cp, "/", de->d_name, NULL); + if (stat((*maildir)[*nmsgs].filename, &ms) != 0) + adios ((*maildir)[*nmsgs].filename, "couldn't get delivery time"); + (*maildir)[*nmsgs].mtime = ms.st_mtime; + (*nmsgs)++; + } + free (cp); + closedir (md); +} + +/* + * On return, maildir_out will be NULL or point to memory the caller may free. + */ +void maildir_read_and_sort (char *maildirpath, + struct Maildir_entry **maildir_out, + int *num_maildir_entries_out) { + int num_maildir_entries = 0; + struct Maildir_entry *Maildir = NULL; + int msize = 0; + + read_one_dir("/new", maildirpath, &Maildir, &msize, &num_maildir_entries); + read_one_dir("/cur", maildirpath, &Maildir, &msize, &num_maildir_entries); + if (num_maildir_entries == 0) + die("no mail to incorporate"); + qsort (Maildir, num_maildir_entries, sizeof(*Maildir), maildir_srt); + + *num_maildir_entries_out = num_maildir_entries; + *maildir_out = Maildir; +} diff --git a/sbr/maildir_read_and_sort.h b/sbr/maildir_read_and_sort.h new file mode 100644 index 00000000..a100288f --- /dev/null +++ b/sbr/maildir_read_and_sort.h @@ -0,0 +1,15 @@ +/* maildir_read_and_sort.h -- returns a sorted list of msgs in a Maildir. + * + * This code is Copyright (c) 2020, by the authors of nmh. See the + * COPYRIGHT file in the root directory of the nmh distribution for + * complete copyright information. + */ + +struct Maildir_entry { + char *filename; + time_t mtime; +}; + +void maildir_read_and_sort (char *maildirpath, + struct Maildir_entry **maildir_out, + int *num_msgs_out); diff --git a/uip/inc.c b/uip/inc.c index 1d8e94f3..7ebb0581 100644 --- a/uip/inc.c +++ b/uip/inc.c @@ -67,6 +67,7 @@ #include "sbr/lock_file.h" #include "sbr/m_maildir.h" #include "sbr/m_mktemp.h" +#include "sbr/maildir_read_and_sort.h" #ifndef TLS_SUPPORT # define TLSminc(a) (a) @@ -118,11 +119,8 @@ DEFINE_SWITCH_ARRAY(INC, switches); #define INC_FILE 0 #define INC_POP 1 -static struct Maildir_entry { - char *filename; - time_t mtime; -} *Maildir = NULL; -static int num_maildir_entries = 0; +static struct Maildir_entry *Maildir; +static int num_maildir_entries; static bool snoop; typedef struct { @@ -181,21 +179,9 @@ static FILE *in; /* * prototypes */ -static int maildir_srt(const void *va, const void *vb) PURE; static void inc_done(int) NORETURN; static int pop_action(void *closure, char *); -static int -maildir_srt(const void *va, const void *vb) -{ - const struct Maildir_entry *a = va, *b = vb; - if (a->mtime > b->mtime) - return 1; - if (a->mtime < b->mtime) - return -1; - return 0; -} - int main (int argc, char **argv) { @@ -478,53 +464,7 @@ main (int argc, char **argv) if (stat (newmail, &s1) == NOTOK || s1.st_size == 0) die("no mail to incorporate"); if (s1.st_mode & S_IFDIR) { - DIR *md; - struct dirent *de; - struct stat ms; - int i; - i = 0; - cp = concat (newmail, "/new", NULL); - if ((md = opendir(cp)) == NULL) - die("unable to open %s", cp); - while ((de = readdir (md)) != NULL) { - if (de->d_name[0] == '.') - continue; - if (i >= num_maildir_entries) { - if ((Maildir = realloc(Maildir, sizeof(*Maildir) * (2*i+16))) == NULL) - die("not enough memory for %d messages", 2*i+16); - num_maildir_entries = 2*i+16; - } - Maildir[i].filename = concat (cp, "/", de->d_name, NULL); - if (stat(Maildir[i].filename, &ms) != 0) - adios (Maildir[i].filename, "couldn't get delivery time"); - Maildir[i].mtime = ms.st_mtime; - i++; - } - free (cp); - closedir (md); - cp = concat (newmail, "/cur", NULL); - if ((md = opendir(cp)) == NULL) - die("unable to open %s", cp); - while ((de = readdir (md)) != NULL) { - if (de->d_name[0] == '.') - continue; - if (i >= num_maildir_entries) { - if ((Maildir = realloc(Maildir, sizeof(*Maildir) * (2*i+16))) == NULL) - die("not enough memory for %d messages", 2*i+16); - num_maildir_entries = 2*i+16; - } - Maildir[i].filename = concat (cp, "/", de->d_name, NULL); - if (stat(Maildir[i].filename, &ms) != 0) - adios (Maildir[i].filename, "couldn't get delivery time"); - Maildir[i].mtime = ms.st_mtime; - i++; - } - free (cp); - closedir (md); - if (i == 0) - die("no mail to incorporate"); - num_maildir_entries = i; - qsort (Maildir, num_maildir_entries, sizeof(*Maildir), maildir_srt); + maildir_read_and_sort(newmail, &Maildir, &num_maildir_entries); } cp = mh_xstrdup(newmail); diff --git a/uip/scan.c b/uip/scan.c index 12879050..4b72e1a3 100644 --- a/uip/scan.c +++ b/uip/scan.c @@ -35,6 +35,7 @@ #include "h/utils.h" #include "sbr/m_maildir.h" #include "sbr/terminal.h" +#include "sbr/maildir_read_and_sort.h" #define SCAN_SWITCHES \ X("clear", 0, CLRSW) \ @@ -64,7 +65,7 @@ main (int argc, char **argv) { bool clearflag = false; bool hdrflag = false; - int ontty; + int ontty = 0; int width = -1; bool revflag = false; int i, state, msgnum; @@ -167,10 +168,10 @@ main (int argc, char **argv) */ nfs = new_fs (form, format, FORMAT); - /* - * We are scanning a maildrop file - */ if (file) { + /* + * We have a -file argument + */ if (msgs.size) die("\"msgs\" not allowed with -file"); if (folder) @@ -181,10 +182,63 @@ main (int argc, char **argv) in = stdin; file = "stdin"; } else { - if ((in = fopen (file, "r")) == NULL) - adios (file, "unable to open"); + /* check if "file" is really a Maildir folder */ + struct stat st; + if (stat (file, &st) == NOTOK) + adios (file, "unable to stat"); + if (!(st.st_mode & S_IFDIR)) { + if ((in = fopen (file, "r")) == NULL) + adios (file, "unable to open"); + } else { + /* + * We are scanning a Maildir folder + */ + struct Maildir_entry *Maildir; + int num_maildir_entries; + int msgnum = 0; + char *fnp; + FILE *in; + int i; + + maildir_read_and_sort(file, &Maildir, &num_maildir_entries); + for (i = 0; i < num_maildir_entries; i++) { + msgnum++; + fnp = Maildir[i].filename; + + if ((in = fopen (fnp, "r")) == NULL) { + admonish (fnp, "unable to open message"); + continue; + } + + switch (state = scan (in, msgnum, 0, nfs, width, + 0, 0, hdrflag ? file : NULL, + 0L, 1, &scanl)) { + case SCNMSG: + case SCNERR: + break; + + default: + die("scan() botch (%d)", state); + + case SCNEOF: + inform("message %d: empty", msgnum); + break; + } + if (scanl) + charstring_clear(scanl); + scan_finished (); + hdrflag = false; + fclose (in); + if (ontty) + fflush (stdout); + } + done (0); + } } + /* + * We are scanning a maildrop file + */ if (hdrflag) { printf ("FOLDER %s\t%s\n", file, dtimenow (1)); }