X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/f77e0b578f71edb6f9105d5af8d54c8237cd9078..94187a80bd60baab4b9c4b949ad820d730578123:/h/mh.h diff --git a/h/mh.h b/h/mh.h index 1edf4822..ebd124d1 100644 --- a/h/mh.h +++ b/h/mh.h @@ -3,12 +3,25 @@ #include +/* 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 MAXARGS 1000 /* max arguments to exec */ @@ -20,27 +33,39 @@ * This macro is for use by scan, for example, so that platforms with * a small BUFSIZ can easily allocate larger buffers. */ -#define NMH_BUFSIZ (BUFSIZ>=8192 ? BUFSIZ : 8192) - -#ifndef FALSE -#define FALSE false -#endif -#ifndef TRUE -#define TRUE true -#endif -typedef unsigned char boolean; /* not int so we can pack in a structure */ +#define NMH_BUFSIZ max(BUFSIZ, 8192) -/* If we're using gcc then give it some information about - * functions that abort. - */ +/* 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))) @@ -48,6 +73,12 @@ typedef unsigned char boolean; /* not int so we can pack in a structure */ * 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: @@ -58,24 +89,24 @@ typedef unsigned char boolean; /* not int so we can pack in a structure */ typedef struct charstring *charstring_t; charstring_t charstring_create (size_t); -charstring_t charstring_copy (const charstring_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); +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); -void charstring_append (charstring_t, const charstring_t); -void charstring_append_cstring (charstring_t, const char []); -void charstring_clear (charstring_t); +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); +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); -size_t charstring_bytes (const charstring_t); -size_t charstring_chars (const charstring_t); +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); +int charstring_last_char_len (const charstring_t) NONNULL(1); /* * user context/profile structure @@ -101,11 +132,60 @@ struct swit { 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; /* @@ -187,37 +267,45 @@ extern struct swit anoyes[]; /* standard yes/no switches */ #define MBITS "\020\01EXISTS\02SELECTED\03NEW\04UNSEEN" -/* - * type for holding the sequence set of a message - */ +/* 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 (size_t /* initial size in bits, can be 0 */); -void bvector_copy (bvector_t, bvector_t); -void bvector_free (bvector_t); -void bvector_clear (bvector_t, size_t); -void bvector_clear_all (bvector_t); -void bvector_set (bvector_t, size_t); -unsigned int bvector_at (bvector_t, size_t); -unsigned long bvector_first_bits (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); -char *svector_push_back (svector_t, char *); -char *svector_at (svector_t, size_t); -char **svector_find(svector_t, const char *); -char **svector_strs (svector_t); -size_t svector_size (svector_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); -int ivector_push_back (ivector_t, int); -int ivector_at (ivector_t, size_t); -int *ivector_atp (ivector_t, 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 @@ -263,7 +351,7 @@ struct msgs { * in a particular sequence. */ size_t num_msgstats; - bvector_t *msgstats; /* msg status */ + struct bvector *msgstats; /* msg status */ /* * A FILE handle containing an open filehandle for the sequence file @@ -289,7 +377,7 @@ struct msgs { /* * macros for message and sequence manipulation */ -#define msgstat(mp,n) (mp)->msgstats[(n) - mp->lowoff] +#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)) @@ -329,7 +417,7 @@ struct msgs { #define make_seq_private(mp,seqnum) \ bvector_set (mp->attrstats, FFATTRSLOT + seqnum) #define make_all_public(mp) \ - mp->attrstats = bvector_create(0); bvector_clear_all (mp->attrstats) + mp->attrstats = bvector_create(); bvector_clear_all (mp->attrstats) /* * macros for folder attributes @@ -342,8 +430,6 @@ struct msgs { #define other_files(mp) ((mp)->msgflags & OTHERS) #define set_other_files(mp) ((mp)->msgflags |= OTHERS) -#define NULLMP ((struct msgs *) 0) - /* * m_getfld() message parsing */ @@ -358,23 +444,31 @@ struct msgs { 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 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 NOUSE 0 /* draft being re-used */ #define TFOLDER 0 /* path() given a +folder */ @@ -421,13 +515,6 @@ extern char *defpath; /* pathname of user's profile */ 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 @@ -487,6 +574,4 @@ extern char *version_str; extern char *whatnowproc; extern char *whomproc; -extern void (*done) (int) NORETURN; - #include