-
-/*
- * mh.h -- main header file for all of nmh
+/* mh.h -- main header file for all of nmh
*/
#include <h/nmh.h>
+/* It's undefined behaviour in C99 to convert from a function pointer to
+ * a data-object pointer, e.g. void pointer. gcc's -pedantic warns of
+ * this and can stop compilation. POSIX requires the operation however,
+ * e.g. for dlsym(3), and so we know it's safe on POSIX platforms, e.g.
+ * the pointers are of the same size. Thus use a union to subvert gcc's
+ * check. The function-pointer equivalent of a void pointer is any
+ * function-pointer type as all function pointers are defined to be
+ * convertible from one another; use the simplest available. */
+typedef union {
+ void *v;
+ void (*f)(void);
+} generic_pointer;
+
/*
* Well-used constants
*/
#define NOTOK (-1) /* syscall()s return this on error */
#define OK 0 /* ditto on success */
-#define DONE 1 /* trinary logic */
+#define DONE 1 /* ternary logic */
#define ALL ""
-#define Nbby 8 /* number of bits/byte */
#define MAXARGS 1000 /* max arguments to exec */
#define NFOLDERS 1000 /* max folder arguments on command line */
#define DMAXFOLDER 4 /* typical number of digits */
#define MAXFOLDER 1000 /* message increment */
-#ifndef FALSE
-#define FALSE 0
-#endif
-#ifndef TRUE
-#define TRUE 1
-#endif
-typedef unsigned char boolean; /* not int so we can pack in a structure */
-
-/* If we're using gcc then give it some information about
- * functions that abort.
+/*
+ * This macro is for use by scan, for example, so that platforms with
+ * a small BUFSIZ can easily allocate larger buffers.
*/
+#define NMH_BUFSIZ max(BUFSIZ, 8192)
+
+/* If we're using gcc then tell it extra information so it can do more
+ * compile-time checks. */
#if __GNUC__ > 2
#define NORETURN __attribute__((__noreturn__))
-#define NMH_UNUSED(i) (void) i
+#define CONST __attribute__((const))
+#define MALLOC __attribute__((malloc))
+#define NONNULL(...) __attribute__((nonnull(__VA_ARGS__)))
+#define PURE __attribute__((pure))
+#define ENDNULL __attribute__((sentinel))
#else
#define NORETURN
-#define NMH_UNUSED(i) i
+#define CHECK_PRINTF(fmt, arg)
+#define ALLOC_SIZE(...)
+#define CONST
+#define MALLOC
+#define NONNULL(...)
+#define PURE
+#define ENDNULL
+#endif
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)
+#define ALLOC_SIZE(...) __attribute__((alloc_size(__VA_ARGS__)))
+#define CHECK_PRINTF(fmt, arg) __attribute__((format(printf, fmt, arg)))
+#else
+#define ALLOC_SIZE(...)
+#define CHECK_PRINTF(fmt, arg)
#endif
+/* Silence the compiler's "unused variable" warning. */
+#define NMH_UNUSED(i) (void)i
+
+/* DIM gives the number of elements in the one-dimensional array a. */
+#define DIM(a) (sizeof (a) / sizeof (*(a)))
+
+/* LEN gives the strlen() of string constant s, excluding the
+ * terminating NUL. */
+#define LEN(s) (sizeof (s) - 1)
+
+/* FENDNULL fends off NULL by giving an empty string instead. */
+#define FENDNULL(s) ((s) ? (s) : "")
+
+/* If not specified in a file and PAGER is NULL or empty. */
+#define DEFAULT_PAGER "more"
+
+/*
+ * char array that keeps track of size in both bytes and characters
+ * Usage note:
+ * Don't store return value of charstring_buffer() and use later
+ * after intervening push_back's; use charstring_buffer_copy()
+ * instead.
+ */
+typedef struct charstring *charstring_t;
+
+charstring_t charstring_create (size_t);
+charstring_t charstring_copy (const charstring_t) NONNULL(1);
+void charstring_free (charstring_t);
+/* Append a single-byte character: */
+void charstring_push_back (charstring_t, const char) NONNULL(1);
+/* Append possibly multi-byte character(s): */
+void charstring_push_back_chars (charstring_t, const char [], size_t, size_t) NONNULL(1);
+void charstring_append (charstring_t, const charstring_t) NONNULL(2);
+void charstring_append_cstring (charstring_t, const char []) NONNULL(2);
+void charstring_clear (charstring_t) NONNULL(1);
+/* Don't store return value of charstring_buffer() and use later after
+ intervening push_back's; use charstring_buffer_copy() instead. */
+const char *charstring_buffer (const charstring_t) NONNULL(1);
+/* User is responsible for free'ing result of buffer copy. */
+char *charstring_buffer_copy (const charstring_t) NONNULL(1);
+size_t charstring_bytes (const charstring_t) NONNULL(1) PURE;
+size_t charstring_chars (const charstring_t) NONNULL(1) PURE;
+/* Length of the last character in the charstring. */
+int charstring_last_char_len (const charstring_t) NONNULL(1);
+
/*
* user context/profile structure
*/
char *sw;
- /* The minchars field is apparently used like this:
+ /*
+ * The previous comments here about minchars was incorrect; this is
+ * (AFAIK) the correct information.
+ *
+ * A minchars of "0" means this switch can be abbreviated to any number
+ * of characters (assuming the abbreviation does not match any other
+ * switches).
+ *
+ * A positive value for minchars means that when the user specifies
+ * the switch on the command line, it MUST be at least that many
+ * characters.
+ *
+ * A negative value for minchars means that the user-given switch must
+ * be that many characters, but will NOT be shown in -help output.
+ *
+ * So what should I use? Well, for nearly all switches you want to specify
+ * a minchars of 0. smatch will report an error if the switch given
+ * matches more than one entry. Let's say you have the following
+ * two switches: -append and -apply. -app will return AMBIGSW from
+ * smatch. -appe and -appl will work fine. So 0 is the correct choice
+ * here.
+ *
+ * The only time you want to specify a minimum length is if you have
+ * a switch who's name is a substring of a longer switch. The example
+ * you see sometimes in the code is -form and -format. If you gave a
+ * minchars of 0 for both, -form would match both -form AND -format,
+ * and you'd always get AMBIGSW. The solution is to specify a minchars
+ * of 5 for -format; that way just -form will just match -form. When
+ * a minchars is given, the -help output will specify the minimum
+ * switch length, like this:
+ *
+ * -(forma)t string
+ *
+ * A negative value works the same way, except the switch isn't printed
+ * in -help. Why would you do that? Well, there are a few instances
+ * of internal switches and some switches which only appear if a particular
+ * feature is enabled (such as SASL or TLS). Lately I've been of the
+ * opinion that all switches should be specified, even if they are
+ * internal or use non-available features, but currently the smatch
+ * code still supports this.
+ *
+ * This isn't the appropriate place to make this note, but since I was
+ * here ... when creating switches, you should make a negation switch
+ * right after the enabling switch. E.g. you should have:
+ *
+ * X("sasl", 0, SASLSW) \
+ * X("nosasl", 0, NOSASLSW) \
+ *
+ * in the switch array, because when you run -help, print_sw will detect
+ * this and output:
+ *
+ * -[no]sasl
+ */
- -# : Switch can be abbreviated to # characters; switch hidden in -help.
- 0 : Switch can't be abbreviated; switch shown in -help.
- # : Switch can be abbreviated to # characters; switch shown in -help. */
int minchars;
/*
/*
* Macros to use when declaring struct swit arrays.
*
- * These macros are what known as X-Macros. In your source code you
+ * These macros use a technique known as X-Macros. In your source code you
* use them like this:
*
* #define FOO_SWITCHES \
*
* The argument to each entry in FOO_SWITCHES are the switch name (sw),
* the minchars field (see above) and the return value for this switch.
+ * Note that the last entry in the above definition must either omit the
+ * continuation backslash, or be followed by a blank line. In the nmh
+ * code the style is to have every line include a backslash and follow
+ * the SWITCHES macro definition by a blank line.
*
* After you define FOO_SWITCHES, you instantiate it as follows:
*
extern struct swit anoyes[]; /* standard yes/no switches */
-#define ATTACHFORMATS 3 /* Number of send attach formats. */
-
/*
* general folder attributes
*/
#define READONLY (1<<0) /* No write access to folder */
-#define SEQMOD (1<<1) /* folder's sequences modifed */
+#define SEQMOD (1<<1) /* folder's sequences modified */
#define ALLOW_NEW (1<<2) /* allow the "new" sequence */
#define OTHERS (1<<3) /* folder has other files */
-#define MODIFIED (1<<4) /* msh in-core folder modified */
-#define FBITS "\020\01READONLY\02SEQMOD\03ALLOW_NEW\04OTHERS\05MODIFIED"
-
-/*
- * type for holding the sequence set of a message
- */
-typedef unsigned long seqset_t;
+#define FBITS "\020\01READONLY\02SEQMOD\03ALLOW_NEW\04OTHERS"
/*
* first free slot for user defined sequences
* and attributes
*/
-#define FFATTRSLOT 5
-
-/*
- * Determine the number of user defined sequences we
- * can have. The first FFATTRSLOT sequence flags are for
- * internal nmh message flags.
- */
-#define NUMATTRS ((sizeof(seqset_t) * Nbby) - FFATTRSLOT)
+#define FFATTRSLOT 4
/*
* internal messages attributes (sequences)
*/
-#define EXISTS (1<<0) /* exists */
-#define DELETED (1<<1) /* deleted */
-#define SELECTED (1<<2) /* selected for use */
-#define SELECT_EMPTY (1<<3) /* "new" message */
-#define SELECT_UNSEEN (1<<4) /* inc/show "unseen" */
-
-#define MBITS "\020\01EXISTS\02DELETED\03SELECTED\04NEW\05UNSEEN"
+#define EXISTS (0) /* exists */
+#define SELECTED (1) /* selected for use */
+#define SELECT_EMPTY (2) /* "new" message */
+#define SELECT_UNSEEN (3) /* inc/show "unseen" */
+
+#define MBITS "\020\01EXISTS\02SELECTED\03NEW\04UNSEEN"
+
+/* A vector of bits for tracking the sequence membership of a single
+ * message. Do not access the struct members; use vector.c.
+ * Do not move or copy this struct as it may contain a pointer to
+ * itself; use bvector_copy(). */
+struct bvector {
+ unsigned long *bits;
+ size_t maxsize;
+ unsigned long tiny[2]; /* Default fixed-size storage for bits. */
+};
+typedef struct bvector *bvector_t;
+
+bvector_t bvector_create (void);
+void bvector_init(struct bvector *bv) NONNULL(1);
+void bvector_copy (bvector_t, bvector_t) NONNULL(1, 2);
+void bvector_free (bvector_t) NONNULL(1);
+void bvector_fini(struct bvector *bv) NONNULL(1);
+void bvector_clear (bvector_t, size_t) NONNULL(1);
+void bvector_clear_all (bvector_t) NONNULL(1);
+void bvector_set (bvector_t, size_t) NONNULL(1);
+unsigned int bvector_at (bvector_t, size_t) NONNULL(1) PURE;
+unsigned long bvector_first_bits (bvector_t) NONNULL(1) PURE;
+
+typedef struct svector *svector_t;
+
+svector_t svector_create (size_t);
+void svector_free (svector_t) NONNULL(1);
+char *svector_push_back (svector_t, char *) NONNULL(1);
+char *svector_at (svector_t, size_t) NONNULL(1);
+char **svector_find(svector_t, const char *) NONNULL(1) PURE;
+char **svector_strs (svector_t) NONNULL(1) PURE;
+size_t svector_size (svector_t) NONNULL(1) PURE;
+
+typedef struct ivector *ivector_t;
+
+ivector_t ivector_create (size_t);
+void ivector_free (ivector_t) NONNULL(1);
+int ivector_push_back (ivector_t, int) NONNULL(1);
+int ivector_at (ivector_t, size_t) NONNULL(1);
+int *ivector_atp (ivector_t, size_t) NONNULL(1);
/*
* Primary structure of folder/message information
char *foldpath; /* Pathname of folder */
/*
- * Name of sequences in this folder. We add an
- * extra slot, so we can NULL terminate the list.
+ * Name of sequences in this folder.
*/
- char *msgattrs[NUMATTRS + 1];
+ svector_t msgattrs;
/*
* bit flags for whether sequence
* is public (0), or private (1)
*/
- seqset_t attrstats;
+ bvector_t attrstats;
/*
* These represent the lowest and highest possible
int hghoff;
/*
- * This is an array of seqset_t which we allocate dynamically.
- * Each seqset_t is a set of bits flags for a particular message.
+ * This is an array of bvector_t which we allocate dynamically.
+ * Each bvector_t is a set of bits flags for a particular message.
* These bit flags represent general attributes such as
* EXISTS, SELECTED, etc. as well as track if message is
* in a particular sequence.
*/
- seqset_t *msgstats; /* msg status */
+ size_t num_msgstats;
+ struct bvector *msgstats; /* msg status */
/*
* A FILE handle containing an open filehandle for the sequence file
/*
* Amount of space to allocate for msgstats. Allocate
- * the array to have space for messages numbers lo to hi.
+ * the array to have space for messages numbered lo to hi.
+ * Use MSGSTATNUM to load mp->num_msgstats first.
*/
-#define MSGSTATSIZE(mp,lo,hi) ((size_t) (((hi) - (lo) + 1) * sizeof(*(mp)->msgstats)))
+#define MSGSTATNUM(lo, hi) ((size_t) ((hi) - (lo) + 1))
+#define MSGSTATSIZE(mp) ((mp)->num_msgstats * sizeof *(mp)->msgstats)
/*
* macros for message and sequence manipulation
*/
-#define clear_msg_flags(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] = 0)
-#define copy_msg_flags(mp,i,j) \
- ((mp)->msgstats[(i) - mp->lowoff] = (mp)->msgstats[(j) - mp->lowoff])
-#define get_msg_flags(mp,ptr,msgnum) (*(ptr) = (mp)->msgstats[(msgnum) - mp->lowoff])
-#define set_msg_flags(mp,ptr,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] = *(ptr))
-
-#define does_exist(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] & EXISTS)
-#define unset_exists(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] &= ~EXISTS)
-#define set_exists(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] |= EXISTS)
-
-#define is_selected(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] & SELECTED)
-#define unset_selected(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] &= ~SELECTED)
-#define set_selected(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] |= SELECTED)
-
-#define is_select_empty(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] & SELECT_EMPTY)
+#define msgstat(mp,n) ((mp)->msgstats + (n) - mp->lowoff)
+#define clear_msg_flags(mp,msgnum) bvector_clear_all (msgstat(mp, msgnum))
+#define copy_msg_flags(mp,i,j) bvector_copy (msgstat(mp,i), msgstat(mp,j))
+#define get_msg_flags(mp,ptr,msgnum) bvector_copy (ptr, msgstat(mp, msgnum))
+#define set_msg_flags(mp,ptr,msgnum) bvector_copy (msgstat(mp, msgnum), ptr)
+
+#define does_exist(mp,msgnum) bvector_at (msgstat(mp, msgnum), EXISTS)
+#define unset_exists(mp,msgnum) bvector_clear (msgstat(mp, msgnum), EXISTS)
+#define set_exists(mp,msgnum) bvector_set (msgstat(mp, msgnum), EXISTS)
+
+#define is_selected(mp,msgnum) bvector_at (msgstat(mp, msgnum), SELECTED)
+#define unset_selected(mp,msgnum) bvector_clear (msgstat(mp, msgnum), SELECTED)
+#define set_selected(mp,msgnum) bvector_set (msgstat(mp, msgnum), SELECTED)
+
+#define is_select_empty(mp,msgnum) \
+ bvector_at (msgstat(mp, msgnum), SELECT_EMPTY)
#define set_select_empty(mp,msgnum) \
- ((mp)->msgstats[(msgnum) - mp->lowoff] |= SELECT_EMPTY)
+ bvector_set (msgstat(mp, msgnum), SELECT_EMPTY)
-#define is_unseen(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] & SELECT_UNSEEN)
-#define unset_unseen(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] &= ~SELECT_UNSEEN)
-#define set_unseen(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] |= SELECT_UNSEEN)
-
-/* for msh only */
-#define set_deleted(mp,msgnum) ((mp)->msgstats[(msgnum) - mp->lowoff] |= DELETED)
+#define is_unseen(mp,msgnum) \
+ bvector_at (msgstat(mp, msgnum), SELECT_UNSEEN)
+#define unset_unseen(mp,msgnum) \
+ bvector_clear (msgstat(mp, msgnum), SELECT_UNSEEN)
+#define set_unseen(mp,msgnum) \
+ bvector_set (msgstat(mp, msgnum), SELECT_UNSEEN)
#define in_sequence(mp,seqnum,msgnum) \
- ((mp)->msgstats[(msgnum) - mp->lowoff] & ((seqset_t)1 << (FFATTRSLOT + seqnum)))
+ bvector_at (msgstat(mp, msgnum), FFATTRSLOT + seqnum)
#define clear_sequence(mp,seqnum,msgnum) \
- ((mp)->msgstats[(msgnum) - mp->lowoff] &= ~((seqset_t)1 << (FFATTRSLOT + seqnum)))
+ bvector_clear (msgstat(mp, msgnum), FFATTRSLOT + seqnum)
#define add_sequence(mp,seqnum,msgnum) \
- ((mp)->msgstats[(msgnum) - mp->lowoff] |= ((seqset_t)1 << (FFATTRSLOT + seqnum)))
+ bvector_set (msgstat(mp, msgnum), FFATTRSLOT + seqnum)
#define is_seq_private(mp,seqnum) \
- ((mp)->attrstats & ((seqset_t)1 << (FFATTRSLOT + seqnum)))
+ bvector_at (mp->attrstats, FFATTRSLOT + seqnum)
#define make_seq_public(mp,seqnum) \
- ((mp)->attrstats &= ~((seqset_t)1 << (FFATTRSLOT + seqnum)))
+ bvector_clear (mp->attrstats, FFATTRSLOT + seqnum)
#define make_seq_private(mp,seqnum) \
- ((mp)->attrstats |= ((seqset_t)1 << (FFATTRSLOT + seqnum)))
+ bvector_set (mp->attrstats, FFATTRSLOT + seqnum)
#define make_all_public(mp) \
- ((mp)->attrstats = 0)
+ mp->attrstats = bvector_create(); bvector_clear_all (mp->attrstats)
/*
* macros for folder attributes
#define other_files(mp) ((mp)->msgflags & OTHERS)
#define set_other_files(mp) ((mp)->msgflags |= OTHERS)
-#define NULLMP ((struct msgs *) 0)
-
/*
* m_getfld() message parsing
*/
followed by a colon. Add one for
terminating NULL. */
-#define LENERR (-2) /* Name too long error from getfld */
-#define FMTERR (-3) /* Message Format error */
-#define FLD 0 /* Field returned */
-#define FLDPLUS 1 /* Field returned with more to come */
-#define BODY 3 /* Body returned with more to come */
-#define FILEEOF 5 /* Reached end of input file */
+/* Token type or error returned from m_getfld(), and its internal state
+ * for the next call. */
+/* FLD detects the header's name is too long to fit in the fixed size
+ * array. */
+#define LENERR (-2)
+/* FLD reaches EOF after the header's name, or the name is followed by
+ * a linefeed rather than a colon and the body buffer isn't large enough
+ * to pretend this header line starts the body. */
+#define FMTERR (-3)
+/* The initial state, looking for headers. Returned when the header's
+ * value finishes. */
+#define FLD 0
+/* Another chunk of the header's value has been returned, but there's
+ * more to come. */
+#define FLDPLUS 1
+/* A chunk of the email's body has been returned. */
+#define BODY 3
+/* Either the end of the input file has been reached, or the delimiter
+ * between emails has been found and the caller should
+ * m_getfld_state_reset() to reset the state to FLD for continuing
+ * through the file. */
+#define FILEEOF 5
-struct m_getfld_state;
typedef struct m_getfld_state *m_getfld_state_t;
-/*
- * Maildrop styles
- */
-#define MS_DEFAULT 0 /* default (one msg per file) */
-#define MS_UNKNOWN 1 /* type not known yet */
-#define MS_MBOX 2 /* Unix-style "from" lines */
-#define MS_MMDF 3 /* string mmdlm2 */
-#define MS_MSH 4 /* whacko msh */
-
#define NOUSE 0 /* draft being re-used */
#define TFOLDER 0 /* path() given a +folder */
#define LINK "@" /* Name of link to file to which you are */
/* replying. */
-#define NMH_ATTACH_HEADER "Nmh-Attachment" /* Default header for -attach */
+/*
+ * credentials management
+ */
+typedef struct nmh_creds *nmh_creds_t;
/*
* miscellaneous macros
extern char *ctxpath; /* pathname of user's context */
extern struct node *m_defs; /* list of profile/context entries */
-/* What style to use for generated Message-ID and Content-ID header
- fields. The localname style is pid.time@localname, where time is
- in seconds. The random style replaces the localname with some
- (pseudo)random bytes and uses microsecond-resolution time. */
-int save_message_id_style (const char *);
-char *message_id (time_t, int);
-
/*
* These standard strings are defined in config.c. They are the
* only system-dependent parameters in nmh, and thus by redefining
extern char *components;
extern char *context;
extern char *current;
+extern char *credentials_file;
+extern int credentials_no_perm_check;
extern char *defaultfolder;
extern char *digestcomps;
extern char *distcomps;
extern char *mhlreply;
extern char *moreproc;
extern char *msgprot;
-extern char *mshproc;
extern char *nmhaccessftp;
extern char *nmhaccessurl;
extern char *nmhstorage;
extern char *showmimeproc;
extern char *showproc;
extern char *usequence;
+extern char *user_agent;
extern char *version_num;
extern char *version_str;
-extern char *vmhproc;
extern char *whatnowproc;
extern char *whomproc;
-extern void (*done) (int) NORETURN;
-
#include <h/prototypes.h>
-