diff options
Diffstat (limited to 'src/libs/libgroff/tmpfile.cpp')
-rw-r--r-- | src/libs/libgroff/tmpfile.cpp | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/src/libs/libgroff/tmpfile.cpp b/src/libs/libgroff/tmpfile.cpp new file mode 100644 index 0000000..5e807ae --- /dev/null +++ b/src/libs/libgroff/tmpfile.cpp @@ -0,0 +1,188 @@ +// -*- C++ -*- +/* Copyright (C) 1989-2020 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, or +(at your option) any later version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "lib.h" + +#include <errno.h> +#include <stdlib.h> + +#include "posix.h" +#include "errarg.h" +#include "error.h" +#include "nonposix.h" + +// If this is set, create temporary files there +#define GROFF_TMPDIR_ENVVAR "GROFF_TMPDIR" +// otherwise if this is set, create temporary files there +#define TMPDIR_ENVVAR "TMPDIR" +// otherwise, on MS-DOS or MS-Windows ... +#if defined(__MSDOS__) || defined(_WIN32) +// if either of these is set, create temporary files there +// (giving priority to WIN32_TMPDIR_ENVVAR) +#define WIN32_TMPDIR_ENVVAR "TMP" +#define MSDOS_TMPDIR_ENVVAR "TEMP" +#endif +// otherwise if P_tmpdir is defined, create temporary files there +#ifdef P_tmpdir +# define DEFAULT_TMPDIR P_tmpdir +#else +// otherwise create temporary files here. +# define DEFAULT_TMPDIR "/tmp" +#endif +// Use this as the prefix for temporary filenames. +#define TMPFILE_PREFIX_SHORT "" +#define TMPFILE_PREFIX_LONG "groff" + +char *tmpfile_prefix; +size_t tmpfile_prefix_len; +int use_short_postfix = 0; + +struct temp_init { + temp_init(); + ~temp_init(); +} _temp_init; + +temp_init::temp_init() +{ + // First, choose a location for creating temporary files... + const char *tem; + // using the first match for any of the environment specs in listed order. + if ( + (tem = getenv(GROFF_TMPDIR_ENVVAR)) == 0 + && (tem = getenv(TMPDIR_ENVVAR)) == 0 +#if defined(__MSDOS__) || defined(_WIN32) + // If we didn't find a match for either of the above + // (which are preferred, regardless of the host operating system), + // and we are hosted on either MS-Windows or MS-DOS, + // then try the Microsoft conventions. + && (tem = getenv(WIN32_TMPDIR_ENVVAR)) == 0 + && (tem = getenv(MSDOS_TMPDIR_ENVVAR)) == 0 +#endif + ) + // If we didn't find an environment spec fall back to this default. + tem = DEFAULT_TMPDIR; + size_t tem_len = strlen(tem); + const char *tem_end = tem + tem_len - 1; + int need_slash = (strchr(DIR_SEPS, *tem_end) == 0) ? 1 : 0; + char *tem2 = new char[tem_len + need_slash + 1]; + strcpy(tem2, tem); + if (need_slash) + strcat(tem2, "/"); + const char *tem3 = TMPFILE_PREFIX_LONG; + if (file_name_max(tem2) <= 14) { + tem3 = TMPFILE_PREFIX_SHORT; + use_short_postfix = 1; + } + tmpfile_prefix_len = tem_len + need_slash + strlen(tem3); + tmpfile_prefix = new char[tmpfile_prefix_len + 1]; + strcpy(tmpfile_prefix, tem2); + strcat(tmpfile_prefix, tem3); + delete[] tem2; +} + +temp_init::~temp_init() +{ + delete[] tmpfile_prefix; +} + +/* + * Generate a temporary name template with a postfix + * immediately after the TMPFILE_PREFIX. + * It uses the groff preferences for a temporary directory. + * Note that no file name is either created or opened, + * only the *template* is returned. + */ + +char *xtmptemplate(const char *postfix_long, const char *postfix_short) +{ + const char *postfix = use_short_postfix ? postfix_short : postfix_long; + int postlen = 0; + if (postfix) + postlen = strlen(postfix); + char *templ = new char[tmpfile_prefix_len + postlen + 6 + 1]; + strcpy(templ, tmpfile_prefix); + if (postlen > 0) + strcat(templ, postfix); + strcat(templ, "XXXXXX"); + return templ; +} + +// The trick with unlinking the temporary file while it is still in +// use is not portable, it will fail on MS-DOS and most MS-Windows +// filesystems. So it cannot be used on non-Posix systems. +// Instead, we maintain a list of files to be deleted on exit. +// This should be portable to all platforms. + +struct xtmpfile_list { + char *fname; + xtmpfile_list *next; + xtmpfile_list(char *fn) : fname(fn), next(0) {} +}; + +xtmpfile_list *xtmpfiles_to_delete = 0; + +struct xtmpfile_list_init { + ~xtmpfile_list_init(); +} _xtmpfile_list_init; + +xtmpfile_list_init::~xtmpfile_list_init() +{ + xtmpfile_list *x = xtmpfiles_to_delete; + while (x != 0) { + if (unlink(x->fname) < 0) + error("cannot unlink '%1': %2", x->fname, strerror(errno)); + xtmpfile_list *tmp = x; + x = x->next; + delete[] tmp->fname; + delete tmp; + } +} + +static void add_tmp_file(const char *name) +{ + char *s = new char[strlen(name)+1]; + strcpy(s, name); + xtmpfile_list *x = new xtmpfile_list(s); + x->next = xtmpfiles_to_delete; + xtmpfiles_to_delete = x; +} + +// Open a temporary file and with fatal error on failure. + +FILE *xtmpfile(char **namep, + const char *postfix_long, const char *postfix_short, + int do_unlink) +{ + char *templ = xtmptemplate(postfix_long, postfix_short); + errno = 0; + int fd = mkstemp(templ); + if (fd < 0) + fatal("cannot create temporary file: %1", strerror(errno)); + errno = 0; + FILE *fp = fdopen(fd, FOPEN_RWB); // many callers of xtmpfile use binary I/O + if (!fp) + fatal("fdopen: %1", strerror(errno)); + if (do_unlink) + add_tmp_file(templ); + if (namep) + *namep = templ; + else + delete[] templ; + return fp; +} |