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