/* Create a temporary file or directory, safely. This is the mktemp utility
Copyright (C) 2007-2018 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, see <https://www.gnu.org/licenses/>. */ The GNUv3 license
/* Written by Jim Meyering and Eric Blake. */
#include <config.h> Provides system specific information
#include <sys/types.h> Provides system data types
#include <getopt.h> ...!includes auto-comment...
#include "system.h" ...!includes auto-comment...
#include "close-stream.h" ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "filenamecat.h" ...!includes auto-comment...
#include "quote.h" ...!includes auto-comment...
#include "tempname.h" ...!includes auto-comment...
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "mktemp" Line 33
#define AUTHORS \ Line 35
proper_name ("Jim Meyering"), \ Line 36
proper_name ("Eric Blake") Line 37
static const char *default_template = "tmp.XXXXXXXXXX"; Line 39
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum Line 43
{
SUFFIX_OPTION = CHAR_MAX + 1, Line 45
}; Block 1
static struct option const longopts[] = Line 48
{
{"directory", no_argument, NULL, 'd'}, Line 50
{"quiet", no_argument, NULL, 'q'}, Line 51
{"dry-run", no_argument, NULL, 'u'}, Line 52
{"suffix", required_argument, NULL, SUFFIX_OPTION}, Line 53
{"tmpdir", optional_argument, NULL, 'p'}, Line 54
{GETOPT_HELP_OPTION_DECL}, Line 55
{GETOPT_VERSION_OPTION_DECL}, Line 56
{NULL, 0, NULL, 0} Line 57
}; Block 2
void Line 60
usage (int status) Line 61
{
if (status != EXIT_SUCCESS) Line 63
emit_try_help (); ...!common auto-comment...
else Line 65
{
printf (_("Usage: %s [OPTION]... [TEMPLATE]\n"), program_name); Line 67
fputs (_("\ Line 68
Create a temporary file or directory, safely, and print its name.\n\ Line 69
TEMPLATE must contain at least 3 consecutive 'X's in last component.\n\ Line 70
If TEMPLATE is not specified, use tmp.XXXXXXXXXX, and --tmpdir is implied.\n\ Line 71
"), stdout); Line 72
fputs (_("\ Line 73
Files are created u+rw, and directories u+rwx, minus umask restrictions.\n\ Line 74
"), stdout); Line 75
fputs ("\n", stdout); Line 76
fputs (_("\ Line 77
-d, --directory create a directory, not a file\n\ Line 78
-u, --dry-run do not create anything; merely print a name (unsafe)\n\ Line 79
-q, --quiet suppress diagnostics about file/dir-creation failure\n\ Line 80
"), stdout); Line 81
fputs (_("\ Line 82
--suffix=SUFF append SUFF to TEMPLATE; SUFF must not contain a slash.\n\Line 83
This option is implied if TEMPLATE does not end in X\n\ Line 84
"), stdout); Line 85
fputs (_("\ Line 86
-p DIR, --tmpdir[=DIR] interpret TEMPLATE relative to DIR; if DIR is not\n\ Line 87
specified, use $TMPDIR if set, else /tmp. With\n\ Line 88
this option, TEMPLATE must not be an absolute name;\n\ Line 89
unlike with -t, TEMPLATE may contain slashes, but\n\ Line 90
mktemp creates only the final component\n\ Line 91
"), stdout); Line 92
fputs (_("\ Line 93
-t interpret TEMPLATE as a single file name component,\n\ Line 94
relative to a directory: $TMPDIR, if set; else the\n\ Line 95
directory specified via -p; else /tmp [deprecated]\n\ Line 96
"), stdout); Line 97
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 98
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 99
emit_ancillary_info (PROGRAM_NAME); Line 100
}
exit (status); Line 103
} Block 3
static size_t Line 106
count_consecutive_X_s (const char *s, size_t len) Line 107
{
size_t n = 0; Line 109
for ( ; len && s[len-1] == 'X'; len--) Line 110
++n; Line 111
return n; Line 112
} Block 4
static int Line 115
mkstemp_len (char *tmpl, size_t suff_len, size_t x_len, bool dry_run) Line 116
{
return gen_tempname_len (tmpl, suff_len, 0, dry_run ? GT_NOCREATE : GT_FILE, Line 118
x_len); Line 119
} Block 5
static int Line 122
mkdtemp_len (char *tmpl, size_t suff_len, size_t x_len, bool dry_run) Line 123
{
return gen_tempname_len (tmpl, suff_len, 0, dry_run ? GT_NOCREATE : GT_DIR, Line 125
x_len); Line 126
} Block 6
/* True if we have already closed standard output. */
static bool stdout_closed; Line 130
/* Avoid closing stdout twice. Since we conditionally call
close_stream (stdout) in order to decide whether to clean up a
temporary file, the exit hook needs to know whether to do all of
close_stdout or just the stderr half. */
static void Line 136
maybe_close_stdout (void) Line 137
{
if (!stdout_closed) Line 139
close_stdout (); Line 140
else if (close_stream (stderr) != 0) Line 141
_exit (EXIT_FAILURE); Line 142
} Block 7
int
main (int argc, char **argv) Line 146
{
char const *dest_dir; Line 148
char const *dest_dir_arg = NULL; Line 149
bool suppress_file_err = false; Line 150
int c; Line 151
unsigned int n_args; Line 152
char *template; Line 153
char *suffix = NULL; Line 154
bool use_dest_dir = false; Line 155
bool deprecated_t_option = false; Line 156
bool create_directory = false; Line 157
bool dry_run = false; Line 158
int status = EXIT_SUCCESS; Line 159
size_t x_count; Line 160
size_t suffix_len; Line 161
char *dest_name; Line 162
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
atexit (maybe_close_stdout); Close stdout on exit (see gnulib)
while ((c = getopt_long (argc, argv, "dp:qtuV", longopts, NULL)) != -1) Line 172
{
switch (c) Line 174
{
case 'd': Line 176
create_directory = true; Line 177
break; Line 178
case 'p': Line 179
dest_dir_arg = optarg; Line 180
use_dest_dir = true; Line 181
break; Line 182
case 'q': Line 183
suppress_file_err = true; Line 184
break; Line 185
case 't': Line 186
use_dest_dir = true; Line 187
deprecated_t_option = true; Line 188
break; Line 189
case 'u': Line 190
dry_run = true; Line 191
break; Line 192
case SUFFIX_OPTION: Line 194
suffix = optarg; Line 195
break; Line 196
case_GETOPT_HELP_CHAR; Line 198
case 'V': /* Undocumented alias, for compatibility with the original Line 200
mktemp program. */
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 202
default: Line 203
usage (EXIT_FAILURE); Line 204
}
}
n_args = argc - optind; Line 208
if (2 <= n_args) Line 209
{
error (0, 0, _("too many templates")); Line 211
usage (EXIT_FAILURE); Line 212
}
if (n_args == 0) Line 215
{
use_dest_dir = true; Line 217
template = (char *) default_template; Line 218
}
else Line 220
{
template = argv[optind]; Line 222
}
if (suffix) Line 225
{
size_t len = strlen (template); Line 227
if (!len || template[len - 1] != 'X') Line 228
{
die (EXIT_FAILURE, 0, Line 230
_("with --suffix, template %s must end in X"), Line 231
quote (template)); Line 232
}
suffix_len = strlen (suffix); Line 234
dest_name = xcharalloc (len + suffix_len + 1); Line 235
memcpy (dest_name, template, len); Line 236
memcpy (dest_name + len, suffix, suffix_len + 1); Line 237
template = dest_name; Line 238
suffix = dest_name + len; Line 239
}
else Line 241
{
template = xstrdup (template); Line 243
suffix = strrchr (template, 'X'); Line 244
if (!suffix) Line 245
suffix = strchr (template, '\0'); Line 246
else Line 247
suffix++; Line 248
suffix_len = strlen (suffix); Line 249
}
/* At this point, template is malloc'd, and suffix points into template. */
if (suffix_len && last_component (suffix) != suffix) Line 253
{
die (EXIT_FAILURE, 0, Line 255
_("invalid suffix %s, contains directory separator"), Line 256
quote (suffix)); Line 257
}
x_count = count_consecutive_X_s (template, suffix - template); Line 259
if (x_count < 3) Line 260
die (EXIT_FAILURE, 0, _("too few X's in template %s"), quote (template)); Line 261
if (use_dest_dir) Line 263
{
if (deprecated_t_option) Line 265
{
char *env = getenv ("TMPDIR"); Line 267
if (env && *env) Line 268
dest_dir = env; Line 269
else if (dest_dir_arg && *dest_dir_arg) Line 270
dest_dir = dest_dir_arg; Line 271
else Line 272
dest_dir = "/tmp"; Line 273
if (last_component (template) != template) Line 275
die (EXIT_FAILURE, 0, Line 276
_("invalid template, %s, contains directory separator"), Line 277
quote (template)); Line 278
}
else Line 280
{
if (dest_dir_arg && *dest_dir_arg) Line 282
dest_dir = dest_dir_arg; Line 283
else Line 284
{
char *env = getenv ("TMPDIR"); Line 286
dest_dir = (env && *env ? env : "/tmp"); Line 287
}
if (IS_ABSOLUTE_FILE_NAME (template)) Line 289
die (EXIT_FAILURE, 0, Line 290
_("invalid template, %s; with --tmpdir," Line 291
" it may not be absolute"), Line 292
quote (template)); Line 293
}
dest_name = file_name_concat (dest_dir, template, NULL); Line 296
free (template); Line 297
template = dest_name; Line 298
/* Note that suffix is now invalid. */
}
/* Make a copy to be used in case of diagnostic, since failing
mkstemp may leave the buffer in an undefined state. */
dest_name = xstrdup (template); Line 304
if (create_directory) Line 306
{
int err = mkdtemp_len (dest_name, suffix_len, x_count, dry_run); Line 308
if (err != 0) Line 309
{
if (!suppress_file_err) Line 311
error (0, errno, _("failed to create directory via template %s"), Line 312
quote (template)); Line 313
status = EXIT_FAILURE; Line 314
}
}
else Line 317
{
int fd = mkstemp_len (dest_name, suffix_len, x_count, dry_run); Line 319
if (fd < 0 || (!dry_run && close (fd) != 0)) Line 320...!syscalls auto-comment...
{
if (!suppress_file_err) Line 322
error (0, errno, _("failed to create file via template %s"), Line 323
quote (template)); Line 324
status = EXIT_FAILURE; Line 325
}
}
if (status == EXIT_SUCCESS) Line 329
{
puts (dest_name); Line 331
/* If we created a file, but then failed to output the file
name, we should clean up the mess before failing. */
if (!dry_run && ((stdout_closed = true), close_stream (stdout) != 0)) Line 334
{
int saved_errno = errno; Line 336
remove (dest_name); Line 337
if (!suppress_file_err) Line 338
error (0, saved_errno, _("write error")); Line 339
status = EXIT_FAILURE; Line 340
}
}
#ifdef lint Line 344
free (dest_name); Line 345
free (template); Line 346
#endif Line 347
return status; Line 349
} Block 8