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