xref: /freebsd/usr.bin/mktemp/mktemp.c (revision b50261e21f39a6c7249a49e7b60aa878c98512a8)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1994, 1995, 1996, 1998 Peter Wemm <peter@netplex.com.au>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 /*
31  * This program was originally written long ago, originally for a non
32  * BSD-like OS without mkstemp().  It's been modified over the years
33  * to use mkstemp() rather than the original O_CREAT|O_EXCL/fstat/lstat
34  * etc style hacks.
35  * A cleanup, misc options and mkdtemp() calls were added to try and work
36  * more like the OpenBSD version - which was first to publish the interface.
37  */
38 
39 #include <err.h>
40 #include <getopt.h>
41 #include <paths.h>
42 #include <stdbool.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 
48 static void usage(void) __dead2;
49 
50 static const struct option long_opts[] = {
51 	{"directory",	no_argument,	NULL,	'd'},
52 	{"tmpdir",	optional_argument,	NULL,	'p'},
53 	{"quiet",	no_argument,	NULL,	'q'},
54 	{"dry-run",	no_argument,	NULL,	'u'},
55 	{NULL,		no_argument,	NULL,	0},
56 };
57 
58 int
59 main(int argc, char **argv)
60 {
61 	int c, fd, ret;
62 	const char *prefix, *tmpdir;
63 	char *name;
64 	int dflag, qflag, tflag, uflag;
65 	bool prefer_tmpdir;
66 
67 	ret = dflag = qflag = tflag = uflag = 0;
68 	prefer_tmpdir = true;
69 	prefix = "mktemp";
70 	name = NULL;
71 	tmpdir = NULL;
72 
73 	while ((c = getopt_long(argc, argv, "dp:qt:u", long_opts, NULL)) != -1)
74 		switch (c) {
75 		case 'd':
76 			dflag++;
77 			break;
78 
79 		case 'p':
80 			tmpdir = optarg;
81 			if (tmpdir == NULL || *tmpdir == '\0')
82 				tmpdir = getenv("TMPDIR");
83 
84 			/*
85 			 * We've already done the necessary environment
86 			 * fallback, skip the later one.
87 			 */
88 			prefer_tmpdir = false;
89 			break;
90 
91 		case 'q':
92 			qflag++;
93 			break;
94 
95 		case 't':
96 			prefix = optarg;
97 			tflag++;
98 			break;
99 
100 		case 'u':
101 			uflag++;
102 			break;
103 
104 		default:
105 			usage();
106 		}
107 
108 	argc -= optind;
109 	argv += optind;
110 
111 	if (!tflag && argc < 1) {
112 		tflag = 1;
113 		prefix = "tmp";
114 
115 		/*
116 		 * For this implied -t mode, we actually want to swap the usual
117 		 * order of precedence: -p, then TMPDIR, then /tmp.
118 		 */
119 		prefer_tmpdir = false;
120 	}
121 
122 	if (tflag) {
123 		const char *envtmp;
124 		size_t len;
125 
126 		envtmp = NULL;
127 
128 		/*
129 		 * $TMPDIR preferred over `-p` if specified, for compatibility.
130 		 */
131 		if (prefer_tmpdir || tmpdir == NULL)
132 			envtmp = getenv("TMPDIR");
133 		if (envtmp != NULL)
134 			tmpdir = envtmp;
135 		if (tmpdir == NULL)
136 			tmpdir = _PATH_TMP;
137 		len = strlen(tmpdir);
138 		if (len > 0 && tmpdir[len - 1] == '/')
139 			asprintf(&name, "%s%s.XXXXXXXXXX", tmpdir, prefix);
140 		else
141 			asprintf(&name, "%s/%s.XXXXXXXXXX", tmpdir, prefix);
142 		/* if this fails, the program is in big trouble already */
143 		if (name == NULL) {
144 			if (qflag)
145 				return (1);
146 			else
147 				errx(1, "cannot generate template");
148 		}
149 	}
150 
151 	/* generate all requested files */
152 	while (name != NULL || argc > 0) {
153 		if (name == NULL) {
154 			if (!tflag && tmpdir != NULL)
155 				asprintf(&name, "%s/%s", tmpdir, argv[0]);
156 			else
157 				name = strdup(argv[0]);
158 			if (name == NULL)
159 				err(1, "%s", argv[0]);
160 			argv++;
161 			argc--;
162 		}
163 
164 		if (dflag) {
165 			if (mkdtemp(name) == NULL) {
166 				ret = 1;
167 				if (!qflag)
168 					warn("mkdtemp failed on %s", name);
169 			} else {
170 				printf("%s\n", name);
171 				if (uflag)
172 					rmdir(name);
173 			}
174 		} else {
175 			fd = mkstemp(name);
176 			if (fd < 0) {
177 				ret = 1;
178 				if (!qflag)
179 					warn("mkstemp failed on %s", name);
180 			} else {
181 				close(fd);
182 				if (uflag)
183 					unlink(name);
184 				printf("%s\n", name);
185 			}
186 		}
187 		if (name)
188 			free(name);
189 		name = NULL;
190 	}
191 	return (ret);
192 }
193 
194 static void
195 usage(void)
196 {
197 	fprintf(stderr,
198 		"usage: mktemp [-d] [-p tmpdir] [-q] [-t prefix] [-u] template "
199 		"...\n");
200 	fprintf(stderr,
201 		"       mktemp [-d] [-p tmpdir] [-q] [-u] -t prefix \n");
202 	exit (1);
203 }
204