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