From: Eric Blake Date: Wed, 4 Nov 2009 18:13:39 +0000 (-0700) Subject: mktemp: add suffix handling X-Git-Tag: v8.1~34 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=31a9937081dc41a5f8d28d71709204e413e09972;p=platform%2Fupstream%2Fcoreutils.git mktemp: add suffix handling Now that mkstemps is supported, we might as well use it. * src/mktemp.c (TMPDIR_OPTION): New enum value. (longopts): Add new option. (usage): Document it. (count_trailing_X_s): Rename... (count_consecutive_X_s): ...to this, and add parameter. (mkstemp_len, mkdtemp_len): Add parameter. (main): Implement new option. (AUTHORS): Add myself. * AUTHORS (mktemp): Likewise. * tests/misc/mktemp: Test new option. * doc/coreutils.texi (mktemp invocation): Document it. * NEWS: Likewise. Fixes http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=548316. --- diff --git a/AUTHORS b/AUTHORS index 7095db0..59a0dfa 100644 --- a/AUTHORS +++ b/AUTHORS @@ -46,7 +46,7 @@ md5sum: Ulrich Drepper, Scott Miller, David Madore mkdir: David MacKenzie mkfifo: David MacKenzie mknod: David MacKenzie -mktemp: Jim Meyering +mktemp: Jim Meyering, Eric Blake mv: Mike Parker, David MacKenzie, Jim Meyering nice: David MacKenzie nl: Scott Bartram, David MacKenzie diff --git a/NEWS b/NEWS index 07eecab..5b75dbb 100644 --- a/NEWS +++ b/NEWS @@ -70,6 +70,10 @@ GNU coreutils NEWS -*- outline -*- md5sum --check now also accepts openssl-style checksums. So do sha1sum, sha224sum, sha384sum and sha512sum. + mktemp now accepts the option --suffix to provide a known suffix + after the substitution in the template. Additionally, uses such as + "mktemp fileXXXXXX.txt" are able to infer an appropriate --suffix. + touch now accepts the option --no-dereference (-h), as a means to change symlink timestamps on platforms with enough support. diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 6126cf8..227014c 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -12068,12 +12068,12 @@ mktemp [@var{option}]@dots{} [@var{template}] @end example Safely create a temporary file or directory based on @var{template}, -and print its name. If given, @var{template} must end in at least -three consecutive @samp{X}s. If omitted, the template +and print its name. If given, @var{template} must include at least +three consecutive @samp{X}s in the last component. If omitted, the template @samp{tmp.XXXXXXXXXX} is used, and option @option{--tmpdir} is -implied. The trailing @samp{X}s in the @var{template} will be replaced +implied. The final run of @samp{X}s in the @var{template} will be replaced by alpha-numeric characters; thus, on a case-sensitive file system, -and with a @var{template} ending in @var{n} instances of @samp{X}, +and with a @var{template} including a run of @var{n} instances of @samp{X}, there are @samp{62**@var{n}} potential file names. Older scripts used to create temporary files by simply joining the @@ -12108,6 +12108,15 @@ file.H47c @end example @item +Create a temporary file with a known suffix. +@example +$ mktemp --suffix=.txt file-XXXX +file-H08W.txt +$ mktemp file-XXXX-XXXX.txt +file-XXXX-eI9L.txt +@end example + +@item Create a secure fifo relative to the user's choice of @env{TMPDIR}, but falling back to the current directory rather than @file{/tmp}. Note that @command{mktemp} does not create fifos, but can create a @@ -12186,6 +12195,16 @@ specified, @var{template} must not be absolute. However, @var{template} can still contain slashes, although intermediate directories must already exist. +@item --suffix=@var{suffix} +@opindex --suffix +Append @var{suffix} to the @var{template}. @var{suffix} must not +contain slash. If @option{--suffix} is specified, @var{template} must +end in @samp{X}; if it is not specified, then an appropriate +@option{--suffix} is inferred by finding the last @samp{X} in +@var{template}. This option exists for use with the default +@var{template} and for the creation of a @var{suffix} that starts with +@samp{X}. + @item -t @opindex -t Treat @var{template} as a single file relative to the value of diff --git a/src/mktemp.c b/src/mktemp.c index 6cf40b0..ac35026 100644 --- a/src/mktemp.c +++ b/src/mktemp.c @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -/* Written by Jim Meyering. */ +/* Written by Jim Meyering and Eric Blake. */ #include #include @@ -32,7 +32,9 @@ /* The official name of this program (e.g., no `g' prefix). */ #define PROGRAM_NAME "mktemp" -#define AUTHORS proper_name ("Jim Meyering") +#define AUTHORS \ + proper_name ("Jim Meyering"), \ + proper_name ("Eric Blake") static const char *default_template = "tmp.XXXXXXXXXX"; @@ -40,7 +42,8 @@ static const char *default_template = "tmp.XXXXXXXXXX"; non-character as a pseudo short option, starting with CHAR_MAX + 1. */ enum { - TMPDIR_OPTION = CHAR_MAX + 1 + SUFFIX_OPTION = CHAR_MAX + 1, + TMPDIR_OPTION }; static struct option const longopts[] = @@ -48,6 +51,7 @@ static struct option const longopts[] = {"directory", no_argument, NULL, 'd'}, {"quiet", no_argument, NULL, 'q'}, {"dry-run", no_argument, NULL, 'u'}, + {"suffix", required_argument, NULL, SUFFIX_OPTION}, {"tmpdir", optional_argument, NULL, TMPDIR_OPTION}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, @@ -65,7 +69,7 @@ usage (int status) printf (_("Usage: %s [OPTION]... [TEMPLATE]\n"), program_name); fputs (_("\ Create a temporary file or directory, safely, and print its name.\n\ -TEMPLATE must end in at least 3 consecutive `X's.\n\ +TEMPLATE must contain at least 3 consecutive `X's in last component.\n\ If TEMPLATE is not specified, use tmp.XXXXXXXXXX, and --tmpdir is implied.\n\ "), stdout); fputs ("\n", stdout); @@ -75,6 +79,10 @@ If TEMPLATE is not specified, use tmp.XXXXXXXXXX, and --tmpdir is implied.\n\ -q, --quiet suppress diagnostics about file/dir-creation failure\n\ "), stdout); fputs (_("\ + --suffix=SUFF append SUFF to TEMPLATE. SUFF must not contain slash.\n\ + This option is implied if TEMPLATE does not end in X.\n\ +"), stdout); + fputs (_("\ --tmpdir[=DIR] interpret TEMPLATE relative to DIR. If DIR is not\n\ specified, use $TMPDIR if set, else /tmp. With\n\ this option, TEMPLATE must not be an absolute name.\n\ @@ -98,9 +106,8 @@ If TEMPLATE is not specified, use tmp.XXXXXXXXXX, and --tmpdir is implied.\n\ } static size_t -count_trailing_X_s (const char *s) +count_consecutive_X_s (const char *s, size_t len) { - size_t len = strlen (s); size_t n = 0; for ( ; len && s[len-1] == 'X'; len--) ++n; @@ -108,16 +115,16 @@ count_trailing_X_s (const char *s) } static int -mkstemp_len (char *tmpl, size_t suff_len, bool dry_run) +mkstemp_len (char *tmpl, size_t suff_len, size_t x_len, bool dry_run) { - return gen_tempname_len (tmpl, 0, 0, dry_run ? GT_NOCREATE : GT_FILE, + return gen_tempname_len (tmpl, suff_len, 0, dry_run ? GT_NOCREATE : GT_FILE, suff_len); } static int -mkdtemp_len (char *tmpl, size_t suff_len, bool dry_run) +mkdtemp_len (char *tmpl, size_t suff_len, size_t x_len, bool dry_run) { - return gen_tempname_len (tmpl, 0, 0, dry_run ? GT_NOCREATE : GT_DIR, + return gen_tempname_len (tmpl, suff_len, 0, dry_run ? GT_NOCREATE : GT_DIR, suff_len); } @@ -130,12 +137,14 @@ main (int argc, char **argv) int c; unsigned int n_args; char *template; + char *suffix = NULL; bool use_dest_dir = false; bool deprecated_t_option = false; bool create_directory = false; bool dry_run = false; int status = EXIT_SUCCESS; size_t x_count; + size_t suffix_len; char *dest_name; initialize_main (&argc, &argv); @@ -173,6 +182,10 @@ main (int argc, char **argv) dest_dir_arg = optarg; break; + case SUFFIX_OPTION: + suffix = optarg; + break; + case_GETOPT_HELP_CHAR; case 'V': /* Undocumented alias. FIXME: remove in 2011. */ @@ -208,7 +221,41 @@ main (int argc, char **argv) template = argv[optind]; } - x_count = count_trailing_X_s (template); + if (suffix) + { + size_t len = strlen (template); + if (!len || template[len - 1] != 'X') + { + error (EXIT_FAILURE, 0, + _("with --suffix, template %s must end in X"), + quote (template)); + } + suffix_len = strlen (suffix); + dest_name = xcharalloc (len + suffix_len + 1); + memcpy (dest_name, template, len); + memcpy (dest_name + len, suffix, suffix_len + 1); + template = dest_name; + suffix = dest_name + len; + } + else + { + template = xstrdup (template); + suffix = strrchr (template, 'X'); + if (!suffix) + suffix = strchr (template, '\0'); + else + suffix++; + suffix_len = strlen (suffix); + } + + /* At this point, template is malloc'd, and suffix points into template. */ + if (suffix_len && last_component (suffix) != suffix) + { + error (EXIT_FAILURE, 0, + _("invalid suffix %s, contains directory separator"), + quote (suffix)); + } + x_count = count_consecutive_X_s (template, suffix - template); if (x_count < 3) error (EXIT_FAILURE, 0, _("too few X's in template %s"), quote (template)); @@ -242,11 +289,10 @@ main (int argc, char **argv) quote (template)); } - template = file_name_concat (dest_dir, template, NULL); - } - else - { - template = xstrdup (template); + dest_name = file_name_concat (dest_dir, template, NULL); + free (template); + template = dest_name; + /* Note that suffix is now invalid. */ } /* Make a copy to be used in case of diagnostic, since failing @@ -255,7 +301,7 @@ main (int argc, char **argv) if (create_directory) { - int err = mkdtemp_len (dest_name, x_count, dry_run); + int err = mkdtemp_len (dest_name, suffix_len, x_count, dry_run); if (err != 0) { error (0, errno, _("failed to create directory via template %s"), @@ -265,7 +311,7 @@ main (int argc, char **argv) } else { - int fd = mkstemp_len (dest_name, x_count, dry_run); + int fd = mkstemp_len (dest_name, suffix_len, x_count, dry_run); if (fd < 0 || (!dry_run && close (fd) != 0)) { error (0, errno, _("failed to create file via template %s"), diff --git a/tests/misc/mktemp b/tests/misc/mktemp index e620311..7735f33 100755 --- a/tests/misc/mktemp +++ b/tests/misc/mktemp @@ -60,8 +60,8 @@ my @Tests = . "Try `$prog --help' for more information.\n"}, {EXIT => 1} ], ['too-many-q', '-q a b', {EXIT => 1} ], - ['too-few-x', 'foo.XX', - {ERR=>"$prog: too few X's in template `foo.XX'\n"}, {EXIT => 1} ], + ['too-few-x', 'foo.XX', {EXIT => 1}, + {ERR=>"$prog: too few X's in template `foo.XX'\n"}], ['too-few-xq', '-q foo.XX', {EXIT => 1} ], ['1f', 'bar.XXXX', {OUT => "bar.ZZZZ\n"}, @@ -110,8 +110,72 @@ my @Tests = . "with --tmpdir, it may not be absolute\n"}, {EXIT => 1} ], # Suffix after X. - ['invalid-t3', 'aXXXXb', - {ERR=>"$prog: too few X's in template `aXXXXb'\n"}, {EXIT => 1} ], + ['suffix1f', 'aXXXXb', {OUT=>"aZZZZb\n"}, + {OUT_SUBST=>'s,a....b,aZZZZb,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }}], + ['suffix1d', '-d aXXXXb', {OUT=>"aZZZZb\n"}, + {OUT_SUBST=>'s,a....b,aZZZZb,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'D'; }}], + ['suffix1u', '-u aXXXXb', {OUT=>"aZZZZb\n"}, + {OUT_SUBST=>'s,a....b,aZZZZb,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + -e $f and die "dry-run created file"; }}], + + ['suffix2f', 'aXXXXaaXXXXa', {OUT=>"aXXXXaaZZZZa\n"}, + {OUT_SUBST=>'s,a....a$,aZZZZa,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }}], + ['suffix2d', '-d --suffix= aXXXXaaXXXX', {OUT=>"aXXXXaaZZZZ\n"}, + {OUT_SUBST=>'s,a....$,aZZZZ,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'D'; }}], + + ['suffix3f', '--suffix=b aXXXX', {OUT=>"aZZZZb\n"}, + {OUT_SUBST=>'s,a....b,aZZZZb,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }}], + + ['suffix4f', '--suffix=X aXXXX', {OUT=>"aZZZZX\n"}, + {OUT_SUBST=>'s,^a....,aZZZZ,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }}], + + ['suffix5f', '--suffix /b aXXXX', {EXIT=>1}, + {ERR=>"$prog: invalid suffix `/b', contains directory separator\n"}], + + ['suffix6f', 'aXXXX/b', {EXIT=>1}, + {ERR=>"$prog: invalid suffix `/b', contains directory separator\n"}], + ['suffix6f-q', '-q aXXXX/b', {EXIT=>1}], + + ['suffix7f', '--suffix= aXXXXb', {EXIT=>1}, + {ERR=>"$prog: with --suffix, template `aXXXXb' must end in X\n"}], + ['suffix7f-q', '-q --suffix= aXXXXb', {EXIT=>1}], + ['suffix7d', '-d --suffix=aXXXXb ""', {EXIT=>1}, + {ERR=>"$prog: with --suffix, template `' must end in X\n"}], + + ['suffix8f', 'aXXXX --suffix=b', {OUT=>"aZZZZb\n"}, + {OUT_SUBST=>'s,^a....,aZZZZ,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }}], + + ['suffix9f', 'aXXXX --suffix=b', {EXIT=>1}, + {ENV=>"POSIXLY_CORRECT=1"}, + {ERR=>"$prog: too many templates\n" + . "Try `$prog --help' for more information.\n"}], + + ['suffix10f', 'aXXb', {EXIT => 1}, + {ERR=>"$prog: too few X's in template `aXXb'\n"}], + ['suffix10d', '-d --suffix=X aXX', {EXIT => 1}, + {ERR=>"$prog: too few X's in template `aXXX'\n"}], + + ['suffix11f', '--suffix=.txt', {OUT=>"./tmp.ZZZZZZZZZZ.txt\n"}, + {ENV=>"TMPDIR=."}, + {OUT_SUBST=>'s,\..{10}\.,.ZZZZZZZZZZ.,'}, + {POST => sub { my ($f) = @_; defined $f or return; chomp $f; + check_tmp $f, 'F'; }}], + # Test template with subdirectory ['tmp-w-slash', '--tmpdir=. a/bXXXX',