unistr/u{8,16,32}-uctomb: Avoid possible trouble with huge strings.
[gnulib.git] / lib / tmpfile.c
blobbdabe27d4b59e4a85a53af41ac7169bff0f5b5b8
1 /* Create a temporary file.
2 Copyright (C) 2007, 2009-2020 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Ben Pfaff. */
19 #include <config.h>
21 /* Specification. */
22 #include <stdio.h>
24 #include <errno.h>
25 #include <stdbool.h>
27 #if defined _WIN32 && ! defined __CYGWIN__
28 /* A native Windows platform. */
30 # include <fcntl.h>
31 # include <string.h>
32 # include <sys/stat.h>
34 # include <io.h>
36 # define WIN32_LEAN_AND_MEAN /* avoid including junk */
37 # include <windows.h>
39 #else
41 # include <unistd.h>
43 #endif
45 #include "pathmax.h"
46 #include "tempname.h"
47 #include "tmpdir.h"
49 /* PATH_MAX is guaranteed to be defined, because this replacement is only
50 used on native Windows and Android. */
52 #if defined _WIN32 && ! defined __CYGWIN__
53 /* A native Windows platform. */
55 /* Don't assume that UNICODE is not defined. */
56 # undef OSVERSIONINFO
57 # define OSVERSIONINFO OSVERSIONINFOA
58 # undef GetVersionEx
59 # define GetVersionEx GetVersionExA
60 # undef GetTempPath
61 # define GetTempPath GetTempPathA
63 /* On Windows, opening a file with _O_TEMPORARY has the effect of passing
64 the FILE_FLAG_DELETE_ON_CLOSE flag to CreateFile(), which has the effect
65 of deleting the file when it is closed - even when the program crashes.
66 But (according to the Cygwin sources) it works only on Windows NT or newer.
67 So we cache the info whether we are running on Windows NT or newer. */
69 static bool
70 supports_delete_on_close ()
72 static int known; /* 1 = yes, -1 = no, 0 = unknown */
73 if (!known)
75 OSVERSIONINFO v;
77 /* According to
78 <https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getversionexa>
79 this structure must be initialized as follows: */
80 v.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
82 if (GetVersionEx (&v))
83 known = (v.dwPlatformId == VER_PLATFORM_WIN32_NT ? 1 : -1);
84 else
85 known = -1;
87 return (known > 0);
90 FILE *
91 tmpfile (void)
93 char dir[PATH_MAX];
94 DWORD retval;
96 /* Find Windows temporary file directory.
97 We provide this as the directory argument to path_search because Windows
98 defines P_tmpdir to "\\" and will therefore try to put all temporary files
99 in the root directory (unless $TMPDIR is set). */
100 retval = GetTempPath (PATH_MAX, dir);
101 if (retval > 0 && retval < PATH_MAX)
103 char xtemplate[PATH_MAX];
105 if (path_search (xtemplate, PATH_MAX, dir, NULL, true) >= 0)
107 size_t len = strlen (xtemplate);
108 int o_temporary = (supports_delete_on_close () ? _O_TEMPORARY : 0);
109 int fd;
113 memcpy (&xtemplate[len - 6], "XXXXXX", 6);
114 if (gen_tempname (xtemplate, 0, 0, GT_NOCREATE) < 0)
116 fd = -1;
117 break;
120 fd = _open (xtemplate,
121 _O_CREAT | _O_EXCL | o_temporary
122 | _O_RDWR | _O_BINARY,
123 _S_IREAD | _S_IWRITE);
125 while (fd < 0 && errno == EEXIST);
127 if (fd >= 0)
129 FILE *fp = _fdopen (fd, "w+b");
131 if (fp != NULL)
132 return fp;
133 else
135 int saved_errno = errno;
136 _close (fd);
137 errno = saved_errno;
142 else
144 if (retval > 0)
145 errno = ENAMETOOLONG;
146 else
147 /* Ideally this should translate GetLastError () to an errno value. */
148 errno = ENOENT;
151 return NULL;
154 #else
156 FILE *
157 tmpfile (void)
159 char buf[PATH_MAX];
160 int fd;
161 FILE *fp;
163 /* Try $TMPDIR first, not /tmp nor P_tmpdir, because we need this replacement
164 on Android, and /tmp does not exist on Android. */
166 if (path_search (buf, sizeof buf, NULL, "tmpf", true))
167 return NULL;
169 fd = gen_tempname (buf, 0, 0, GT_FILE);
170 if (fd < 0)
171 return NULL;
173 /* Note that this relies on the Unix semantics that
174 a file is not really removed until it is closed. */
175 (void) unlink (buf);
177 if ((fp = fdopen (fd, "w+b")) == NULL)
179 int saved_errno = errno;
180 close (fd);
181 errno = saved_errno;
184 return fp;
187 #endif