test/repl/test-trailing-newline \
test/scan/test-header-parsing \
test/scan/test-scan \
+ test/scan/test-scan-file \
test/scan/test-scan-multibyte \
test/send/test-sendfrom \
test/sequences/test-flist \
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 \
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 \
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
-.TH SCAN %manext1% 2014-01-20 "%nmhversion%"
+.TH SCAN %manext1% 2020-02-17 "%nmhversion%"
.
.\" %nmhwarning%
.
.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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
--- /dev/null
+#!/bin/sh
+######################################################
+#
+# Test "scan -file"
+#
+######################################################
+
+if test -z "${MH_OBJ_DIR}"; then
+ srcdir=`dirname "$0"`/../..
+ MH_OBJ_DIR=`cd "$srcdir" && pwd`; export MH_OBJ_DIR
+fi
+
+. "$MH_OBJ_DIR/test/common.sh"
+
+expected="$MH_TEST_DIR/$$.expected"
+actual="$MH_TEST_DIR/$$.actual"
+
+setup_test
+cat >"$expected" <<EOF
+ 1 09/29 Test1 Testing message 1<<This is message number 1 >>
+ 2 09/29 Test2 Testing message 2<<This is message number 2 >>
+ 3 09/29 Test3 Testing message 3<<This is message number 3 >>
+ 4 09/29 Test4 Testing message 4<<This is message number 4 >>
+ 5 09/29 Test5 Testing message 5<<This is message number 5 >>
+ 6 09/29 Test6 Testing message 6<<This is message number 6 >>
+ 7 09/29 Test7 Testing message 7<<This is message number 7 >>
+ 8 09/29 Test8 Testing message 8<<This is message number 8 >>
+ 9 09/29 Test9 Testing message 9<<This is message number 9 >>
+ 10 09/29 Test10 Testing message 10<<This is message number 10 >>
+EOF
+
+## Combine the inbox messages into one spool file
+
+rm -f "$MH_TEST_DIR/spoolfile"
+for i in 1 2 3 4 5 6 7 8 9 10
+do
+ printf '\1\1\1\1\n'
+ cat "$MH_TEST_DIR/Mail/inbox/$i"
+ printf '\1\1\1\1\n'
+done > "$MH_TEST_DIR/spoolfile"
+
+## Test scanning the file
+
+start_test 'scan file'
+
+run_prog scan -file "$MH_TEST_DIR/spoolfile" -width 80 >"$actual" || exit 1
+check "$expected" "$actual" 'keep first'
+
+test -z "$MH_TEST_NOCLEANUP" && rm -f "$MH_TEST_DIR/spoolfile"
+
+## Convert the test inbox folder into a Maildir directory
+
+rm -rf "$MH_TEST_DIR/Maildir/new" "$MH_TEST_DIR/Maildir/cur"
+mkdir -p "$MH_TEST_DIR/Maildir/new" "$MH_TEST_DIR/Maildir/cur" || exit 1
+
+# move the 10 test messages into the Maildir directories
+subdir=cur
+for i in 1 2 3 4 5 6 7 8 9 10
+do
+ # split messages between subdirs, to test that "scan" sorts them correctly
+ if [ "$subdir" = cur ]; then
+ subdir=new
+ info=
+ else
+ subdir=cur
+ info=:2,
+ fi
+ arith_eval 1567890000 + "$i"; timepart=$arith_val
+ destfile=$MH_TEST_DIR/Maildir/$subdir/$timepart.P$$.testhost$info
+ mv "$MH_TEST_DIR/Mail/inbox/$i" "$destfile"
+ # Maildir sorts by mtime, so make sure all times are unique
+ touch -c -m --date="@$timepart" "$destfile"
+done
+
+# should be empty now; remove it to make sure "scan -file" is not using it
+rmdir "$MH_TEST_DIR/Mail/inbox"
+
+## Test scanning the Maildir
+
+start_test 'scan file Maildir'
+
+run_prog scan -file "$MH_TEST_DIR/Maildir" -width 80 >"$actual" || exit 1
+check "$expected" "$actual"
+
+test -z "$MH_TEST_NOCLEANUP" && rm -rf "$MH_TEST_DIR/Maildir"
+
+
+finish_test
+exit "${failed:-0}"
#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)
#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 {
/*
* 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)
{
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);
#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) \
{
bool clearflag = false;
bool hdrflag = false;
- int ontty;
+ int ontty = 0;
int width = -1;
bool revflag = false;
int i, state, msgnum;
while ((cp = *argp++)) {
if (*cp == '-') {
switch (smatch (++cp, switches)) {
- case AMBIGSW:
+ case AMBIGSW:
ambigsw (cp, switches);
done (1);
- case UNKWNSW:
+ case UNKWNSW:
die("-%s unknown", cp);
- case HELPSW:
+ case HELPSW:
snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
invo_name);
print_help (buf, switches, 1);
print_version(invo_name);
done (0);
- case CLRSW:
+ case CLRSW:
clearflag = true;
continue;
- case NCLRSW:
+ case NCLRSW:
clearflag = false;
continue;
- case FORMSW:
+ case FORMSW:
if (!(form = *argp++) || *form == '-')
die("missing argument to %s", argp[-2]);
format = NULL;
continue;
- case FMTSW:
+ case FMTSW:
if (!(format = *argp++) || *format == '-')
die("missing argument to %s", argp[-2]);
form = NULL;
continue;
- case HEADSW:
+ case HEADSW:
hdrflag = true;
continue;
- case NHEADSW:
+ case NHEADSW:
hdrflag = false;
continue;
- case WIDTHSW:
+ case WIDTHSW:
if (!(cp = *argp++) || *cp == '-')
die("missing argument to %s", argp[-2]);
width = atoi (cp);
*/
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)
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));
}
switch (state = scan (in, msgnum, 0, nfs, width,
msgnum == mp->curmsg, unseen,
folder, 0L, 1, &scanl)) {
- case SCNMSG:
- case SCNERR:
+ case SCNMSG:
+ case SCNERR:
break;
- default:
+ default:
die("scan() botch (%d)", state);
- case SCNEOF:
+ case SCNEOF:
inform("message %d: empty", msgnum);
break;
}