1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2003
3114402Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "lib.h"
23114402Sru
24114402Sru#include <errno.h>
25114402Sru#include <stdlib.h>
26114402Sru
27114402Sru#include "posix.h"
28114402Sru#include "errarg.h"
29114402Sru#include "error.h"
30114402Sru#include "nonposix.h"
31114402Sru
32114402Sru// If this is set, create temporary files there
33114402Sru#define GROFF_TMPDIR_ENVVAR "GROFF_TMPDIR"
34114402Sru// otherwise if this is set, create temporary files there
35114402Sru#define TMPDIR_ENVVAR "TMPDIR"
36151497Sru// otherwise, on MS-DOS or MS-Windows ...
37151497Sru#if defined(__MSDOS__) || defined(_WIN32)
38151497Sru// if either of these is set, create temporary files there
39151497Sru// (giving priority to WIN32_TMPDIR_ENVVAR)
40151497Sru#define WIN32_TMPDIR_ENVVAR "TMP"
41151497Sru#define MSDOS_TMPDIR_ENVVAR "TEMP"
42151497Sru#endif
43114402Sru// otherwise if P_tmpdir is defined, create temporary files there
44114402Sru#ifdef P_tmpdir
45114402Sru# define DEFAULT_TMPDIR P_tmpdir
46114402Sru#else
47114402Sru// otherwise create temporary files here.
48114402Sru# define DEFAULT_TMPDIR "/tmp"
49114402Sru#endif
50114402Sru// Use this as the prefix for temporary filenames.
51114402Sru#define TMPFILE_PREFIX_SHORT ""
52114402Sru#define TMPFILE_PREFIX_LONG "groff"
53114402Sru
54114402Sruchar *tmpfile_prefix;
55114402Srusize_t tmpfile_prefix_len;
56114402Sruint use_short_postfix = 0;
57114402Sru
58114402Srustruct temp_init {
59114402Sru  temp_init();
60114402Sru  ~temp_init();
61114402Sru} _temp_init;
62114402Sru
63114402Srutemp_init::temp_init()
64114402Sru{
65151497Sru  // First, choose a location for creating temporary files...
66151497Sru  const char *tem;
67151497Sru  // using the first match for any of the environment specs in listed order.
68151497Sru  if (
69151497Sru      (tem = getenv(GROFF_TMPDIR_ENVVAR)) == NULL
70151497Sru      && (tem = getenv(TMPDIR_ENVVAR)) == NULL
71151497Sru#if defined(__MSDOS__) || defined(_WIN32)
72151497Sru      // If we didn't find a match for either of the above
73151497Sru      // (which are preferred, regardless of the host operating system),
74151497Sru      // and we are hosted on either MS-Windows or MS-DOS,
75151497Sru      // then try the Microsoft conventions.
76151497Sru      && (tem = getenv(WIN32_TMPDIR_ENVVAR)) == NULL
77151497Sru      && (tem = getenv(MSDOS_TMPDIR_ENVVAR)) == NULL
78151497Sru#endif
79151497Sru     )
80151497Sru    // If we didn't find an environment spec fall back to this default.
81151497Sru    tem = DEFAULT_TMPDIR;
82114402Sru  size_t tem_len = strlen(tem);
83114402Sru  const char *tem_end = tem + tem_len - 1;
84114402Sru  int need_slash = strchr(DIR_SEPS, *tem_end) == NULL ? 1 : 0;
85114402Sru  char *tem2 = new char[tem_len + need_slash + 1];
86114402Sru  strcpy(tem2, tem);
87114402Sru  if (need_slash)
88114402Sru    strcat(tem2, "/");
89114402Sru  const char *tem3 = TMPFILE_PREFIX_LONG;
90114402Sru  if (file_name_max(tem2) <= 14) {
91114402Sru    tem3 = TMPFILE_PREFIX_SHORT;
92114402Sru    use_short_postfix = 1;
93114402Sru  }
94114402Sru  tmpfile_prefix_len = tem_len + need_slash + strlen(tem3);
95114402Sru  tmpfile_prefix = new char[tmpfile_prefix_len + 1];
96114402Sru  strcpy(tmpfile_prefix, tem2);
97114402Sru  strcat(tmpfile_prefix, tem3);
98114402Sru  a_delete tem2;
99114402Sru}
100114402Sru
101114402Srutemp_init::~temp_init()
102114402Sru{
103114402Sru  a_delete tmpfile_prefix;
104114402Sru}
105114402Sru
106114402Sru/*
107114402Sru *  Generate a temporary name template with a postfix
108114402Sru *  immediately after the TMPFILE_PREFIX.
109114402Sru *  It uses the groff preferences for a temporary directory.
110114402Sru *  Note that no file name is either created or opened,
111114402Sru *  only the *template* is returned.
112114402Sru */
113114402Sru
114114402Sruchar *xtmptemplate(const char *postfix_long, const char *postfix_short)
115114402Sru{
116114402Sru  const char *postfix = use_short_postfix ? postfix_short : postfix_long;
117114402Sru  int postlen = 0;
118114402Sru  if (postfix)
119114402Sru    postlen = strlen(postfix);
120114402Sru  char *templ = new char[tmpfile_prefix_len + postlen + 6 + 1];
121114402Sru  strcpy(templ, tmpfile_prefix);
122114402Sru  if (postlen > 0)
123114402Sru    strcat(templ, postfix);
124114402Sru  strcat(templ, "XXXXXX");
125114402Sru  return templ;
126114402Sru}
127114402Sru
128114402Sru// The trick with unlinking the temporary file while it is still in
129114402Sru// use is not portable, it will fail on MS-DOS and most MS-Windows
130114402Sru// filesystems.  So it cannot be used on non-Posix systems.
131114402Sru// Instead, we maintain a list of files to be deleted on exit.
132114402Sru// This should be portable to all platforms.
133114402Sru
134114402Srustruct xtmpfile_list {
135114402Sru  char *fname;
136114402Sru  xtmpfile_list *next;
137114402Sru  xtmpfile_list(char *fn) : fname(fn), next(0) {}
138114402Sru};
139114402Sru
140114402Sruxtmpfile_list *xtmpfiles_to_delete = 0;
141114402Sru
142114402Srustruct xtmpfile_list_init {
143114402Sru  ~xtmpfile_list_init();
144114402Sru} _xtmpfile_list_init;
145114402Sru
146114402Sruxtmpfile_list_init::~xtmpfile_list_init()
147114402Sru{
148114402Sru  xtmpfile_list *x = xtmpfiles_to_delete;
149114402Sru  while (x != 0) {
150114402Sru    if (unlink(x->fname) < 0)
151114402Sru      error("cannot unlink `%1': %2", x->fname, strerror(errno));
152114402Sru    xtmpfile_list *tmp = x;
153114402Sru    x = x->next;
154114402Sru    a_delete tmp->fname;
155114402Sru    delete tmp;
156114402Sru  }
157114402Sru}
158114402Sru
159114402Srustatic void add_tmp_file(const char *name)
160114402Sru{
161114402Sru  char *s = new char[strlen(name)+1];
162114402Sru  strcpy(s, name);
163114402Sru  xtmpfile_list *x = new xtmpfile_list(s);
164114402Sru  x->next = xtmpfiles_to_delete;
165114402Sru  xtmpfiles_to_delete = x;
166114402Sru}
167114402Sru
168114402Sru// Open a temporary file and with fatal error on failure.
169114402Sru
170114402SruFILE *xtmpfile(char **namep,
171114402Sru	       const char *postfix_long, const char *postfix_short,
172114402Sru	       int do_unlink)
173114402Sru{
174114402Sru  char *templ = xtmptemplate(postfix_long, postfix_short);
175114402Sru  errno = 0;
176114402Sru  int fd = mkstemp(templ);
177114402Sru  if (fd < 0)
178114402Sru    fatal("cannot create temporary file: %1", strerror(errno));
179114402Sru  errno = 0;
180114402Sru  FILE *fp = fdopen(fd, FOPEN_RWB); // many callers of xtmpfile use binary I/O
181114402Sru  if (!fp)
182114402Sru    fatal("fdopen: %1", strerror(errno));
183114402Sru  if (do_unlink)
184114402Sru    add_tmp_file(templ);
185114402Sru  if (namep)
186114402Sru    *namep = templ;
187114402Sru  else
188114402Sru    a_delete templ;
189114402Sru  return fp;
190114402Sru}
191