tmpfile.cpp revision 114402
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
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with groff; see the file COPYING.  If not, write to the Free Software
20Foundation, 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
47char *tmpfile_prefix;
48size_t tmpfile_prefix_len;
49int use_short_postfix = 0;
50
51struct temp_init {
52  temp_init();
53  ~temp_init();
54} _temp_init;
55
56temp_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
83temp_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
96char *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
116struct xtmpfile_list {
117  char *fname;
118  xtmpfile_list *next;
119  xtmpfile_list(char *fn) : fname(fn), next(0) {}
120};
121
122xtmpfile_list *xtmpfiles_to_delete = 0;
123
124struct xtmpfile_list_init {
125  ~xtmpfile_list_init();
126} _xtmpfile_list_init;
127
128xtmpfile_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
141static 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
152FILE *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}
173