New program: mktemp.

* NEWS: Mention this.
* README: Add mktemp to the list.
* AUTHORS: Add this: mktemp: Jim Meyering
* src/mktemp.c: New file.
* src/Makefile.am (bin_PROGRAMS): Add mktemp.
(mktemp_LDADD): Add $(LIB_GETHRXTIME).
* man/mktemp.x: New file.
* man/Makefile.am (dist_man_MANS): Add mktemp.1.
(mktemp.1): New dependency.
* man/.cvsignore: Add mktemp.1.
* man/.gitignore: New file.
* src/.cvsignore, src/.gitignore: Add mktemp.
* tests/misc/mktemp: New file.
* tests/misc/Makefile.am (TESTS): Add mktemp.
* tests/Coreutils.pm (run_tests): Give the POST-test function
access to stdout and stderr contents, so it can verify that
the named-on-stdout file/dir does indeed exist and has proper
permissions, etc.
[po/ChangeLog]
* POTFILES.in: Add src/mktemp.c.
This commit is contained in:
Jim Meyering
2007-06-02 23:04:10 +02:00
parent 7eab7d027e
commit 99b039fead
16 changed files with 576 additions and 7 deletions

View File

@@ -46,6 +46,7 @@ md5sum: Ulrich Drepper, Scott Miller, David Madore
mkdir: David MacKenzie
mkfifo: David MacKenzie
mknod: David MacKenzie
mktemp: Jim Meyering
mv: Mike Parker, David MacKenzie, Jim Meyering
nice: David MacKenzie
nl: Scott Bartram, David MacKenzie

View File

@@ -1,5 +1,27 @@
2007-10-07 Jim Meyering <meyering@redhat.com>
New program: mktemp.
* NEWS: Mention this.
* README: Add mktemp to the list.
* AUTHORS: Add this: mktemp: Jim Meyering
* src/mktemp.c: New file.
* src/Makefile.am (bin_PROGRAMS): Add mktemp.
(mktemp_LDADD): Add $(LIB_GETHRXTIME).
* man/mktemp.x: New file.
* man/Makefile.am (dist_man_MANS): Add mktemp.1.
(mktemp.1): New dependency.
* man/.cvsignore: Add mktemp.1.
* man/.gitignore: New file.
* src/.cvsignore, src/.gitignore: Add mktemp.
* tests/misc/mktemp: New file.
* tests/misc/Makefile.am (TESTS): Add mktemp.
* tests/Coreutils.pm (run_tests): Give the POST-test function
access to stdout and stderr contents, so it can verify that
the named-on-stdout file/dir does indeed exist and has proper
permissions, etc.
[po/ChangeLog]
* POTFILES.in: Add src/mktemp.c.
Make tempname more random, via the randint module.
* gl/modules/tempname (Depends-on): Add randint and stdbool.
* gl/lib/tempname.c: Include randint.h and stdbool.h.

2
NEWS
View File

@@ -7,6 +7,8 @@ GNU coreutils NEWS -*- outline -*-
arch: equivalent to uname -m, not installed by default
But don't install this program on Solaris systems.
mktemp: create a temporary file or directory (or names)
** Programs no longer installed by default
hostname, su

4
README
View File

@@ -9,8 +9,8 @@ The programs that can be built with this package are:
[ arch base64 basename cat chcon chgrp chmod chown chroot cksum comm cp
csplit cut date dd df dir dircolors dirname du echo env expand expr
factor false fmt fold groups head hostid hostname id install join
kill link ln logname ls md5sum mkdir mkfifo mknod mv nice nl nohup
factor false fmt fold groups head hostid hostname id install join kill
link ln logname ls md5sum mkdir mkfifo mknod mktemp mv nice nl nohup
od paste pathchk pinky pr printenv printf ptx pwd readlink rm rmdir
runcon seq sha1sum sha224sum sha256sum sha384sum sha512sum shred shuf
sleep sort split stat stty su sum sync tac tail tee test touch tr true

View File

@@ -32,7 +32,7 @@ AC_CONFIG_AUX_DIR(build-aux)
AC_CONFIG_HEADERS([lib/config.h:lib/config.hin])
AB_INIT()
AM_INIT_AUTOMAKE([1.10 dist-bzip2])
AM_INIT_AUTOMAKE([1.10 dist-lzma])
AC_PROG_CC_STDC
AM_PROG_CC_C_O

98
man/.gitignore vendored Normal file
View File

@@ -0,0 +1,98 @@
Makefile
Makefile.in
base64.1
basename.1
cat.1
chgrp.1
chmod.1
chown.1
chroot.1
cksum.1
comm.1
cp.1
csplit.1
cut.1
date.1
dd.1
df.1
dir.1
dircolors.1
dirname.1
du.1
echo.1
env.1
expand.1
expr.1
factor.1
false.1
fmt.1
fold.1
groups.1
head.1
hostid.1
hostname.1
id.1
install.1
join.1
kill.1
link.1
ln.1
logname.1
ls.1
md5sum.1
mkdir.1
mkfifo.1
mknod.1
mktemp.1
mv.1
nice.1
nl.1
nohup.1
od.1
paste.1
pathchk.1
pinky.1
pr.1
printenv.1
printf.1
ptx.1
pwd.1
readlink.1
rm.1
rmdir.1
seq.1
sha1sum.1
sha224sum.1
sha256sum.1
sha384sum.1
sha512sum.1
shred.1
shuf.1
sleep.1
sort.1
split.1
stat.1
stty.1
su.1
sum.1
sync.1
tac.1
tail.1
tee.1
test.1
touch.1
tr.1
true.1
tsort.1
tty.1
uname.1
unexpand.1
uniq.1
unlink.1
uptime.1
users.1
vdir.1
wc.1
who.1
whoami.1
yes.1

View File

@@ -75,6 +75,7 @@ md5sum.1: $(common_dep) $(srcdir)/md5sum.x ../src/md5sum.c
mkdir.1: $(common_dep) $(srcdir)/mkdir.x ../src/mkdir.c
mkfifo.1: $(common_dep) $(srcdir)/mkfifo.x ../src/mkfifo.c
mknod.1: $(common_dep) $(srcdir)/mknod.x ../src/mknod.c
mktemp.1: $(common_dep) $(srcdir)/mktemp.x ../src/mktemp.c
mv.1: $(common_dep) $(srcdir)/mv.x ../src/mv.c
nice.1: $(common_dep) $(srcdir)/nice.x ../src/nice.c
nl.1: $(common_dep) $(srcdir)/nl.x ../src/nl.c

6
man/mktemp.x Normal file
View File

@@ -0,0 +1,6 @@
[NAME]
mktemp \- create a temporary file or directory
[DESCRIPTION]
.\" Add any additional description here
[SEE ALSO]
mkstemp(3), mkdtemp(3), mktemp(3)

View File

@@ -11,6 +11,10 @@
* POTFILES.in: Remove lib/human.c.
2007-05-19 Jim Meyering <jim@meyering.net>
* POTFILES.in: Add src/mktemp.c.
2007-02-02 Jim Meyering <jim@meyering.net>
* POTFILES.in: Add src/runcon.c.

View File

@@ -74,6 +74,7 @@ src/md5sum.c
src/mkdir.c
src/mkfifo.c
src/mknod.c
src/mktemp.c
src/mv.c
src/nice.c
src/nl.c

1
src/.gitignore vendored
View File

@@ -47,6 +47,7 @@ md5sum
mkdir
mkfifo
mknod
mktemp
mv
nice
nl

View File

@@ -31,7 +31,8 @@ EXTRA_PROGRAMS = \
$(build_if_possible__progs) \
[ chcon chgrp chown chmod cp dd dircolors du \
ginstall link ln dir vdir ls mkdir \
mkfifo mknod mv nohup readlink rm rmdir shred stat sync touch unlink \
mkfifo mknod mktemp \
mv nohup readlink rm rmdir shred stat sync touch unlink \
cat cksum comm csplit cut expand fmt fold head join md5sum \
nl od paste pr ptx sha1sum sha224sum sha256sum sha384sum sha512sum \
shuf sort split sum tac tail tr tsort unexpand uniq wc \
@@ -96,6 +97,7 @@ ls_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_SELINUX)
pr_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
shred_LDADD = $(LDADD) $(LIB_GETHRXTIME) $(LIB_FDATASYNC)
shuf_LDADD = $(LDADD) $(LIB_GETHRXTIME)
mktemp_LDADD = $(LDADD) $(LIB_GETHRXTIME)
vdir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_SELINUX)
## If necessary, add -lm to resolve use of pow in lib/strtod.c.

292
src/mktemp.c Normal file
View File

@@ -0,0 +1,292 @@
/* Create a temporary file or directory, safely.
Copyright (C) 2007 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
/* Jim Meyering */
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>
#include "system.h"
#include "error.h"
#include "filenamecat.h"
#include "long-options.h"
#include "quote.h"
#include "tempname.h"
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "mktemp"
#define AUTHORS "Jim Meyering"
static const char *default_template = "tmp.XXXXXXXXXX";
/* The name this program was run with. */
char *program_name;
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
TMPDIR_OPTION = CHAR_MAX + 1
};
static struct option const longopts[] =
{
{"directory", no_argument, NULL, 'd'},
{"quiet", no_argument, NULL, 'q'},
{"dry-run", no_argument, NULL, 'u'},
{"tmpdir", optional_argument, NULL, TMPDIR_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
};
void
usage (int status)
{
if (status != EXIT_SUCCESS)
fprintf (stderr, _("Try `%s --help' for more information.\n"),
program_name);
else
{
printf (_("Usage: %s [OPTION]... [TEMPLATE]\n"), program_name);
fputs (_("\
Create a temporary file or directory, safely, and print its name.\n\
If TEMPLATE is not specified, use tmp.XXXXXXXXXX.\n\
"), stdout);
fputs ("\n", stdout);
fputs (_("\
-d, --directory create a directory, not a file\n\
"), stdout);
fputs (_("\
-q, --quiet suppress diagnostics about file/dir-creation failure\n\
"), stdout);
fputs (_("\
-u, --dry-run do not create anything; merely print a name (unsafe)\n\
"), stdout);
fputs (_("\
--tmpdir[=DIR] interpret TEMPLATE relative to DIR. If DIR is\n\
not specified, use $TMPDIR if set, else /tmp.\n\
With this option, TEMPLATE must not be an absolute name.\n\
Unlike with -t, TEMPLATE may contain slashes, but even\n\
here, mktemp still creates only the final component.\n\
"), stdout);
fputs ("\n", stdout);
fputs (_("\
-p DIR use DIR as a prefix; implies -t [deprecated]\n\
"), stdout);
fputs (_("\
-t interpret TEMPLATE as a single file name component,\n\
relative to a directory: $TMPDIR, if set; else the\n\
directory specified via -p; else /tmp [deprecated]\n\
"), stdout);
fputs ("\n", stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
emit_bug_reporting_address ();
}
exit (status);
}
static size_t
count_trailing_X_s (const char *s)
{
size_t len = strlen (s);
size_t n = 0;
for ( ; len && s[len-1] == 'X'; len--)
++n;
return n;
}
static int
mkstemp_len (char *tmpl, size_t suff_len, bool dry_run)
{
return gen_tempname_len (tmpl, dry_run ? GT_NOCREATE : GT_FILE, suff_len);
}
static int
mkdtemp_len (char *tmpl, size_t suff_len, bool dry_run)
{
return gen_tempname_len (tmpl, dry_run ? GT_NOCREATE : GT_DIR, suff_len);
}
int
main (int argc, char **argv)
{
char *dest_dir;
char *dest_dir_arg = NULL;
bool suppress_stderr = false;
int c;
unsigned int n_args;
char *template;
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;
char *dest_name;
initialize_main (&argc, &argv);
program_name = argv[0];
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
while ((c = getopt_long (argc, argv, "dp:qtuV", longopts, NULL)) != -1)
{
switch (c)
{
case 'd':
create_directory = true;
break;
case 'p':
dest_dir_arg = optarg;
use_dest_dir = true;
break;
case 'q':
suppress_stderr = true;
break;
case 't':
use_dest_dir = true;
deprecated_t_option = true;
break;
case 'u':
dry_run = true;
break;
case TMPDIR_OPTION:
use_dest_dir = true;
dest_dir_arg = optarg;
break;
case_GETOPT_HELP_CHAR;
case 'V':
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
usage (EXIT_FAILURE);
}
}
if (suppress_stderr)
{
/* From here on, redirect stderr to /dev/null.
A diagnostic from getopt_long, above, would still go to stderr. */
freopen ("/dev/null", "wb", stderr);
}
n_args = argc - optind;
if (2 <= n_args)
{
error (0, 0, _("too many templates"));
usage (EXIT_FAILURE);
}
if (n_args == 0)
{
use_dest_dir = true;
template = (char *) default_template;
}
else
{
template = argv[optind];
}
x_count = count_trailing_X_s (template);
if (x_count < 3)
error (EXIT_FAILURE, 0, _("too few X's in template %s"), quote (template));
if (use_dest_dir)
{
if (deprecated_t_option)
{
char *env = getenv ("TMPDIR");
dest_dir = (env && *env
? env
: (dest_dir_arg ? dest_dir_arg : "/tmp"));
if (last_component (template) != template)
error (EXIT_FAILURE, 0,
_("invalid template, %s, contains directory separator"),
quote (template));
}
else
{
if (dest_dir_arg && *dest_dir_arg)
dest_dir = dest_dir_arg;
else
{
char *env = getenv ("TMPDIR");
dest_dir = (env && *env ? env : "/tmp");
}
if (IS_ABSOLUTE_FILE_NAME (template))
error (EXIT_FAILURE, 0,
_("invalid template, %s; with --tmpdir,"
" it may not be absolute"),
quote (template));
}
dest_name = file_name_concat (dest_dir, template, NULL);
}
else
{
dest_name = xstrdup (template);
}
if (create_directory)
{
int err = mkdtemp_len (dest_name, x_count, dry_run);
if (err != 0)
{
error (0, errno, _("failed to create directory via template %s"),
quote (dest_name));
status = EXIT_FAILURE;
}
}
else
{
int fd = mkstemp_len (dest_name, x_count, dry_run);
if (fd < 0 || (!dry_run && close (fd) != 0))
{
error (0, errno, _("failed to create file via template %s"),
quote (dest_name));
status = EXIT_FAILURE;
}
}
if (status == EXIT_SUCCESS)
puts (dest_name);
#ifdef lint
free (dest_name);
#endif
exit (status);
}
/*
* Local variables:
* indent-tabs-mode: nil
* End:
*/

View File

@@ -1,8 +1,7 @@
package Coreutils;
# This is a testing framework.
# Copyright (C) 1998, 2000, 2001, 2002, 2004, 2005, 2006 Free Software
# Foundation, Inc.
# Copyright (C) 1998, 2000-2002, 2004-2007 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -478,6 +477,23 @@ sub run_tests ($$$$$)
goto cleanup;
}
my %actual_data;
# Record actual stdout and stderr contents, if POST may need them.
if ($expect->{POST})
{
foreach my $eo (qw (OUT ERR))
{
my $out_file = $actual{$eo};
open IN, $out_file
or (warn "$program_name: cannot open $out_file for reading: $!\n"),
$fail = 1, next;
$actual_data{$eo} = <IN>;
close IN
or (warn "$program_name: failed to read $out_file: $!\n"),
$fail = 1;
}
}
foreach my $eo (qw (OUT ERR))
{
my $subst_expr = $expect->{RESULT_SUBST}->{$eo};
@@ -525,7 +541,8 @@ sub run_tests ($$$$$)
}
cleanup:
&{$expect->{POST}} if $expect->{POST};
$expect->{POST}
and &{$expect->{POST}} ($actual_data{OUT}, $actual_data{ERR});
}

View File

@@ -43,6 +43,7 @@ TESTS = \
date \
xstrtol \
od \
mktemp \
arch \
pr \
df-P \

121
tests/misc/mktemp Executable file
View File

@@ -0,0 +1,121 @@
#!/bin/sh
# Test "mktemp".
# Copyright (C) 2007 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
: ${PERL=perl}
: ${srcdir=.}
$PERL -e 1 > /dev/null 2>&1 || {
echo 1>&2 "$0: configure didn't find a usable version of Perl," \
"so can't run this test"
exit 77
}
me=`echo $0|sed 's,.*/,,'`
exec $PERL -w -I$srcdir/.. -MCoreutils -M"CuTmpdir qw($me)" -- - <<\EOF
require 5.003;
use strict;
(my $ME = $0) =~ s|.*/||;
sub check_tmp($$)
{
my ($file, $file_or_dir) = @_;
my (undef, undef, $mode, undef) = stat $file
or die "$ME: failed to stat $file: $!\n";
my $required_mode;
if ($file_or_dir eq 'D') {
-d $file or die "$ME: $file isn't a directory\n";
-x $file or die "$ME: $file isn't owner-searchable\n";
$required_mode = 0700;
} elsif ($file_or_dir eq 'F') {
-f $file or die "$ME: $file isn't a regular file\n";
$required_mode = 0600;
}
-r $file or die "$ME: $file isn't owner-readable\n";
-w $file or die "$ME: $file isn't owner-writable\n";
($mode & 0777) == $required_mode
or die "$ME: $file doesn't have required permissions\n";
$file_or_dir eq 'D'
and do { rmdir $file or die "$ME: failed to rmdir $file: $!\n" };
$file_or_dir eq 'F'
and do { unlink $file or die "$ME: failed to unlink $file: $!\n" };
}
# Turn off localisation of executable's ouput.
@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3;
my $prog = 'mktemp';
my @Tests =
(
# test-name, [option, option, ...] {OUT=>"expected-output"}
#
['too-many', 'a b',
{ERR=>"$prog: too many templates\n"
. "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} ],
['1f', 'bar.XXXX', {OUT => "bar.ZZZZ\n"},
{OUT_SUBST => 's,\.....$,.ZZZZ,'},
{POST => sub { my ($f) = @_; defined $f or return; chomp $f;
check_tmp $f, 'F'; }}
],
# Create a temporary directory.
['1d', '-d f.XXXX', {OUT => "f.ZZZZ\n"},
{OUT_SUBST => 's,\.....$,.ZZZZ,'},
{POST => sub { my ($f) = @_; defined $f or return; chomp $f;
check_tmp $f, 'D'; }}
],
# Use a template consisting solely of X's
['1d-allX', '-d XXXX', {OUT => "ZZZZ\n"},
{OUT_SUBST => 's,^....$,ZZZZ,'},
{POST => sub { my ($f) = @_; defined $f or return; chomp $f;
check_tmp $f, 'D'; }}
],
['invalid-tmpl', '-t a/bXXXX',
{ERR=>"$prog: invalid template, `a/bXXXX', "
. "contains directory separator\n"}, {EXIT => 1} ],
['invalid-t2', '--tmpdir=a /bXXXX',
{ERR=>"$prog: invalid template, `/bXXXX'; "
. "with --tmpdir, it may not be absolute\n"}, {EXIT => 1} ],
['tmp-w-slash', '--tmpdir=. a/bXXXX',
{PRE => sub {mkdir 'a',0755 or die "a: $!\n"}},
{OUT_SUBST => 's,b....$,bZZZZ,'},
{OUT => "./a/bZZZZ\n"},
{POST => sub { my ($f) = @_; defined $f or return; chomp $f;
check_tmp $f, 'F'; unlink $f; rmdir 'a' or die "rmdir a: $!\n" }}
],
);
my $save_temps = $ENV{DEBUG};
my $verbose = $ENV{VERBOSE};
my $fail = run_tests ($ME, $prog, \@Tests, $save_temps, $verbose);
exit $fail;
EOF