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