* Makefile.in (ENVSETUP): Don't assume POSIX make semantics for
[s-roff.git] / src / libs / libgroff / tmpfile.cc
blobffc165cedf7fbcfa9ae1fd85405d592d7fe42482
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
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 2, or (at your option) any later
10 version.
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.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 #include <stdio.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <stdlib.h>
26 #include "posix.h"
27 #include "lib.h"
28 #include "errarg.h"
29 #include "error.h"
30 #include "nonposix.h"
32 extern "C" {
33 // Solaris 2.5.1 has these functions,
34 // but its stdlib.h fails to declare them.
35 char *mktemp(char *);
36 int mkstemp(char *);
39 // If this is set, create temporary files there
40 #define GROFF_TMPDIR_ENVVAR "GROFF_TMPDIR"
41 // otherwise if this is set, create temporary files there
42 #define TMPDIR_ENVVAR "TMPDIR"
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 "groff"
54 * Generate a temporary name template with a postfix
55 * immediately after the TMPFILE_PREFIX.
56 * It uses the groff preferences for a temporary directory.
57 * Note that no file name is either created or opened,
58 * only the *template* is returned.
61 char *xtmptemplate(char *postfix)
63 const char *dir = getenv(GROFF_TMPDIR_ENVVAR);
64 int postlen = 0;
66 if (postfix)
67 postlen = strlen(postfix);
69 if (!dir) {
70 dir = getenv(TMPDIR_ENVVAR);
71 if (!dir)
72 dir = DEFAULT_TMPDIR;
75 size_t dir_len = strlen(dir);
76 const char *dir_end = dir + dir_len - 1;
77 int needs_slash = strchr(DIR_SEPS, *dir_end) == NULL;
78 char *templ = new char[strlen(dir) + needs_slash
79 + sizeof(TMPFILE_PREFIX) - 1 + 6 + 1 + postlen];
80 strcpy(templ, dir);
81 if (needs_slash)
82 strcat(templ, "/");
83 strcat(templ, TMPFILE_PREFIX);
84 if (postlen > 0)
85 strcat(templ, postfix);
86 strcat(templ, "XXXXXX");
88 return( templ );
91 // The trick with unlinking the temporary file while it is still in
92 // use is not portable, it will fail on MS-DOS and most MS-Windows
93 // filesystems. So it cannot be used on non-Posix systems.
94 // Instead, we maintain a list of files to be deleted on exit, and
95 // register an atexit function that will remove them all in one go.
96 // This should be portable to all platforms.
98 static struct xtmpfile_list {
99 struct xtmpfile_list *next;
100 char *fname;
101 } *xtmpfiles_to_delete;
103 static void remove_tmp_files(void)
105 struct xtmpfile_list *p = xtmpfiles_to_delete;
107 while (p)
109 if (unlink(p->fname) < 0)
110 error("cannot unlink `%1': %2", p->fname, strerror(errno));
111 a_delete p->fname;
112 struct xtmpfile_list *old = p;
113 p = p->next;
114 free(old);
118 static void add_tmp_file(const char *name)
120 if (xtmpfiles_to_delete == NULL)
121 atexit(remove_tmp_files);
123 char *fname = new char[FILENAME_MAX];
124 struct xtmpfile_list *p
125 = (struct xtmpfile_list *)malloc(sizeof(struct xtmpfile_list));
126 if (p == NULL)
128 error("cannot unlink `%1': %2", name, strerror(errno));
129 return;
131 p->next = xtmpfiles_to_delete;
132 p->fname = strcpy(fname, name);
133 xtmpfiles_to_delete = p;
136 // Open a temporary file and with fatal error on failure.
138 #ifndef _MSC_VER
140 FILE *xtmpfile(char **namep, char *postfix, int do_unlink)
142 char *templ = xtmptemplate(postfix);
144 #ifdef HAVE_MKSTEMP
145 errno = 0;
146 int fd = mkstemp(templ);
147 if (fd < 0)
148 fatal("cannot create temporary file: %1", strerror(errno));
149 errno = 0;
150 FILE *fp = fdopen(fd, FOPEN_RWB); // many callers of xtmpfile use binary I/O
151 if (!fp)
152 fatal("fdopen: %1", strerror(errno));
153 #else /* not HAVE_MKSTEMP */
154 if (!mktemp(templ) || !templ[0])
155 fatal("cannot create file name for temporary file");
156 errno = 0;
157 FILE *fp = fopen(templ, FOPEN_RWB);
158 if (!fp)
159 fatal("cannot open `%1': %2", templ, strerror(errno));
160 #endif /* not HAVE_MKSTEMP */
161 if (do_unlink)
162 add_tmp_file(templ);
163 if ((namep != 0) && ((*namep) != 0)) {
164 *namep = templ;
165 } else {
166 a_delete templ;
168 return fp;
171 #else
173 // FIXME: does MSVC have mktemp or mkstemp? If so, it should now
174 // use the version above, as it no longer removes an open file.
175 // The version below will NOT work with grohtml, since grohtml
176 // wants to know the name of the file opened by xtmpfile!!
178 // If you're not running Unix, the following will do:
179 FILE *xtmpfile(char **namep, char *postfix, int do_unlink)
181 FILE *fp = tmpfile();
182 if (!fp)
183 fatal("couldn't create temporary file");
184 return fp;
187 #endif /* _MSC_VER */