LCOV - code coverage report
Current view: top level - libs/libgroff - tmpfile.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 63 69 91.3 %
Date: 2026-01-16 17:51:41 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright 1989-2025 Free Software Foundation, Inc.
       2             :      Written by James Clark (jjc@jclark.com)
       3             : 
       4             : This file is part of groff, the GNU roff typesetting system.
       5             : 
       6             : groff is free software; you can redistribute it and/or modify it under
       7             : the terms of the GNU General Public License as published by the Free
       8             : Software Foundation, either version 3 of the License, or
       9             : (at your option) any later version.
      10             : 
      11             : groff is distributed in the hope that it will be useful, but WITHOUT ANY
      12             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14             : for more details.
      15             : 
      16             : You should have received a copy of the GNU General Public License
      17             : along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      18             : 
      19             : #ifdef HAVE_CONFIG_H
      20             : #include <config.h>
      21             : #endif
      22             : 
      23             : #include <errno.h>
      24             : #include <stdio.h> // FILE, fdopen()
      25             : #include <stdlib.h> // getenv(), mkstemp()
      26             : #include <string.h> // strcat(), strchr(), strcpy(), strerror(),
      27             :                     // strlen()
      28             : 
      29             : // needed for unlink()
      30             : #include "posix.h"
      31             : #include "nonposix.h"
      32             : 
      33             : #include "lib.h"
      34             : 
      35             : #include "posix.h"
      36             : #include "errarg.h"
      37             : #include "error.h"
      38             : #include "nonposix.h"
      39             : 
      40             : // If this is set, create temporary files there
      41             : #define GROFF_TMPDIR_ENVVAR "GROFF_TMPDIR"
      42             : // otherwise if this is set, create temporary files there
      43             : #define TMPDIR_ENVVAR "TMPDIR"
      44             : // otherwise, on MS-DOS or MS-Windows ...
      45             : #if defined(__MSDOS__) || defined(_WIN32)
      46             : // if either of these is set, create temporary files there
      47             : // (giving priority to WIN32_TMPDIR_ENVVAR)
      48             : #define WIN32_TMPDIR_ENVVAR "TMP"
      49             : #define MSDOS_TMPDIR_ENVVAR "TEMP"
      50             : #endif
      51             : // otherwise if P_tmpdir is defined, create temporary files there
      52             : #ifdef P_tmpdir
      53             : # define DEFAULT_TMPDIR P_tmpdir
      54             : #else
      55             : // otherwise create temporary files here.
      56             : # define DEFAULT_TMPDIR "/tmp"
      57             : #endif
      58             : // Use this as the prefix for temporary filenames.
      59             : #define TMPFILE_PREFIX_SHORT ""
      60             : #define TMPFILE_PREFIX_LONG "groff"
      61             : 
      62             : char *tmpfile_prefix;
      63             : size_t tmpfile_prefix_len;
      64             : int use_short_postfix = 0;
      65             : 
      66             : struct temp_init {
      67             :   temp_init();
      68             :   ~temp_init();
      69             : } _temp_init;
      70             : 
      71         100 : temp_init::temp_init()
      72             : {
      73             :   // First, choose a location for creating temporary files...
      74             :   const char *tem;
      75             :   // using the first match for any of the environment specs in listed order.
      76         100 :   if (
      77         100 :       (tem = getenv(GROFF_TMPDIR_ENVVAR)) == 0
      78         100 :       && (tem = getenv(TMPDIR_ENVVAR)) == 0
      79             : #if defined(__MSDOS__) || defined(_WIN32)
      80             :       // If we didn't find a match for either of the above
      81             :       // (which are preferred, regardless of the host operating system),
      82             :       // and we are hosted on either MS-Windows or MS-DOS,
      83             :       // then try the Microsoft conventions.
      84             :       && (tem = getenv(WIN32_TMPDIR_ENVVAR)) == 0
      85             :       && (tem = getenv(MSDOS_TMPDIR_ENVVAR)) == 0
      86             : #endif
      87             :      )
      88             :     // If we didn't find an environment spec fall back to this default.
      89           0 :     tem = DEFAULT_TMPDIR;
      90         100 :   size_t tem_len = strlen(tem);
      91         100 :   const char *tem_end = tem + tem_len - 1;
      92         100 :   int need_slash = (strchr(DIR_SEPS, *tem_end) == 0) ? 1 : 0;
      93         100 :   char *tem2 = new char[tem_len + need_slash + 1];
      94         100 :   strcpy(tem2, tem);
      95         100 :   if (need_slash)
      96         100 :     strcat(tem2, "/");
      97         100 :   const char *tem3 = TMPFILE_PREFIX_LONG;
      98         100 :   if (file_name_max(tem2) <= 14) {
      99           0 :     tem3 = TMPFILE_PREFIX_SHORT;
     100           0 :     use_short_postfix = 1;
     101             :   }
     102         100 :   tmpfile_prefix_len = tem_len + need_slash + strlen(tem3);
     103         100 :   tmpfile_prefix = new char[tmpfile_prefix_len + 1];
     104         100 :   strcpy(tmpfile_prefix, tem2);
     105         100 :   strcat(tmpfile_prefix, tem3);
     106         100 :   delete[] tem2;
     107         100 : }
     108             : 
     109         100 : temp_init::~temp_init()
     110             : {
     111         100 :   delete[] tmpfile_prefix;
     112         100 : }
     113             : 
     114             : /*
     115             :  *  Generate a temporary name template with a postfix
     116             :  *  immediately after the TMPFILE_PREFIX.
     117             :  *  It uses the groff preferences for a temporary directory.
     118             :  *  Note that no file name is either created or opened,
     119             :  *  only the *template* is returned.
     120             :  */
     121             : 
     122         183 : char *xtmptemplate(const char *postfix_long, const char *postfix_short)
     123             : {
     124         183 :   const char *postfix = use_short_postfix ? postfix_short : postfix_long;
     125         183 :   int postlen = 0;
     126         183 :   if (postfix)
     127          68 :     postlen = strlen(postfix);
     128         183 :   char *templ = new char[tmpfile_prefix_len + postlen + 6 + 1];
     129         183 :   strcpy(templ, tmpfile_prefix);
     130         183 :   if (postlen > 0)
     131          68 :     strcat(templ, postfix);
     132         183 :   strcat(templ, "XXXXXX");
     133         183 :   return templ;
     134             : }
     135             : 
     136             : // The trick with unlinking the temporary file while it is still in
     137             : // use is not portable, it will fail on MS-DOS and most MS-Windows
     138             : // filesystems.  So it cannot be used on non-Posix systems.
     139             : // Instead, we maintain a list of files to be deleted on exit.
     140             : // This should be portable to all platforms.
     141             : 
     142             : struct xtmpfile_list {
     143             :   char *fname;
     144             :   xtmpfile_list *next;
     145         183 :   xtmpfile_list(char *fn) : fname(fn), next(0) {}
     146             : };
     147             : 
     148             : xtmpfile_list *xtmpfiles_to_delete = 0;
     149             : 
     150             : struct xtmpfile_list_init {
     151             :   ~xtmpfile_list_init();
     152             : } _xtmpfile_list_init;
     153             : 
     154         100 : xtmpfile_list_init::~xtmpfile_list_init()
     155             : {
     156         100 :   xtmpfile_list *x = xtmpfiles_to_delete;
     157         283 :   while (x != 0) {
     158         183 :     if (unlink(x->fname) < 0)
     159           0 :       error("cannot unlink '%1': %2", x->fname, strerror(errno));
     160         183 :     xtmpfile_list *tmp = x;
     161         183 :     x = x->next;
     162         183 :     delete[] tmp->fname;
     163         183 :     delete tmp;
     164             :   }
     165         100 : }
     166             : 
     167         183 : static void add_tmp_file(const char *name)
     168             : {
     169         183 :   char *s = new char[strlen(name)+1];
     170         183 :   strcpy(s, name);
     171         183 :   xtmpfile_list *x = new xtmpfile_list(s);
     172         183 :   x->next = xtmpfiles_to_delete;
     173         183 :   xtmpfiles_to_delete = x;
     174         183 : }
     175             : 
     176             : // Open a temporary file; any failure is fatal.
     177             : 
     178         183 : FILE *xtmpfile(const char **namep,
     179             :                const char *postfix_long, const char *postfix_short)
     180             : {
     181             :   // `xtmptemplate()` allocates storage for `templ` with `new`.
     182         183 :   char *templ = xtmptemplate(postfix_long, postfix_short);
     183         183 :   errno = 0;
     184         183 :   int fd = mkstemp(templ);
     185         183 :   if (fd < 0)
     186           0 :     fatal("cannot create temporary file: %1", strerror(errno));
     187         183 :   errno = 0;
     188             :  // Many of our callers use binary I/O.
     189         183 :   FILE *fp = fdopen(fd, FOPEN_RWB);
     190         183 :   if (!fp)
     191           0 :     fatal("cannot open temporary file: %1", strerror(errno));
     192         183 :   add_tmp_file(templ);
     193         183 :   if (namep != 0 /* nullptr */)
     194          68 :     *namep = templ;
     195             :   else
     196         115 :     delete[] templ;
     197         183 :   return fp;
     198             : }
     199             : 
     200             : // Local Variables:
     201             : // fill-column: 72
     202             : // mode: C++
     203             : // End:
     204             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14