xref: /freebsd/usr.bin/mktemp/mktemp.c (revision 9f44a47fd07924afc035991af15d84e6585dea4f)
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 #ifndef lint
49 static const char rcsid[] =
50 	"$FreeBSD$";
51 #endif /* not lint */
52 
53 static void usage(void);
54 
55 static const struct option long_opts[] = {
56 	{"directory",	no_argument,	NULL,	'd'},
57 	{"tmpdir",	optional_argument,	NULL,	'p'},
58 	{"quiet",	no_argument,	NULL,	'q'},
59 	{"dry-run",	no_argument,	NULL,	'u'},
60 	{NULL,		no_argument,	NULL,	0},
61 };
62 
63 int
64 main(int argc, char **argv)
65 {
66 	int c, fd, ret;
67 	const char *prefix, *tmpdir;
68 	char *name;
69 	int dflag, qflag, tflag, uflag;
70 	bool prefer_tmpdir;
71 
72 	ret = dflag = qflag = tflag = uflag = 0;
73 	prefer_tmpdir = true;
74 	prefix = "mktemp";
75 	name = NULL;
76 	tmpdir = NULL;
77 
78 	while ((c = getopt_long(argc, argv, "dp:qt:u", long_opts, NULL)) != -1)
79 		switch (c) {
80 		case 'd':
81 			dflag++;
82 			break;
83 
84 		case 'p':
85 			tmpdir = optarg;
86 			if (tmpdir == NULL || *tmpdir == '\0')
87 				tmpdir = getenv("TMPDIR");
88 
89 			/*
90 			 * We've already done the necessary environment
91 			 * fallback, skip the later one.
92 			 */
93 			prefer_tmpdir = false;
94 			break;
95 
96 		case 'q':
97 			qflag++;
98 			break;
99 
100 		case 't':
101 			prefix = optarg;
102 			tflag++;
103 			break;
104 
105 		case 'u':
106 			uflag++;
107 			break;
108 
109 		default:
110 			usage();
111 		}
112 
113 	argc -= optind;
114 	argv += optind;
115 
116 	if (!tflag && argc < 1) {
117 		tflag = 1;
118 		prefix = "tmp";
119 
120 		/*
121 		 * For this implied -t mode, we actually want to swap the usual
122 		 * order of precedence: -p, then TMPDIR, then /tmp.
123 		 */
124 		prefer_tmpdir = false;
125 	}
126 
127 	if (tflag) {
128 		const char *envtmp;
129 		size_t len;
130 
131 		envtmp = NULL;
132 
133 		/*
134 		 * $TMPDIR preferred over `-p` if specified, for compatibility.
135 		 */
136 		if (prefer_tmpdir || tmpdir == NULL)
137 			envtmp = getenv("TMPDIR");
138 		if (envtmp != NULL)
139 			tmpdir = envtmp;
140 		if (tmpdir == NULL)
141 			tmpdir = _PATH_TMP;
142 		len = strlen(tmpdir);
143 		if (len > 0 && tmpdir[len - 1] == '/')
144 			asprintf(&name, "%s%s.XXXXXXXXXX", tmpdir, prefix);
145 		else
146 			asprintf(&name, "%s/%s.XXXXXXXXXX", tmpdir, prefix);
147 		/* if this fails, the program is in big trouble already */
148 		if (name == NULL) {
149 			if (qflag)
150 				return (1);
151 			else
152 				errx(1, "cannot generate template");
153 		}
154 	}
155 
156 	/* generate all requested files */
157 	while (name != NULL || argc > 0) {
158 		if (name == NULL) {
159 			if (!tflag && tmpdir != NULL)
160 				asprintf(&name, "%s/%s", tmpdir, argv[0]);
161 			else
162 				name = strdup(argv[0]);
163 			if (name == NULL)
164 				err(1, "%s", argv[0]);
165 			argv++;
166 			argc--;
167 		}
168 
169 		if (dflag) {
170 			if (mkdtemp(name) == NULL) {
171 				ret = 1;
172 				if (!qflag)
173 					warn("mkdtemp failed on %s", name);
174 			} else {
175 				printf("%s\n", name);
176 				if (uflag)
177 					rmdir(name);
178 			}
179 		} else {
180 			fd = mkstemp(name);
181 			if (fd < 0) {
182 				ret = 1;
183 				if (!qflag)
184 					warn("mkstemp failed on %s", name);
185 			} else {
186 				close(fd);
187 				if (uflag)
188 					unlink(name);
189 				printf("%s\n", name);
190 			}
191 		}
192 		if (name)
193 			free(name);
194 		name = NULL;
195 	}
196 	return (ret);
197 }
198 
199 static void
200 usage(void)
201 {
202 	fprintf(stderr,
203 		"usage: mktemp [-d] [-p tmpdir] [-q] [-t prefix] [-u] template "
204 		"...\n");
205 	fprintf(stderr,
206 		"       mktemp [-d] [-p tmpdir] [-q] [-u] -t prefix \n");
207 	exit (1);
208 }
209