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