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