1 /*
2 * snprintf.c - Implement snprintf and vsnprintf on platforms that need them.
3 */
4
5 /*
6 * Copyright (C) 2006, 2007, 2018, 2022 the Free Software Foundation, Inc.
7 *
8 * This file is part of GAWK, the GNU implementation of the
9 * AWK Programming Language.
10 *
11 * GAWK is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * GAWK is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26
27 /* If using in a multi-threaded context, then SNPRINTF_REENTRANT must be
28 defined. But in that case, performance will be much worse, since a
29 temporary file is created and closed for each call to snprintf. */
30
31 #if defined(HAVE_MKSTEMP)
32 /* If mkstemp is available, use it instead of tmpfile(), since some older
33 implementations of tmpfile() were not secure. */
34
35 static char *tmpfilename = NULL;
36 static FILE *safe_f = NULL;
37
38 #ifdef HAVE_ATEXIT
39 static void close_safe_f()
40 {
41 if (safe_f != NULL) {
42 fclose(safe_f);
43 safe_f = NULL;
44 }
45 if (tmpfilename != NULL) {
46 unlink(tmpfilename);
47 free(tmpfilename);
48 tmpfilename = NULL;
49 }
50 }
51 #endif
52
53 static FILE *
54 safe_tmpfile (void)
55 {
56 static bool first = true;
57 static const char template[] = "snprintfXXXXXX";
58 int fd;
59 static char *tmpdir = NULL;
60 static int len = 0;
61
62 if (first) {
63 first = false;
64 /*
65 * First try Unix stanadard env var, then Windows var,
66 * then fall back to /tmp.
67 */
68 if ((tmpdir = getenv("TMPDIR")) != NULL && *tmpdir != '\0')
69 ; /* got it */
70 else if ((tmpdir = getenv("TEMP")) != NULL && *tmpdir != '\0')
71 ; /* got it */
72 else
73 tmpdir = "/tmp";
74
75 len = strlen(tmpdir) + 1 + strlen(template) + 1;
76 #ifdef HAVE_ATEXIT
77 atexit(close_safe_f);
78 #endif /* HAVE_ATEXIT */
79 }
80
81 if ((tmpfilename = (char *) malloc(len)) == NULL)
82 return NULL;
83 else
84 sprintf(tmpfilename, "%s/%s", tmpdir, template);
85
86 if ((fd = mkstemp (tmpfilename)) < 0)
87 return NULL;
88
89 #if ! defined(MSDOS) && ! defined(_MSC_VER) \
90 && ! defined(_WIN32) && ! defined(__CRTRSXNT__) \
91 && ! defined(__MINGW32__) && ! defined(__WIN32__)
92 /* If not MS unlink after opening. */
93 unlink (tmpfilename);
94 free(tmpfilename);
95 tmpfilename = NULL;
96 #endif
97
98 if ((safe_f = fdopen (fd, "w+b")) == NULL) {
99 close (fd);
100 return NULL;
101 }
102 /* setvbuf(f,NULL,_IOFBF,4*BUFSIZ); */
103 return safe_f;
104 }
105
106 #elif defined(HAVE_TMPFILE)
107 #define safe_tmpfile tmpfile
108 #else
109 #error Neither mkstemp() nor tmpfile() is available on this platform.
110 #endif
111
112 #if (__STDC_VERSION__ + 0) < 199901
113 #undef restrict /* force it! */
114 #define restrict
115 #endif
116
117 int
118 vsnprintf (char *restrict buf, size_t len,
119 const char *restrict fmt, va_list args)
120 {
121 int actual;
122 int nread;
123 size_t cnt = 0;
124 #ifndef SNPRINTF_REENTRANT
125 static
126 #endif
127 FILE *fp;
128
129 if ((buf == NULL) || (len < 1))
130 return -1;
131
132 buf[0] = '\0'; /* in case the caller does not check the return code! */
133
134 #ifdef SNPRINTF_REENTRANT
135 if ((fp = safe_tmpfile ()) == NULL)
136 return -1;
137 #else
138 if ((fp == NULL) && ((fp = safe_tmpfile ()) == NULL))
139 return -1;
140 rewind (fp);
141 #endif
142 actual = vfprintf (fp, fmt, args);
143 rewind (fp);
144 if (actual < 0) {
145 #ifdef SNPRINTF_REENTRANT
146 fclose (fp);
147 if (tmpfilename != NULL) {
148 unlink(tmpfilename);
149 free(tmpfilename);
150 tmpfilename = NULL;
151 }
152 #endif
153 return -1;
154 }
155 else if ((size_t) actual < len)
156 len = actual;
157 else
158 --len;
159 while (cnt < len && (nread = fread (buf + cnt, 1, len - cnt, fp)) > 0)
160 cnt += nread;
161 buf[cnt] = '\0';
162 #ifdef SNPRINTF_REENTRANT
163 fclose (fp);
164 if (tmpfilename != NULL) {
165 unlink(tmpfilename);
166 free(tmpfilename);
167 tmpfilename = NULL;
168 }
169 #endif
170 if (cnt < len)
171 return -1;
172
173 return actual;
174 }
175
176 int
177 snprintf (char *restrict buf, size_t len, const char *restrict fmt, ...)
178 {
179 int rv;
180 va_list args;
181
182 va_start (args, fmt);
183 rv = vsnprintf (buf, len, fmt, args);
184 va_end (args);
185 return rv;
186 }