Make the new printf-surprise test more precise.
[coreutils/ericb.git] / src / mktemp.c
blob6580f3c7552743ba584bc82f21c6f254f84836ee
1 /* Create a temporary file or directory, safely.
2 Copyright (C) 2007 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18 /* Jim Meyering */
20 #include <config.h>
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <getopt.h>
25 #include "system.h"
27 #include "error.h"
28 #include "filenamecat.h"
29 #include "long-options.h"
30 #include "quote.h"
31 #include "tempname.h"
33 /* The official name of this program (e.g., no `g' prefix). */
34 #define PROGRAM_NAME "mktemp"
36 #define AUTHORS "Jim Meyering"
38 static const char *default_template = "tmp.XXXXXXXXXX";
40 /* The name this program was run with. */
41 char *program_name;
43 /* For long options that have no equivalent short option, use a
44 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
45 enum
47 TMPDIR_OPTION = CHAR_MAX + 1
50 static struct option const longopts[] =
52 {"directory", no_argument, NULL, 'd'},
53 {"quiet", no_argument, NULL, 'q'},
54 {"dry-run", no_argument, NULL, 'u'},
55 {"tmpdir", optional_argument, NULL, TMPDIR_OPTION},
56 {GETOPT_HELP_OPTION_DECL},
57 {GETOPT_VERSION_OPTION_DECL},
58 {NULL, 0, NULL, 0}
61 void
62 usage (int status)
64 if (status != EXIT_SUCCESS)
65 fprintf (stderr, _("Try `%s --help' for more information.\n"),
66 program_name);
67 else
69 printf (_("Usage: %s [OPTION]... [TEMPLATE]\n"), program_name);
70 fputs (_("\
71 Create a temporary file or directory, safely, and print its name.\n\
72 If TEMPLATE is not specified, use tmp.XXXXXXXXXX.\n\
73 "), stdout);
74 fputs ("\n", stdout);
75 fputs (_("\
76 -d, --directory create a directory, not a file\n\
77 "), stdout);
78 fputs (_("\
79 -q, --quiet suppress diagnostics about file/dir-creation failure\n\
80 "), stdout);
81 fputs (_("\
82 -u, --dry-run do not create anything; merely print a name (unsafe)\n\
83 "), stdout);
84 fputs (_("\
85 --tmpdir[=DIR] interpret TEMPLATE relative to DIR. If DIR is\n\
86 not specified, use $TMPDIR if set, else /tmp.\n\
87 With this option, TEMPLATE must not be an absolute name.\n\
88 Unlike with -t, TEMPLATE may contain slashes, but even\n\
89 here, mktemp still creates only the final component.\n\
90 "), stdout);
91 fputs ("\n", stdout);
92 fputs (_("\
93 -p DIR use DIR as a prefix; implies -t [deprecated]\n\
94 "), stdout);
95 fputs (_("\
96 -t interpret TEMPLATE as a single file name component,\n\
97 relative to a directory: $TMPDIR, if set; else the\n\
98 directory specified via -p; else /tmp [deprecated]\n\
99 "), stdout);
100 fputs ("\n", stdout);
101 fputs (HELP_OPTION_DESCRIPTION, stdout);
102 fputs (VERSION_OPTION_DESCRIPTION, stdout);
103 emit_bug_reporting_address ();
106 exit (status);
109 static size_t
110 count_trailing_X_s (const char *s)
112 size_t len = strlen (s);
113 size_t n = 0;
114 for ( ; len && s[len-1] == 'X'; len--)
115 ++n;
116 return n;
119 static int
120 mkstemp_len (char *tmpl, size_t suff_len, bool dry_run)
122 return gen_tempname_len (tmpl, dry_run ? GT_NOCREATE : GT_FILE, suff_len);
125 static int
126 mkdtemp_len (char *tmpl, size_t suff_len, bool dry_run)
128 return gen_tempname_len (tmpl, dry_run ? GT_NOCREATE : GT_DIR, suff_len);
132 main (int argc, char **argv)
134 char *dest_dir;
135 char *dest_dir_arg = NULL;
136 bool suppress_stderr = false;
137 int c;
138 unsigned int n_args;
139 char *template;
140 bool use_dest_dir = false;
141 bool deprecated_t_option = false;
142 bool create_directory = false;
143 bool dry_run = false;
144 int status = EXIT_SUCCESS;
145 size_t x_count;
146 char *dest_name;
148 initialize_main (&argc, &argv);
149 program_name = argv[0];
150 setlocale (LC_ALL, "");
151 bindtextdomain (PACKAGE, LOCALEDIR);
152 textdomain (PACKAGE);
154 atexit (close_stdout);
156 while ((c = getopt_long (argc, argv, "dp:qtuV", longopts, NULL)) != -1)
158 switch (c)
160 case 'd':
161 create_directory = true;
162 break;
163 case 'p':
164 dest_dir_arg = optarg;
165 use_dest_dir = true;
166 break;
167 case 'q':
168 suppress_stderr = true;
169 break;
170 case 't':
171 use_dest_dir = true;
172 deprecated_t_option = true;
173 break;
174 case 'u':
175 dry_run = true;
176 break;
178 case TMPDIR_OPTION:
179 use_dest_dir = true;
180 dest_dir_arg = optarg;
181 break;
183 case_GETOPT_HELP_CHAR;
185 case 'V':
186 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
187 default:
188 usage (EXIT_FAILURE);
192 if (suppress_stderr)
194 /* From here on, redirect stderr to /dev/null.
195 A diagnostic from getopt_long, above, would still go to stderr. */
196 freopen ("/dev/null", "wb", stderr);
199 n_args = argc - optind;
200 if (2 <= n_args)
202 error (0, 0, _("too many templates"));
203 usage (EXIT_FAILURE);
206 if (n_args == 0)
208 use_dest_dir = true;
209 template = (char *) default_template;
211 else
213 template = argv[optind];
216 x_count = count_trailing_X_s (template);
217 if (x_count < 3)
218 error (EXIT_FAILURE, 0, _("too few X's in template %s"), quote (template));
220 if (use_dest_dir)
222 if (deprecated_t_option)
224 char *env = getenv ("TMPDIR");
225 dest_dir = (env && *env
226 ? env
227 : (dest_dir_arg ? dest_dir_arg : "/tmp"));
229 if (last_component (template) != template)
230 error (EXIT_FAILURE, 0,
231 _("invalid template, %s, contains directory separator"),
232 quote (template));
234 else
236 if (dest_dir_arg && *dest_dir_arg)
237 dest_dir = dest_dir_arg;
238 else
240 char *env = getenv ("TMPDIR");
241 dest_dir = (env && *env ? env : "/tmp");
243 if (IS_ABSOLUTE_FILE_NAME (template))
244 error (EXIT_FAILURE, 0,
245 _("invalid template, %s; with --tmpdir,"
246 " it may not be absolute"),
247 quote (template));
250 dest_name = file_name_concat (dest_dir, template, NULL);
252 else
254 dest_name = xstrdup (template);
257 if (create_directory)
259 int err = mkdtemp_len (dest_name, x_count, dry_run);
260 if (err != 0)
262 error (0, errno, _("failed to create directory via template %s"),
263 quote (dest_name));
264 status = EXIT_FAILURE;
267 else
269 int fd = mkstemp_len (dest_name, x_count, dry_run);
270 if (fd < 0 || (!dry_run && close (fd) != 0))
272 error (0, errno, _("failed to create file via template %s"),
273 quote (dest_name));
274 status = EXIT_FAILURE;
278 if (status == EXIT_SUCCESS)
279 puts (dest_name);
281 #ifdef lint
282 free (dest_name);
283 #endif
285 exit (status);
289 * Local variables:
290 * indent-tabs-mode: nil
291 * End: