From: David Levine Date: Mon, 3 Mar 2014 04:38:57 +0000 (-0600) Subject: Added mkstemp(1), wrapper around mkstemp(3)/mkstemps(3), to X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/060475000a2c096fbcab4a028832af774df1ee7b?ds=sidebyside;hp=debab7aa60964cf984342de9ab68e84308f50d2b Added mkstemp(1), wrapper around mkstemp(3)/mkstemps(3), to auxexec, for use by mhmail. --- diff --git a/.gitignore b/.gitignore index 743853bd..b8e0d468 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,7 @@ a.out.dSYM/ /uip/mhshow /uip/mhstore /uip/mhtest +/uip/mkstemp /uip/msgchk /uip/msh /uip/new diff --git a/Makefile.am b/Makefile.am index ef351909..587dfb84 100644 --- a/Makefile.am +++ b/Makefile.am @@ -76,8 +76,8 @@ TESTS = test/ali/test-ali test/anno/test-anno \ test/mhshow/test-charset test/mhshow/test-textcharset \ test/mhshow/test-cte-binary test/mhshow/test-qp \ test/mhshow/test-subpart test/mhshow/test-msg-buffer-boundaries \ - test/mhstore/test-mhstore test/new/test-basic \ - test/pick/test-pick test/pick/test-stderr \ + test/mhstore/test-mhstore test/mkstemp/test-mkstemp \ + test/new/test-basic test/pick/test-pick test/pick/test-stderr \ test/post/test-post-aliases test/post/test-post-basic \ test/post/test-post-multiple test/post/test-post-bcc \ test/post/test-post-dcc test/post/test-post-fcc \ @@ -150,9 +150,9 @@ bin_SCRIPTS = uip/mhmail etc/sendfiles ## ## This is all programs that get installed in the "lib" directory ## -auxexec_PROGRAMS = uip/ap uip/conflict uip/dp uip/fmtdump uip/mhl uip/post \ - uip/rcvdist uip/rcvpack uip/rcvstore uip/rcvtty uip/slocal \ - uip/viamail uip/mhtest +auxexec_PROGRAMS = uip/ap uip/conflict uip/dp uip/fmtdump uip/mhl uip/mkstemp \ + uip/post uip/rcvdist uip/rcvpack uip/rcvstore uip/rcvtty \ + uip/slocal uip/viamail uip/mhtest auxexec_SCRIPTS = uip/spost @@ -228,8 +228,8 @@ man_MANS = man/ali.1 man/anno.1 man/ap.8 man/burst.1 man/comp.1 \ man/mh-format.5 man/mh-mail.5 man/mh-profile.5 man/mh_profile.5 \ man/mh-sequence.5 man/mh-tailor.5 man/mhbuild.1 man/mhfixmsg.1 \ man/mhl.1 man/mhlist.1 man/mhmail.1 man/mhn.1 man/mhparam.1 \ - man/mhpath.1 man/mhshow.1 man/mhstore.1 man/msgchk.1 man/msh.1 \ - man/mts.conf.5 man/new.1 man/next.1 man/nmh.7 man/packf.1 \ + man/mhpath.1 man/mhshow.1 man/mhstore.1 man/mkstemp.1 man/msgchk.1 \ + man/msh.1 man/mts.conf.5 man/new.1 man/next.1 man/nmh.7 man/packf.1 \ man/pick.1 man/post.8 man/prev.1 man/prompter.1 man/rcvdist.1 \ man/rcvpack.1 man/rcvstore.1 man/rcvtty.1 man/refile.1 \ man/repl.1 man/rmf.1 man/rmm.1 man/scan.1 man/send.1 \ @@ -249,8 +249,8 @@ man_SRCS = man/ali.man man/anno.man man/ap.man man/burst.man man/comp.man \ man/mh_profile.man man/mh-sequence.man man/mh-tailor.man \ man/mhbuild.man man/mhfixmsg.man man/mhl.man man/mhlist.man \ man/mhmail.man man/mhn.man man/mhparam.man man/mhpath.man \ - man/mhshow.man man/mhstore.man man/msgchk.man man/msh.man \ - man/mts.conf.man man/new.man man/next.man man/nmh.man \ + man/mhshow.man man/mhstore.man man/mkstemp.man man/msgchk.man \ + man/msh.man man/mts.conf.man man/new.man man/next.man man/nmh.man \ man/packf.man man/pick.man man/post.man man/prev.man \ man/prompter.man man/rcvdist.man man/rcvpack.man \ man/rcvstore.man man/rcvtty.man man/refile.man man/repl.man \ @@ -432,6 +432,9 @@ uip_mhtest_SOURCES = uip/mhtest.c uip/mhparse.c uip/mhcachesbr.c \ uip/md5.c uip_mhtest_LDADD = $(LDADD) $(TERMLIB) $(POSTLINK) +uip_mkstemp_SOURCES = uip/mkstemp.c +uip_mkstemp_LDADD = $(LDADD) $(POSTLINK) + uip_post_SOURCES = uip/post.c uip/aliasbr.c uip_post_LDADD = mts/libmts.a $(LDADD) $(SASLLIB) $(TLSLIB) $(POSTLINK) diff --git a/man/mkstemp.man b/man/mkstemp.man new file mode 100644 index 00000000..1df5fd6d --- /dev/null +++ b/man/mkstemp.man @@ -0,0 +1,81 @@ +.TH MKSTEMP %manext1% "March 2, 2014" "%nmhversion%" +.\" +.\" %nmhwarning% +.\" +.SH NAME +mkstemp \- create a temporary file +.SH SYNOPSIS +.HP 5 +.na +.B %libdir%/mkstemp +.RB [ \-directory +.IR directory ] +.RB [ \-prefix +.IR prefix ] +.RB [ \-suffix +.IR suffix ] +.RB [ \-version ] +.RB [ \-help ] +.ad +.SH DESCRIPTION +.B mkstemp +creates a temporary file using +.IR mkstemp (3), +or if the +.B \-suffix +switch is available and used, +.IR mkstemps (3). +The file name identifies a unique, newly created file, and always +contains 6 characters that appear to be random. +.PP +The +.B \-directory +switch specifies a +.I directory +in which to create the file. +.PP +The +.B \-prefix +switch specifies an initial part, before the 6 characters, of the file +name. +.PP +The +.B \-suffix +switch is only supported on platforms that provide the +.IR mkstemps (3) +library function. It specifies a +.I suffix +to appear after the 6 characters; if it should begin with a special +character such as a period, that must be explicit in +.IR suffix . +.PP +Unlike arguments to most switches of other +.B nmh +commands, the arguments to +.B mkstemp +switches can begin with a dash. +.SH "EXIT STATUS" +If +.B mkstemp +successfully creates the temporary file, it prints the file name on +standard output and exits with status 0. On failure, it prints a +diagnostic message on standard error and exits with status -1. +.SH "PROFILE COMPONENTS" +None +.SH "SEE ALSO" +.IR mkstemp (3), +.IR mkstemps (3) +.SH DEFAULTS +.PD 0 +.TP 14 +\-directory +current directory +.TP +\-prefix +none +.TP +\-suffix +none +.PD +.SH CONTEXT +None diff --git a/test/mkstemp/test-mkstemp b/test/mkstemp/test-mkstemp new file mode 100755 index 00000000..38949369 --- /dev/null +++ b/test/mkstemp/test-mkstemp @@ -0,0 +1,81 @@ +#!/bin/sh +################################## +# +# Test creation of temporary file. +# +################################## + +set -e + +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" + +setup_test + +expected="$MH_TEST_DIR"/$$.expected +actual="$MH_TEST_DIR"/$$.actual + +mkstemp="${MH_LIB_DIR}/mkstemp" + +cd "$MHTMPDIR" + + +# check -help +cat >$expected <<'EOF' +Usage: mkstemp [switches] + switches are: + -directory + -prefix + -suffix + -version + -help +EOF + +$mkstemp -h >$actual 2>&1 +check $expected $actual + + +# check -version +# Verified same behavior as compiled mhmail. +case `$mkstemp -v` in + mkstemp\ --*) ;; + * ) printf '%s: mkstemp -v generated unexpected output\n' "$0" >&2 + failed=`expr ${failed:-0} + 1`;; +esac + + +# check with no switches +tmpfile=`$mkstemp` +if [ -f "$tmpfile" ]; then + rm "$tmpfile" +else + failed=1 +fi + + +# check -directory +tmpfile=`$mkstemp -directory "$MHTMPDIR"` +[ -f "$tmpfile" ] && rm "$tmpfile" || failed=`expr ${failed:-0} + 1` +check_tmpfile=`echo $tmpfile | grep "^$MHTMPDIR/......$" >/dev/null` +run_test `echo $check_tmpfile` '' + + +# check -prefix +tmpfile=`$mkstemp -prefix mkstemptest` +[ -f "$tmpfile" ] && rm "$tmpfile" || failed=`expr ${failed:-0} + 1` +check_tmpfile=`echo $tmpfile | grep '^mkstemptest......$' >/dev/null` +run_test `echo $check_tmpfile` '' + + +# check -suffix +tmpfile=`$mkstemp -suffix .txt` +[ -f "$tmpfile" ] && rm "$tmpfile" || failed=`expr ${failed:-0} + 1` +check_tmpfile=`echo $tmpfile | grep '^......\.txt$' >/dev/null` +run_test `echo $check_tmpfile` '' + + +exit $failed diff --git a/uip/mhmail b/uip/mhmail index ffc51298..b9dc0ddd 100755 --- a/uip/mhmail +++ b/uip/mhmail @@ -215,9 +215,11 @@ fi #### .orig file, so it will remove them, too. umask 077 tmpdir="${MHTMPDIR:-${TMPDIR:-`$nmhbindir/mhpath +`}}" -tmpfile="$tmpdir/mhmail$$" +tmpfilename=`cd "$tmpdir" && "${nmhlibdir}/mkstemp" -p mhmail` +[ $? -ne 0 ] && die "mhmail: failed to create temporary file in $tmpdir" +tmpfile="$tmpdir/$tmpfilename" backup_char=`"$nmhbindir"/mhparam sbackup` -tmpfilebackup="'$tmpdir'/${backup_char}mhmail$$*" +tmpfilebackup="$tmpdir/${backup_char}${tmpfilename}*" tmpfileresent= message_file= @@ -234,7 +236,8 @@ else #### When resending with send, tmpfile will just contain the #### Resent- header fields. "$tmpfileresent" will contain #### the message that is being resent. - tmpfileresent="$tmpdir/mhmail-resent$$" + tmpfileresent=`"${nmhlibdir}/mkstemp" -d "$tmpdir" -p mhmail-resent` + [ $? -ne 0 ] && die "mhmail: failed to create temporary file in $tmpdir" mhdist=1; export mhdist mhaltmsg=$tmpfileresent; export mhaltmsg message_file="$tmpfileresent" diff --git a/uip/mkstemp.c b/uip/mkstemp.c new file mode 100644 index 00000000..16ddf644 --- /dev/null +++ b/uip/mkstemp.c @@ -0,0 +1,240 @@ +/* + * mkstemp.c -- create a temporary file + * + * This code is Copyright (c) 2014 by the authors of nmh. + * See the COPYRIGHT file in the root directory of the nmh + * distribution for complete copyright information. + */ + +/* define NMH to 0 to remove dependencies on nmh. */ +#ifndef NMH +# define NMH 1 +#endif /* ! NMH */ + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ +#include +#include +#include +#include + +#if ! defined HAVE_MKSTEMPS +# define HAVE_MKSTEMPS 0 +#endif /* ! HAVE_MKSTEMPS */ + +char *build_template(const char *, const char *, const char *); +void process_args(int, char **, const char **, const char **, const char **); + +/* + * Use a template of the form: + * [directory/][prefix]XXXXXX[suffix] + * where some of those named components might be null. suffix is only + * supported if HAVE_MKSTEMPS. + */ + +int +main(int argc, char *argv[]) { + const char *directory = "", *prefix = "", *suffix = ""; + size_t suffix_len; + int fd; + char *template; + + process_args(argc, argv, &directory, &prefix, &suffix); + if ((template = build_template(directory, prefix, suffix)) == NULL) { + return -1; + } + + if ((suffix_len = strlen(suffix)) > 0) { +# if HAVE_MKSTEMPS + if ((fd = mkstemps(template, suffix_len)) < 0) { perror("mkstemps"); } +# endif /* HAVE_MKSTEMPS */ + } else { + if ((fd = mkstemp(template)) < 0) { perror("mkstemp"); } + } + + if (fd >= 0) { + (void) puts(template); + (void) close(fd); + } + + free(template); + + return fd >= 0 ? 0 : -1; +} + + +char * +build_template(const char *directory, const char *prefix, const char *suffix) { + const char pattern[] = "XXXXXX"; + size_t len, directory_len, pathsep_len, prefix_len, suffix_len; + char *template; + + directory_len = strlen(directory); + if (directory_len > 0) { + pathsep_len = 1; + if (directory[directory_len - 1] == '/') { + /* Will insert a '/' separately, so truncate the one provided + in the directory name. */ + --directory_len; + } + } else { + pathsep_len = 0; + } + prefix_len = strlen(prefix); + suffix_len = strlen(suffix); + /* sizeof pattern includes its final NULL, so don't add another. */ + len = directory_len + pathsep_len + prefix_len + sizeof pattern + + suffix_len; + + if ((template = malloc(len))) { + char *tp = template; + + (void) strncpy(tp, directory, directory_len); + tp += directory_len; + + if (pathsep_len == 1) { *tp++ = '/'; } + + (void) strncpy(tp, prefix, prefix_len); + tp += prefix_len; + + (void) strncpy(tp, pattern, sizeof pattern - 1); + tp += sizeof pattern - 1; + + (void) strncpy(tp, suffix, suffix_len); + tp += suffix_len; + + template[len-1] = '\0'; + + return template; + } else { + perror("malloc"); + return NULL; + } +} + + +#if NMH +#include + +#if HAVE_MKSTEMPS +# define MHFIXMSG_SWITCHES \ + X("directory", 0, DIRECTORYSW) \ + X("prefix", 0, PREFIXSW) \ + X("suffix", 0, SUFFIXSW) \ + X("version", 0, VERSIONSW) \ + X("help", 0, HELPSW) +#else /* ! HAVE_MKSTEMPS */ +# define MHFIXMSG_SWITCHES \ + X("directory", 0, DIRECTORYSW) \ + X("prefix", 0, PREFIXSW) \ + X("version", 0, VERSIONSW) \ + X("help", 0, HELPSW) +#endif /* ! HAVE_MKSTEMPS */ + +#define X(sw, minchars, id) id, +DEFINE_SWITCH_ENUM(MHFIXMSG); +#undef X + +#define X(sw, minchars, id) { sw, minchars, id }, +DEFINE_SWITCH_ARRAY(MHFIXMSG, switches); +#undef X + +void +process_args(int argc, char **argv, const char **directory, + const char **prefix, const char **suffix) { + char **argp, **arguments, *cp, buf[100]; +# if ! HAVE_MKSTEMPS + NMH_UNUSED(suffix); +# endif /* ! HAVE_MKSTEMPS */ + + if (nmh_init(argv[0], 1)) { done(NOTOK); } + arguments = getarguments (invo_name, argc, argv, 1); + argp = arguments; + + /* + * Parse arguments + */ + while ((cp = *argp++)) { + if (*cp == '-') { + switch (smatch(++cp, switches)) { + case AMBIGSW: + ambigsw(cp, switches); + done(NOTOK); + case UNKWNSW: + advise (NULL, "-%s unknown", cp); + (void) snprintf(buf, sizeof buf, "%s [switches]", invo_name); + print_help(buf, switches, 1); + done(NOTOK); + case HELPSW: + (void) snprintf(buf, sizeof buf, "%s [switches]", invo_name); + print_help(buf, switches, 1); + done(OK); + case VERSIONSW: + print_version(invo_name); + done(OK); + + case DIRECTORYSW: + /* Allow the directory to start with '-'. */ + if ((cp = *argp++) == NULL) { + adios(NULL, "missing argument to %s", argp[-2]); + } + *directory = cp; + continue; + + case PREFIXSW: + /* Allow the prefix to start with '-'. */ + if ((cp = *argp++) == NULL) { + adios(NULL, "missing argument to %s", argp[-2]); + } + *prefix = cp; + continue; + +# if HAVE_MKSTEMPS + case SUFFIXSW: + /* Allow the suffix to start with '-'. */ + if ((cp = *argp++) == NULL) { + adios(NULL, "missing argument to %s", argp[-2]); + } + *suffix = cp; + continue; +# endif /* HAVE_MKSTEMPS */ + } + } + } +} +#else /* ! NMH */ +void +process_args(int argc, char **argv, const char **directory, + const char **prefix, const char **suffix) { +# if HAVE_MKSTEMPS + const char usage[] = + "usage: %s [-h] [-d directory] [-p prefix] [-s suffix]\n"; + const char optstring[] = "d:hp:s:"; +# else /* ! HAVE_MKSTEMPS */ + const char usage[] = "usage: %s [-h] [-d directory] [-p prefix]\n"; + const char optstring[] = "d:hp:"; +# endif /* ! HAVE_MKSTEMPS */ + int opt; + + while ((opt = getopt(argc, argv, optstring)) != -1) { + switch (opt) { + case 'd': + *directory = optarg; + break; + case 'p': + *prefix = optarg; + break; + case 's': + *suffix = optarg; + break; + case 'h': + (void) printf(usage, argv[0]); + exit(0); + default: + (void) fprintf(stderr, usage, argv[0]); + exit(-1); + } + } +} +#endif /* ! NMH */