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