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