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