#include <h/mhparse.h>
#include <h/utils.h>
+enum clobber_policy_t {
+ NMH_CLOBBER_ALWAYS = 0,
+ NMH_CLOBBER_AUTO,
+ NMH_CLOBBER_SUFFIX,
+ NMH_CLOBBER_ASK,
+ NMH_CLOBBER_NEVER
+};
-/*
- * The list of top-level contents to display
- */
-extern CT *cts;
+static enum clobber_policy_t clobber_policy (const char *);
-int autosw = 0;
+struct mhstoreinfo {
+ CT *cts; /* Top-level list of contents to store. */
+ char *cwd; /* cached current directory */
+ int autosw; /* -auto enabled */
+ int verbosw; /* -verbose enabled */
+ int files_not_clobbered; /* output flag indicating that store failed
+ in order to not clobber an existing file */
-/*
- * Cache of current directory. This must be
- * set before these routines are called.
- */
-char *cwd;
+ /* The following must never be touched by a caller: they are for
+ internal use by the mhstoresbr functions. */
+ char *dir; /* directory in which to store contents */
+ enum clobber_policy_t clobber_policy; /* -clobber selection */
+};
+
+typedef struct mhstoreinfo *mhstoreinfo_t;
+
+mhstoreinfo_t
+mhstoreinfo_create (CT *ct, char *pwd, const char *csw, int asw, int vsw) {
+ mhstoreinfo_t info = mh_xmalloc (sizeof *info);
+
+ info->cts = ct;
+ info->cwd = pwd;
+ info->autosw = asw;
+ info->verbosw = vsw;
+ info->files_not_clobbered = 0;
+ info->dir = NULL;
+ info->clobber_policy = clobber_policy (csw);
+
+ return info;
+}
+
+void
+mhstoreinfo_free (mhstoreinfo_t info) {
+ free (info->cwd);
+ free (info);
+}
+
+int
+mhstoreinfo_files_not_clobbered (const mhstoreinfo_t info) {
+ return info->files_not_clobbered;
+}
-/*
- * The directory in which to store the contents.
- */
-static char *dir;
/*
* Type for a compare function for qsort. This keeps
int type_ok (CT, int);
void flush_errors (void);
-/* mhshowsbr.c */
-int show_content_aux (CT, int, int, char *, char *);
-
/*
* prototypes
*/
-void store_all_messages (CT *);
+void store_all_messages (mhstoreinfo_t);
/*
* static prototypes
*/
-static void store_single_message (CT);
-static int store_switch (CT);
-static int store_generic (CT);
-static int store_application (CT);
-static int store_multi (CT);
-static int store_partial (CT);
-static int store_external (CT);
+static void store_single_message (CT, mhstoreinfo_t);
+static int store_switch (CT, mhstoreinfo_t);
+static int store_generic (CT, mhstoreinfo_t);
+static int store_application (CT, mhstoreinfo_t);
+static int store_multi (CT, mhstoreinfo_t);
+static int store_partial (CT, mhstoreinfo_t);
+static int store_external (CT, mhstoreinfo_t);
static int ct_compar (CT *, CT *);
-static int store_content (CT, CT);
+static int store_content (CT, CT, mhstoreinfo_t);
static int output_content_file (CT, int);
static int output_content_folder (char *, char *);
static int parse_format_string (CT, char *, char *, int, char *);
static void get_storeproc (CT);
static int copy_some_headers (FILE *, CT);
-static char *clobber_check (char *);
+static char *clobber_check (char *, mhstoreinfo_t);
/*
* Main entry point to store content
*/
void
-store_all_messages (CT *cts)
+store_all_messages (mhstoreinfo_t info)
{
CT ct, *ctp;
char *cp;
* store any contents.
*/
if ((cp = context_find (nmhstorage)) && *cp)
- dir = getcpy (cp);
+ info->dir = getcpy (cp);
else
- dir = getcpy (cwd);
+ info->dir = getcpy (info->cwd);
- for (ctp = cts; *ctp; ctp++) {
+ for (ctp = info->cts; *ctp; ctp++) {
ct = *ctp;
- store_single_message (ct);
+ store_single_message (ct, info);
}
flush_errors ();
*/
static void
-store_single_message (CT ct)
+store_single_message (CT ct, mhstoreinfo_t info)
{
if (type_ok (ct, 1)) {
umask (ct->c_umask);
- store_switch (ct);
+ store_switch (ct, info);
if (ct->c_fp) {
fclose (ct->c_fp);
ct->c_fp = NULL;
*/
static int
-store_switch (CT ct)
+store_switch (CT ct, mhstoreinfo_t info)
{
switch (ct->c_type) {
case CT_MULTIPART:
- return store_multi (ct);
- break;
+ return store_multi (ct, info);
case CT_MESSAGE:
switch (ct->c_subtype) {
case MESSAGE_PARTIAL:
- return store_partial (ct);
- break;
+ return store_partial (ct, info);
case MESSAGE_EXTERNAL:
- return store_external (ct);
+ return store_external (ct, info);
case MESSAGE_RFC822:
default:
- return store_generic (ct);
- break;
+ return store_generic (ct, info);
}
- break;
case CT_APPLICATION:
- return store_application (ct);
- break;
+ default:
+ return store_application (ct, info);
case CT_TEXT:
case CT_AUDIO:
case CT_IMAGE:
case CT_VIDEO:
- return store_generic (ct);
- break;
-
- default:
- adios (NULL, "unknown content type %d", ct->c_type);
- break;
+ return store_generic (ct, info);
}
return OK; /* NOT REACHED */
*/
static int
-store_generic (CT ct)
+store_generic (CT ct, mhstoreinfo_t info)
{
/*
* Check if the content specifies a filename.
* Don't bother with this for type "message"
* (only "message/rfc822" will use store_generic).
*/
- if (autosw && ct->c_type != CT_MESSAGE)
+ if (info->autosw && ct->c_type != CT_MESSAGE)
get_storeproc (ct);
- return store_content (ct, NULL);
+ return store_content (ct, NULL, info);
}
*/
static int
-store_application (CT ct)
+store_application (CT ct, mhstoreinfo_t info)
{
- char **ap, **ep;
CI ci = &ct->c_ctinfo;
/* Check if the content specifies a filename */
- if (autosw)
+ if (info->autosw)
get_storeproc (ct);
/*
*/
if (!ct->c_storeproc && ct->c_subtype == APPLICATION_OCTETS) {
int tarP = 0, zP = 0, gzP = 0;
+ char *cp;
- for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
- /* check for "type=tar" attribute */
- if (!strcasecmp (*ap, "type")) {
- if (strcasecmp (*ep, "tar"))
- break;
-
+ if ((cp = get_param(ci->ci_first_pm, "type", ' ', 1))) {
+ if (strcasecmp (cp, "tar") == 0)
tarP = 1;
- continue;
- }
+ }
- /* check for "conversions=compress" attribute */
- if ((!strcasecmp (*ap, "conversions") || !strcasecmp (*ap, "x-conversions"))
- && (!strcasecmp (*ep, "compress") || !strcasecmp (*ep, "x-compress"))) {
+ /* check for "conversions=compress" attribute */
+ if ((cp = get_param(ci->ci_first_pm, "conversions", ' ', 1)) ||
+ (cp = get_param(ci->ci_first_pm, "x-conversions", ' ', 1))) {
+ if (strcasecmp (cp, "compress") == 0 ||
+ strcasecmp (cp, "x-compress") == 0) {
zP = 1;
- continue;
}
- /* check for "conversions=gzip" attribute */
- if ((!strcasecmp (*ap, "conversions") || !strcasecmp (*ap, "x-conversions"))
- && (!strcasecmp (*ep, "gzip") || !strcasecmp (*ep, "x-gzip"))) {
+ if (strcasecmp (cp, "gzip") == 0 ||
+ strcasecmp (cp, "x-gzip") == 0) {
gzP = 1;
- continue;
}
}
: (gzP ? "%egzip -dc | tar tvf -"
: "%etar tvf -"), NULL);
if (!ct->c_storeproc) {
- if (autosw) {
+ if (info->autosw) {
ct->c_storeproc = add (zP ? "| uncompress | tar xvpf -"
: (gzP ? "| gzip -dc | tar xvpf -"
: "| tar xvpf -"), NULL);
}
}
- return store_content (ct, NULL);
+ return store_content (ct, NULL, info);
}
*/
static int
-store_multi (CT ct)
+store_multi (CT ct, mhstoreinfo_t info)
{
int result;
struct multipart *m = (struct multipart *) ct->c_ctparams;
NULL here. */
p->c_storage = ct->c_storage;
}
- result = store_switch (p);
+ result = store_switch (p, info);
p->c_storage = NULL;
if (result == OK && ct->c_subtype == MULTI_ALTERNATE)
*/
static int
-store_partial (CT ct)
+store_partial (CT ct, mhstoreinfo_t info)
{
int cur, hi, i;
CT p, *ctp, *ctq;
return OK;
hi = i = 0;
- for (ctp = cts; *ctp; ctp++) {
+ for (ctp = info->cts; *ctp; ctp++) {
p = *ctp;
if (p->c_type == CT_MESSAGE && p->c_subtype == ct->c_subtype) {
pm = (struct partial *) p->c_ctparams;
adios (NULL, "out of memory");
ctq = base;
- for (ctp = cts; *ctp; ctp++) {
+ for (ctp = info->cts; *ctp; ctp++) {
p = *ctp;
if (p->c_type == CT_MESSAGE && p->c_subtype == ct->c_subtype) {
pm = (struct partial *) p->c_ctparams;
ctq = base;
ct = *ctq++;
- if (store_content (ct, NULL) == NOTOK) {
+ if (store_content (ct, NULL, info) == NOTOK) {
losing:
free ((char *) base);
return NOTOK;
for (; *ctq; ctq++) {
p = *ctq;
- if (store_content (p, ct) == NOTOK)
+ if (store_content (p, ct, info) == NOTOK)
goto losing;
}
*/
static int
-store_external (CT ct)
+store_external (CT ct, mhstoreinfo_t info)
{
int result = NOTOK;
struct exbody *e = (struct exbody *) ct->c_ctparams;
* Check if the parameters for the external body
* specified a filename.
*/
- if (autosw) {
+ if (info->autosw) {
char *cp;
if ((cp = e->eb_name)
c_storage, so we know that p->c_storage is NULL here. */
p->c_storage = ct->c_storage;
}
- result = store_switch (p);
+ result = store_switch (p, info);
p->c_storage = NULL;
p->c_partno = NULL;
*/
static int
-store_content (CT ct, CT p)
+store_content (CT ct, CT p, mhstoreinfo_t info)
{
int appending = 0, msgnum = 0;
int is_partial = 0, first_partial = 0;
if ((cp = ct->c_storeproc) == NULL || *cp == '\0') {
CI ci = &ct->c_ctinfo;
- snprintf (buffer, sizeof(buffer), "%s-store-%s/%s",
- invo_name, ci->ci_type, ci->ci_subtype);
- if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
- snprintf (buffer, sizeof(buffer), "%s-store-%s", invo_name, ci->ci_type);
- if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
- cp = ct->c_type == CT_MESSAGE ? "+" : "%m%P.%s";
- }
+ cp = context_find_by_type ("store", ci->ci_type, ci->ci_subtype);
+ if (cp == NULL) {
+ cp = ct->c_type == CT_MESSAGE ? "+" : "%m%P.%s";
}
}
char *tmpfilenam, *folder;
/* Store content in temporary file for now */
- tmpfilenam = m_mktemp(invo_name, NULL, NULL);
+ if ((tmpfilenam = m_mktemp(invo_name, NULL, NULL)) == NULL) {
+ adios(NULL, "unable to create temporary file in %s",
+ get_temp_dir());
+ }
ct->c_storage = add (tmpfilenam, NULL);
/* Get the folder name */
* Parse and expand the storage formatting string
* in `cp' into `buffer'.
*/
- parse_format_string (ct, cp, buffer, sizeof(buffer), dir);
+ parse_format_string (ct, cp, buffer, sizeof(buffer), info->dir);
/*
* If formatting begins with '|' or '!', then pass
* content to standard input of a command and return.
*/
if (buffer[0] == '|' || buffer[0] == '!')
- return show_content_aux (ct, 1, 0, buffer + 1, dir);
+ return show_content_aux (ct, 0, buffer + 1, info->dir, NULL);
/* record the filename */
- if ((ct->c_storage = clobber_check (add (buffer, NULL))) == NULL) {
+ if ((ct->c_storage = clobber_check (add (buffer, NULL), info)) ==
+ NULL) {
+ return NOTOK;
+ }
+ } else {
+ /* The output filename was explicitly specified, so use it. */
+ if ((ct->c_storage = clobber_check (add (ct->c_storage, NULL), info)) ==
+ NULL) {
return NOTOK;
}
- } /* else output filename was explicitly specified, so use it */
+ }
got_filename:
/* flush the output stream */
*/
if (ct->c_folder && (!is_partial || last_partial)) {
msgnum = output_content_folder (ct->c_folder, ct->c_storage);
- unlink (ct->c_storage);
+ (void) m_unlink (ct->c_storage);
if (msgnum == NOTOK)
return NOTOK;
}
- /*
- * Now print out the name/number of the message
- * that we are storing.
- */
- if (is_partial) {
- if (first_partial)
- fprintf (stderr, "reassembling partials ");
- if (last_partial)
- fprintf (stderr, "%s", ct->c_file);
- else
- fprintf (stderr, "%s,", ct->c_file);
- } else {
- fprintf (stderr, "storing message %s", ct->c_file);
- if (ct->c_partno)
- fprintf (stderr, " part %s", ct->c_partno);
- }
-
- /*
- * Unless we are in the "middle" of group of message/partials,
- * we now print the name of the file, folder, and/or message
- * to which we are storing the content.
- */
- if (!is_partial || last_partial) {
- if (ct->c_folder) {
- fprintf (stderr, " to folder %s as message %d\n", ct->c_folder, msgnum);
- } else if (!strcmp(ct->c_storage, "-")) {
- fprintf (stderr, " to stdout\n");
- } else {
- int cwdlen;
+ if (info->verbosw) {
+ /*
+ * Now print out the name/number of the message
+ * that we are storing.
+ */
+ if (is_partial) {
+ if (first_partial)
+ fprintf (stderr, "reassembling partials ");
+ if (last_partial)
+ fprintf (stderr, "%s", ct->c_file);
+ else
+ fprintf (stderr, "%s,", ct->c_file);
+ } else {
+ fprintf (stderr, "storing message %s", ct->c_file);
+ if (ct->c_partno)
+ fprintf (stderr, " part %s", ct->c_partno);
+ }
- cwdlen = strlen (cwd);
- fprintf (stderr, " as file %s\n",
- strncmp (ct->c_storage, cwd, cwdlen)
- || ct->c_storage[cwdlen] != '/'
- ? ct->c_storage : ct->c_storage + cwdlen + 1);
- }
+ /*
+ * Unless we are in the "middle" of group of message/partials,
+ * we now print the name of the file, folder, and/or message
+ * to which we are storing the content.
+ */
+ if (!is_partial || last_partial) {
+ if (ct->c_folder) {
+ fprintf (stderr, " to folder %s as message %d\n", ct->c_folder,
+ msgnum);
+ } else if (!strcmp(ct->c_storage, "-")) {
+ fprintf (stderr, " to stdout\n");
+ } else {
+ int cwdlen = strlen (info->cwd);
+
+ fprintf (stderr, " as file %s\n",
+ strncmp (ct->c_storage, info->cwd, cwdlen)
+ || ct->c_storage[cwdlen] != '/'
+ ? ct->c_storage : ct->c_storage + cwdlen + 1);
+ }
+ }
}
return OK;
break;
default:
- fwrite (buffer, sizeof(*buffer), cc, fp);
+ if ((int) fwrite (buffer, sizeof(*buffer), cc, fp) < cc) {
+ advise ("output_content_file", "fwrite");
+ }
continue;
}
break;
buflen--;
continue;
} else {
- char **ap, **ep;
+ PM pm;
char *s = "";
- for (ap = ci->ci_attrs, ep = ci->ci_values;
- *ap; ap++, ep++) {
- snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
+ for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) {
+ snprintf (bp, buflen, "%s%s=\"%s\"", s,
+ pm->pm_name, get_param_value(pm, '?'));
len = strlen (bp);
bp += len;
buflen -= len;
static void
get_storeproc (CT ct)
{
- char **ap, **ep, *cp;
+ char *cp;
CI ci;
/*
* use that (RFC-2183).
*/
if (ct->c_dispo) {
- char *cp = strchr (ct->c_dispo, ';');
- CI ci = calloc (1, sizeof *ci);
- int status;
- int found_filename = 0;
-
- if (cp && parse_header_attrs (ct->c_file, strlen (invo_name) + 2, &cp,
- ci, &status) == OK) {
- for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
- if (! strcasecmp (*ap, "filename")
- && *(cp = *ep) != '/'
- && *cp != '.'
- && *cp != '|'
- && *cp != '!'
- && !strchr (cp, '%')) {
- ct->c_storeproc = add (cp, NULL);
- found_filename = 1;
- }
- free (*ap);
- }
+ if ((cp = get_param(ct->c_dispo_first, "filename", '_', 0))
+ && *cp != '/'
+ && *cp != '.'
+ && *cp != '|'
+ && *cp != '!'
+ && !strchr (cp, '%')) {
+ ct->c_storeproc = add (cp, NULL);
+ free(cp);
+ return;
}
-
- free (ci);
- if (found_filename) return;
+ if (cp)
+ free(cp);
}
/*
* the storeproc.
*/
ci = &ct->c_ctinfo;
- for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
- if (! strcasecmp (*ap, "name")
- && *(cp = *ep) != '/'
- && *cp != '.'
- && *cp != '|'
- && *cp != '!'
- && !strchr (cp, '%')) {
+ if ((cp = get_param(ci->ci_first_pm, "name", '_', 0))
+ && *cp != '/'
+ && *cp != '.'
+ && *cp != '|'
+ && *cp != '!'
+ && !strchr (cp, '%')) {
ct->c_storeproc = add (cp, NULL);
- return;
- }
+
}
+ if (cp)
+ free(cp);
}
/******************************************************************************/
/* -clobber support */
-enum clobber_policy_t {
- NMH_CLOBBER_ALWAYS,
- NMH_CLOBBER_AUTO,
- NMH_CLOBBER_SUFFIX,
- NMH_CLOBBER_ASK,
- NMH_CLOBBER_NEVER
-};
-
-static enum clobber_policy_t clobber_policy = NMH_CLOBBER_ALWAYS;
-
-int files_not_clobbered = 0;
-
-int
-save_clobber_policy (const char *value) {
- if (! strcasecmp (value, "always")) {
- clobber_policy = NMH_CLOBBER_ALWAYS;
+static
+enum clobber_policy_t
+clobber_policy (const char *value) {
+ if (value == NULL || ! strcasecmp (value, "always")) {
} else if (! strcasecmp (value, "auto")) {
- clobber_policy = NMH_CLOBBER_AUTO;
+ return NMH_CLOBBER_AUTO;
} else if (! strcasecmp (value, "suffix")) {
- clobber_policy = NMH_CLOBBER_SUFFIX;
+ return NMH_CLOBBER_SUFFIX;
} else if (! strcasecmp (value, "ask")) {
- clobber_policy = NMH_CLOBBER_ASK;
+ return NMH_CLOBBER_ASK;
} else if (! strcasecmp (value, "never")) {
- clobber_policy = NMH_CLOBBER_NEVER;
+ return NMH_CLOBBER_NEVER;
} else {
- return 1;
+ adios (NULL, "invalid argument, %s, to clobber", value);
}
- return 0;
+ return NMH_CLOBBER_ALWAYS;
}
/* Should never get here. */
advise (NULL, "will not overwrite %s, invalid clobber policy", buffer);
free (buffer);
- ++files_not_clobbered;
return NULL;
}
advise (NULL, "will not overwrite %s, too many versions", buffer);
free (buffer);
buffer = NULL;
- ++files_not_clobbered;
}
return buffer;
static char *
-clobber_check (char *original_file) {
+clobber_check (char *original_file, mhstoreinfo_t info) {
/* clobber policy return value
* -------------- ------------
* -always original_file
char *cwd = NULL;
int check_again;
- if (clobber_policy == NMH_CLOBBER_ASK) {
+ if (info->clobber_policy == NMH_CLOBBER_ASK) {
/* Save cwd for possible use in loop below. */
char *slash;
file = original_file;
check_again = 0;
- switch (clobber_policy) {
+ switch (info->clobber_policy) {
case NMH_CLOBBER_ALWAYS:
break;
case NMH_CLOBBER_SUFFIX:
case NMH_CLOBBER_AUTO:
if (stat (file, &st) == OK) {
- file = next_version (original_file, clobber_policy);
+ if ((file = next_version (original_file, info->clobber_policy)) ==
+ NULL) {
+ ++info->files_not_clobbered;
+ }
}
break;
if (stat (file, &st) == OK) {
enum answers { NMH_YES, NMH_NO, NMH_RENAME };
static struct swit answer[4] = {
- { "yes", 0, NMH_YES }, { "no", 0, NMH_NO }, { "rename", 0, NMH_RENAME }, { NULL, 0, 0 } };
+ { "yes", 0, NMH_YES },
+ { "no", 0, NMH_NO },
+ { "rename", 0, NMH_RENAME },
+ { NULL, 0, 0 } };
char **ans;
if (isatty (fileno (stdin))) {
case NMH_NO:
free (file);
file = NULL;
- ++files_not_clobbered;
+ ++info->files_not_clobbered;
break;
case NMH_RENAME: {
char buf[PATH_MAX];
if (fgets (buf, sizeof buf, stdin) == NULL ||
buf[0] == '\0') {
file = NULL;
- ++files_not_clobbered;
+ ++info->files_not_clobbered;
} else {
char *newline = strchr (buf, '\n');
if (newline) {
advise (NULL, "will not overwrite %s with -clobber never", file);
free (file);
file = NULL;
- ++files_not_clobbered;
+ ++info->files_not_clobbered;
}
break;
}