TODO: add an item for a chmod optimization
[platform/upstream/coreutils.git] / src / mktemp.c
1 /* Create a temporary file or directory, safely.
2    Copyright (C) 2007-2008 Free Software Foundation, Inc.
3
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 of the License, or
7    (at your option) any later version.
8
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.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* Jim Meyering */
18
19 #include <config.h>
20 #include <stdio.h>
21 #include <sys/types.h>
22 #include <getopt.h>
23
24 #include "system.h"
25
26 #include "error.h"
27 #include "filenamecat.h"
28 #include "quote.h"
29 #include "tempname.h"
30
31 /* The official name of this program (e.g., no `g' prefix).  */
32 #define PROGRAM_NAME "mktemp"
33
34 #define AUTHORS proper_name ("Jim Meyering")
35
36 static const char *default_template = "tmp.XXXXXXXXXX";
37
38 /* For long options that have no equivalent short option, use a
39    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
40 enum
41 {
42   TMPDIR_OPTION = CHAR_MAX + 1
43 };
44
45 static struct option const longopts[] =
46 {
47   {"directory", no_argument, NULL, 'd'},
48   {"quiet", no_argument, NULL, 'q'},
49   {"dry-run", no_argument, NULL, 'u'},
50   {"tmpdir", optional_argument, NULL, TMPDIR_OPTION},
51   {GETOPT_HELP_OPTION_DECL},
52   {GETOPT_VERSION_OPTION_DECL},
53   {NULL, 0, NULL, 0}
54 };
55
56 void
57 usage (int status)
58 {
59   if (status != EXIT_SUCCESS)
60     fprintf (stderr, _("Try `%s --help' for more information.\n"),
61              program_name);
62   else
63     {
64       printf (_("Usage: %s [OPTION]... [TEMPLATE]\n"), program_name);
65       fputs (_("\
66 Create a temporary file or directory, safely, and print its name.\n\
67 If TEMPLATE is not specified, use tmp.XXXXXXXXXX.\n\
68 "), stdout);
69       fputs ("\n", stdout);
70       fputs (_("\
71   -d, --directory  create a directory, not a file\n\
72 "), stdout);
73       fputs (_("\
74   -q, --quiet      suppress diagnostics about file/dir-creation failure\n\
75 "), stdout);
76       fputs (_("\
77   -u, --dry-run    do not create anything; merely print a name (unsafe)\n\
78 "), stdout);
79       fputs (_("\
80   --tmpdir[=DIR]   interpret TEMPLATE relative to DIR.  If DIR is\n\
81                      not specified, use $TMPDIR if set, else /tmp.\n\
82                      With this option, TEMPLATE must not be an absolute name.\n\
83                      Unlike with -t, TEMPLATE may contain slashes, but even\n\
84                      here, mktemp still creates only the final component.\n\
85 "), stdout);
86       fputs ("\n", stdout);
87       fputs (_("\
88   -p DIR           use DIR as a prefix; implies -t [deprecated]\n\
89 "), stdout);
90       fputs (_("\
91   -t               interpret TEMPLATE as a single file name component,\n\
92                      relative to a directory: $TMPDIR, if set; else the\n\
93                      directory specified via -p; else /tmp [deprecated]\n\
94 "), stdout);
95       fputs ("\n", stdout);
96       fputs (HELP_OPTION_DESCRIPTION, stdout);
97       fputs (VERSION_OPTION_DESCRIPTION, stdout);
98       emit_bug_reporting_address ();
99     }
100
101   exit (status);
102 }
103
104 static size_t
105 count_trailing_X_s (const char *s)
106 {
107   size_t len = strlen (s);
108   size_t n = 0;
109   for ( ; len && s[len-1] == 'X'; len--)
110     ++n;
111   return n;
112 }
113
114 static int
115 mkstemp_len (char *tmpl, size_t suff_len, bool dry_run)
116 {
117   return gen_tempname_len (tmpl, dry_run ? GT_NOCREATE : GT_FILE, suff_len);
118 }
119
120 static int
121 mkdtemp_len (char *tmpl, size_t suff_len, bool dry_run)
122 {
123   return gen_tempname_len (tmpl, dry_run ? GT_NOCREATE : GT_DIR, suff_len);
124 }
125
126 int
127 main (int argc, char **argv)
128 {
129   char *dest_dir;
130   char *dest_dir_arg = NULL;
131   bool suppress_stderr = false;
132   int c;
133   unsigned int n_args;
134   char *template;
135   bool use_dest_dir = false;
136   bool deprecated_t_option = false;
137   bool create_directory = false;
138   bool dry_run = false;
139   int status = EXIT_SUCCESS;
140   size_t x_count;
141   char *dest_name;
142
143   initialize_main (&argc, &argv);
144   set_program_name (argv[0]);
145   setlocale (LC_ALL, "");
146   bindtextdomain (PACKAGE, LOCALEDIR);
147   textdomain (PACKAGE);
148
149   atexit (close_stdout);
150
151   while ((c = getopt_long (argc, argv, "dp:qtuV", longopts, NULL)) != -1)
152     {
153       switch (c)
154         {
155         case 'd':
156           create_directory = true;
157           break;
158         case 'p':
159           dest_dir_arg = optarg;
160           use_dest_dir = true;
161           break;
162         case 'q':
163           suppress_stderr = true;
164           break;
165         case 't':
166           use_dest_dir = true;
167           deprecated_t_option = true;
168           break;
169         case 'u':
170           dry_run = true;
171           break;
172
173         case TMPDIR_OPTION:
174           use_dest_dir = true;
175           dest_dir_arg = optarg;
176           break;
177
178         case_GETOPT_HELP_CHAR;
179
180         case 'V':
181         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
182         default:
183           usage (EXIT_FAILURE);
184         }
185     }
186
187   if (suppress_stderr)
188     {
189       /* From here on, redirect stderr to /dev/null.
190          A diagnostic from getopt_long, above, would still go to stderr.  */
191       freopen ("/dev/null", "wb", stderr);
192     }
193
194   n_args = argc - optind;
195   if (2 <= n_args)
196     {
197       error (0, 0, _("too many templates"));
198       usage (EXIT_FAILURE);
199     }
200
201   if (n_args == 0)
202     {
203       use_dest_dir = true;
204       template = (char *) default_template;
205     }
206   else
207     {
208       template = argv[optind];
209     }
210
211   x_count = count_trailing_X_s (template);
212   if (x_count < 3)
213     error (EXIT_FAILURE, 0, _("too few X's in template %s"), quote (template));
214
215   if (use_dest_dir)
216     {
217       if (deprecated_t_option)
218         {
219           char *env = getenv ("TMPDIR");
220           dest_dir = (env && *env
221                       ? env
222                       : (dest_dir_arg ? dest_dir_arg : "/tmp"));
223
224           if (last_component (template) != template)
225             error (EXIT_FAILURE, 0,
226                    _("invalid template, %s, contains directory separator"),
227                    quote (template));
228         }
229       else
230         {
231           if (dest_dir_arg && *dest_dir_arg)
232             dest_dir = dest_dir_arg;
233           else
234             {
235               char *env = getenv ("TMPDIR");
236               dest_dir = (env && *env ? env : "/tmp");
237             }
238           if (IS_ABSOLUTE_FILE_NAME (template))
239             error (EXIT_FAILURE, 0,
240                    _("invalid template, %s; with --tmpdir,"
241                      " it may not be absolute"),
242                    quote (template));
243         }
244
245       template = file_name_concat (dest_dir, template, NULL);
246     }
247   else
248     {
249       template = xstrdup (template);
250     }
251
252   /* Make a copy to be used in case of diagnostic, since failing
253      mkstemp may leave the buffer in an undefined state.  */
254   dest_name = xstrdup (template);
255
256   if (create_directory)
257     {
258       int err = mkdtemp_len (dest_name, x_count, dry_run);
259       if (err != 0)
260         {
261           error (0, errno, _("failed to create directory via template %s"),
262                  quote (template));
263           status = EXIT_FAILURE;
264         }
265     }
266   else
267     {
268       int fd = mkstemp_len (dest_name, x_count, dry_run);
269       if (fd < 0 || (!dry_run && close (fd) != 0))
270         {
271           error (0, errno, _("failed to create file via template %s"),
272                  quote (template));
273           status = EXIT_FAILURE;
274         }
275     }
276
277   if (status == EXIT_SUCCESS)
278     puts (dest_name);
279
280 #ifdef lint
281   free (dest_name);
282 #endif
283
284   exit (status);
285 }
286
287 /*
288  * Local variables:
289  *  indent-tabs-mode: nil
290  * End:
291  */