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