Line data Source code
1 : /* Copyright 1989-2025 Free Software Foundation, Inc.
2 : Written by James Clark (jjc@jclark.com)
3 :
4 : This file is part of groff, the GNU roff typesetting system.
5 :
6 : groff is free software; you can redistribute it and/or modify it under
7 : the terms of the GNU General Public License as published by the Free
8 : Software Foundation, either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : groff is distributed in the hope that it will be useful, but WITHOUT ANY
12 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 : for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 :
19 : #ifdef HAVE_CONFIG_H
20 : #include <config.h>
21 : #endif
22 :
23 : #include <errno.h>
24 : #include <stdio.h> // FILE, fdopen()
25 : #include <stdlib.h> // getenv(), mkstemp()
26 : #include <string.h> // strcat(), strchr(), strcpy(), strerror(),
27 : // strlen()
28 :
29 : // needed for unlink()
30 : #include "posix.h"
31 : #include "nonposix.h"
32 :
33 : #include "lib.h"
34 :
35 : #include "posix.h"
36 : #include "errarg.h"
37 : #include "error.h"
38 : #include "nonposix.h"
39 :
40 : // If this is set, create temporary files there
41 : #define GROFF_TMPDIR_ENVVAR "GROFF_TMPDIR"
42 : // otherwise if this is set, create temporary files there
43 : #define TMPDIR_ENVVAR "TMPDIR"
44 : // otherwise, on MS-DOS or MS-Windows ...
45 : #if defined(__MSDOS__) || defined(_WIN32)
46 : // if either of these is set, create temporary files there
47 : // (giving priority to WIN32_TMPDIR_ENVVAR)
48 : #define WIN32_TMPDIR_ENVVAR "TMP"
49 : #define MSDOS_TMPDIR_ENVVAR "TEMP"
50 : #endif
51 : // otherwise if P_tmpdir is defined, create temporary files there
52 : #ifdef P_tmpdir
53 : # define DEFAULT_TMPDIR P_tmpdir
54 : #else
55 : // otherwise create temporary files here.
56 : # define DEFAULT_TMPDIR "/tmp"
57 : #endif
58 : // Use this as the prefix for temporary filenames.
59 : #define TMPFILE_PREFIX_SHORT ""
60 : #define TMPFILE_PREFIX_LONG "groff"
61 :
62 : char *tmpfile_prefix;
63 : size_t tmpfile_prefix_len;
64 : int use_short_postfix = 0;
65 :
66 : struct temp_init {
67 : temp_init();
68 : ~temp_init();
69 : } _temp_init;
70 :
71 100 : temp_init::temp_init()
72 : {
73 : // First, choose a location for creating temporary files...
74 : const char *tem;
75 : // using the first match for any of the environment specs in listed order.
76 100 : if (
77 100 : (tem = getenv(GROFF_TMPDIR_ENVVAR)) == 0
78 100 : && (tem = getenv(TMPDIR_ENVVAR)) == 0
79 : #if defined(__MSDOS__) || defined(_WIN32)
80 : // If we didn't find a match for either of the above
81 : // (which are preferred, regardless of the host operating system),
82 : // and we are hosted on either MS-Windows or MS-DOS,
83 : // then try the Microsoft conventions.
84 : && (tem = getenv(WIN32_TMPDIR_ENVVAR)) == 0
85 : && (tem = getenv(MSDOS_TMPDIR_ENVVAR)) == 0
86 : #endif
87 : )
88 : // If we didn't find an environment spec fall back to this default.
89 0 : tem = DEFAULT_TMPDIR;
90 100 : size_t tem_len = strlen(tem);
91 100 : const char *tem_end = tem + tem_len - 1;
92 100 : int need_slash = (strchr(DIR_SEPS, *tem_end) == 0) ? 1 : 0;
93 100 : char *tem2 = new char[tem_len + need_slash + 1];
94 100 : strcpy(tem2, tem);
95 100 : if (need_slash)
96 100 : strcat(tem2, "/");
97 100 : const char *tem3 = TMPFILE_PREFIX_LONG;
98 100 : if (file_name_max(tem2) <= 14) {
99 0 : tem3 = TMPFILE_PREFIX_SHORT;
100 0 : use_short_postfix = 1;
101 : }
102 100 : tmpfile_prefix_len = tem_len + need_slash + strlen(tem3);
103 100 : tmpfile_prefix = new char[tmpfile_prefix_len + 1];
104 100 : strcpy(tmpfile_prefix, tem2);
105 100 : strcat(tmpfile_prefix, tem3);
106 100 : delete[] tem2;
107 100 : }
108 :
109 100 : temp_init::~temp_init()
110 : {
111 100 : delete[] tmpfile_prefix;
112 100 : }
113 :
114 : /*
115 : * Generate a temporary name template with a postfix
116 : * immediately after the TMPFILE_PREFIX.
117 : * It uses the groff preferences for a temporary directory.
118 : * Note that no file name is either created or opened,
119 : * only the *template* is returned.
120 : */
121 :
122 183 : char *xtmptemplate(const char *postfix_long, const char *postfix_short)
123 : {
124 183 : const char *postfix = use_short_postfix ? postfix_short : postfix_long;
125 183 : int postlen = 0;
126 183 : if (postfix)
127 68 : postlen = strlen(postfix);
128 183 : char *templ = new char[tmpfile_prefix_len + postlen + 6 + 1];
129 183 : strcpy(templ, tmpfile_prefix);
130 183 : if (postlen > 0)
131 68 : strcat(templ, postfix);
132 183 : strcat(templ, "XXXXXX");
133 183 : return templ;
134 : }
135 :
136 : // The trick with unlinking the temporary file while it is still in
137 : // use is not portable, it will fail on MS-DOS and most MS-Windows
138 : // filesystems. So it cannot be used on non-Posix systems.
139 : // Instead, we maintain a list of files to be deleted on exit.
140 : // This should be portable to all platforms.
141 :
142 : struct xtmpfile_list {
143 : char *fname;
144 : xtmpfile_list *next;
145 183 : xtmpfile_list(char *fn) : fname(fn), next(0) {}
146 : };
147 :
148 : xtmpfile_list *xtmpfiles_to_delete = 0;
149 :
150 : struct xtmpfile_list_init {
151 : ~xtmpfile_list_init();
152 : } _xtmpfile_list_init;
153 :
154 100 : xtmpfile_list_init::~xtmpfile_list_init()
155 : {
156 100 : xtmpfile_list *x = xtmpfiles_to_delete;
157 283 : while (x != 0) {
158 183 : if (unlink(x->fname) < 0)
159 0 : error("cannot unlink '%1': %2", x->fname, strerror(errno));
160 183 : xtmpfile_list *tmp = x;
161 183 : x = x->next;
162 183 : delete[] tmp->fname;
163 183 : delete tmp;
164 : }
165 100 : }
166 :
167 183 : static void add_tmp_file(const char *name)
168 : {
169 183 : char *s = new char[strlen(name)+1];
170 183 : strcpy(s, name);
171 183 : xtmpfile_list *x = new xtmpfile_list(s);
172 183 : x->next = xtmpfiles_to_delete;
173 183 : xtmpfiles_to_delete = x;
174 183 : }
175 :
176 : // Open a temporary file; any failure is fatal.
177 :
178 183 : FILE *xtmpfile(const char **namep,
179 : const char *postfix_long, const char *postfix_short)
180 : {
181 : // `xtmptemplate()` allocates storage for `templ` with `new`.
182 183 : char *templ = xtmptemplate(postfix_long, postfix_short);
183 183 : errno = 0;
184 183 : int fd = mkstemp(templ);
185 183 : if (fd < 0)
186 0 : fatal("cannot create temporary file: %1", strerror(errno));
187 183 : errno = 0;
188 : // Many of our callers use binary I/O.
189 183 : FILE *fp = fdopen(fd, FOPEN_RWB);
190 183 : if (!fp)
191 0 : fatal("cannot open temporary file: %1", strerror(errno));
192 183 : add_tmp_file(templ);
193 183 : if (namep != 0 /* nullptr */)
194 68 : *namep = templ;
195 : else
196 115 : delete[] templ;
197 183 : return fp;
198 : }
199 :
200 : // Local Variables:
201 : // fill-column: 72
202 : // mode: C++
203 : // End:
204 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|