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
48static void usage(void) __dead2;
49
50static 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
58int
59main(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
194static void
195usage(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