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