Get rid of the old texinfo.
[dragonfly.git] / contrib / groff / src / libs / libgroff / tmpfile.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001
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 2, or (at your option) any later
11 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 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. */
21
22 #include "lib.h"
23
24 #include <errno.h>
25 #include <stdlib.h>
26
27 #include "posix.h"
28 #include "errarg.h"
29 #include "error.h"
30 #include "nonposix.h"
31
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 if P_tmpdir is defined, create temporary files there
37 #ifdef P_tmpdir
38 # define DEFAULT_TMPDIR P_tmpdir
39 #else
40 // otherwise create temporary files here.
41 # define DEFAULT_TMPDIR "/tmp"
42 #endif
43 // Use this as the prefix for temporary filenames.
44 #define TMPFILE_PREFIX_SHORT ""
45 #define TMPFILE_PREFIX_LONG "groff"
46
47 char *tmpfile_prefix;
48 size_t tmpfile_prefix_len;
49 int use_short_postfix = 0;
50
51 struct temp_init {
52   temp_init();
53   ~temp_init();
54 } _temp_init;
55
56 temp_init::temp_init()
57 {
58   const char *tem = getenv(GROFF_TMPDIR_ENVVAR);
59   if (!tem) {
60     tem = getenv(TMPDIR_ENVVAR);
61     if (!tem)
62       tem = DEFAULT_TMPDIR;
63   }
64   size_t tem_len = strlen(tem);
65   const char *tem_end = tem + tem_len - 1;
66   int need_slash = strchr(DIR_SEPS, *tem_end) == NULL ? 1 : 0;
67   char *tem2 = new char[tem_len + need_slash + 1];
68   strcpy(tem2, tem);
69   if (need_slash)
70     strcat(tem2, "/");
71   const char *tem3 = TMPFILE_PREFIX_LONG;
72   if (file_name_max(tem2) <= 14) {
73     tem3 = TMPFILE_PREFIX_SHORT;
74     use_short_postfix = 1;
75   }
76   tmpfile_prefix_len = tem_len + need_slash + strlen(tem3);
77   tmpfile_prefix = new char[tmpfile_prefix_len + 1];
78   strcpy(tmpfile_prefix, tem2);
79   strcat(tmpfile_prefix, tem3);
80   a_delete tem2;
81 }
82
83 temp_init::~temp_init()
84 {
85   a_delete tmpfile_prefix;
86 }
87
88 /*
89  *  Generate a temporary name template with a postfix
90  *  immediately after the TMPFILE_PREFIX.
91  *  It uses the groff preferences for a temporary directory.
92  *  Note that no file name is either created or opened,
93  *  only the *template* is returned.
94  */
95
96 char *xtmptemplate(const char *postfix_long, const char *postfix_short)
97 {
98   const char *postfix = use_short_postfix ? postfix_short : postfix_long;
99   int postlen = 0;
100   if (postfix)
101     postlen = strlen(postfix);
102   char *templ = new char[tmpfile_prefix_len + postlen + 6 + 1];
103   strcpy(templ, tmpfile_prefix);
104   if (postlen > 0)
105     strcat(templ, postfix);
106   strcat(templ, "XXXXXX");
107   return templ;
108 }
109
110 // The trick with unlinking the temporary file while it is still in
111 // use is not portable, it will fail on MS-DOS and most MS-Windows
112 // filesystems.  So it cannot be used on non-Posix systems.
113 // Instead, we maintain a list of files to be deleted on exit.
114 // This should be portable to all platforms.
115
116 struct xtmpfile_list {
117   char *fname;
118   xtmpfile_list *next;
119   xtmpfile_list(char *fn) : fname(fn), next(0) {}
120 };
121
122 xtmpfile_list *xtmpfiles_to_delete = 0;
123
124 struct xtmpfile_list_init {
125   ~xtmpfile_list_init();
126 } _xtmpfile_list_init;
127
128 xtmpfile_list_init::~xtmpfile_list_init()
129 {
130   xtmpfile_list *x = xtmpfiles_to_delete;
131   while (x != 0) {
132     if (unlink(x->fname) < 0)
133       error("cannot unlink `%1': %2", x->fname, strerror(errno));
134     xtmpfile_list *tmp = x;
135     x = x->next;
136     a_delete tmp->fname;
137     delete tmp;
138   }
139 }
140
141 static void add_tmp_file(const char *name)
142 {
143   char *s = new char[strlen(name)+1];
144   strcpy(s, name);
145   xtmpfile_list *x = new xtmpfile_list(s);
146   x->next = xtmpfiles_to_delete;
147   xtmpfiles_to_delete = x;
148 }
149
150 // Open a temporary file and with fatal error on failure.
151
152 FILE *xtmpfile(char **namep,
153                const char *postfix_long, const char *postfix_short,
154                int do_unlink)
155 {
156   char *templ = xtmptemplate(postfix_long, postfix_short);
157   errno = 0;
158   int fd = mkstemp(templ);
159   if (fd < 0)
160     fatal("cannot create temporary file: %1", strerror(errno));
161   errno = 0;
162   FILE *fp = fdopen(fd, FOPEN_RWB); // many callers of xtmpfile use binary I/O
163   if (!fp)
164     fatal("fdopen: %1", strerror(errno));
165   if (do_unlink)
166     add_tmp_file(templ);
167   if (namep)
168     *namep = templ;
169   else
170     a_delete templ;
171   return fp;
172 }