xref: /freebsd/usr.bin/mktemp/mktemp.c (revision 58d84ef87094691bf1ad9608964ec85f120cd34a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 			break;
89 
90 		case 'q':
91 			qflag++;
92 			break;
93 
94 		case 't':
95 			prefix = optarg;
96 			tflag++;
97 			break;
98 
99 		case 'u':
100 			uflag++;
101 			break;
102 
103 		default:
104 			usage();
105 		}
106 
107 	argc -= optind;
108 	argv += optind;
109 
110 	if (!tflag && argc < 1) {
111 		tflag = 1;
112 		prefix = "tmp";
113 
114 		/*
115 		 * For this implied -t mode, we actually want to swap the usual
116 		 * order of precedence: -p, then TMPDIR, then /tmp.
117 		 */
118 		prefer_tmpdir = false;
119 	}
120 
121 	if (tflag) {
122 		const char *envtmp;
123 		size_t len;
124 
125 		envtmp = NULL;
126 
127 		/*
128 		 * $TMPDIR preferred over `-p` if specified, for compatibility.
129 		 */
130 		if (prefer_tmpdir || tmpdir == NULL)
131 			envtmp = getenv("TMPDIR");
132 		if (envtmp != NULL)
133 			tmpdir = envtmp;
134 		if (tmpdir == NULL)
135 			tmpdir = _PATH_TMP;
136 		len = strlen(tmpdir);
137 		if (len > 0 && tmpdir[len - 1] == '/')
138 			asprintf(&name, "%s%s.XXXXXXXXXX", tmpdir, prefix);
139 		else
140 			asprintf(&name, "%s/%s.XXXXXXXXXX", tmpdir, prefix);
141 		/* if this fails, the program is in big trouble already */
142 		if (name == NULL) {
143 			if (qflag)
144 				return (1);
145 			else
146 				errx(1, "cannot generate template");
147 		}
148 	}
149 
150 	/* generate all requested files */
151 	while (name != NULL || argc > 0) {
152 		if (name == NULL) {
153 			if (!tflag && tmpdir != NULL)
154 				asprintf(&name, "%s/%s", tmpdir, argv[0]);
155 			else
156 				name = strdup(argv[0]);
157 			if (name == NULL)
158 				err(1, "%s", argv[0]);
159 			argv++;
160 			argc--;
161 		}
162 
163 		if (dflag) {
164 			if (mkdtemp(name) == NULL) {
165 				ret = 1;
166 				if (!qflag)
167 					warn("mkdtemp failed on %s", name);
168 			} else {
169 				printf("%s\n", name);
170 				if (uflag)
171 					rmdir(name);
172 			}
173 		} else {
174 			fd = mkstemp(name);
175 			if (fd < 0) {
176 				ret = 1;
177 				if (!qflag)
178 					warn("mkstemp failed on %s", name);
179 			} else {
180 				close(fd);
181 				if (uflag)
182 					unlink(name);
183 				printf("%s\n", name);
184 			}
185 		}
186 		if (name)
187 			free(name);
188 		name = NULL;
189 	}
190 	return (ret);
191 }
192 
193 static void
194 usage(void)
195 {
196 	fprintf(stderr,
197 		"usage: mktemp [-d] [-p tmpdir] [-q] [-t prefix] [-u] template "
198 		"...\n");
199 	fprintf(stderr,
200 		"       mktemp [-d] [-p tmpdir] [-q] [-u] -t prefix \n");
201 	exit (1);
202 }
203