/contrib/groff/src/libs/libgroff/tmpfile.cpp

https://bitbucket.org/freebsd/freebsd-head/ · C++ · 190 lines · 124 code · 18 blank · 48 comment · 22 complexity · dc2a2a7163033cdbbac1fa1e91d3d3b5 MD5 · raw file

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