1 /*-------------------------------------------------------------------------
2 *
3 * psprintf.c
4 * sprintf into an allocated-on-demand buffer
5 *
6 *
7 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 *
11 * IDENTIFICATION
12 * src/common/psprintf.c
13 *
14 *-------------------------------------------------------------------------
15 */
16
17 #ifndef FRONTEND
18
19 #include "postgres.h"
20
21 #include "utils/memutils.h"
22
23 #else
24
25 #include "postgres_fe.h"
26
27 /* It's possible we could use a different value for this in frontend code */
28 #define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
29
30 #endif
31
32
33 /*
34 * psprintf
35 *
36 * Format text data under the control of fmt (an sprintf-style format string)
37 * and return it in an allocated-on-demand buffer. The buffer is allocated
38 * with palloc in the backend, or malloc in frontend builds. Caller is
39 * responsible to free the buffer when no longer needed, if appropriate.
40 *
41 * Errors are not returned to the caller, but are reported via elog(ERROR)
42 * in the backend, or printf-to-stderr-and-exit() in frontend builds.
43 * One should therefore think twice about using this in libpq.
44 */
45 char *
psprintf(const char * fmt,...)46 psprintf(const char *fmt,...)
47 {
48 size_t len = 128; /* initial assumption about buffer size */
49
50 for (;;)
51 {
52 char *result;
53 va_list args;
54 size_t newlen;
55
56 /*
57 * Allocate result buffer. Note that in frontend this maps to malloc
58 * with exit-on-error.
59 */
60 result = (char *) palloc(len);
61
62 /* Try to format the data. */
63 va_start(args, fmt);
64 newlen = pvsnprintf(result, len, fmt, args);
65 va_end(args);
66
67 if (newlen < len)
68 return result; /* success */
69
70 /* Release buffer and loop around to try again with larger len. */
71 pfree(result);
72 len = newlen;
73 }
74 }
75
76 /*
77 * pvsnprintf
78 *
79 * Attempt to format text data under the control of fmt (an sprintf-style
80 * format string) and insert it into buf (which has length len, len > 0).
81 *
82 * If successful, return the number of bytes emitted, not counting the
83 * trailing zero byte. This will always be strictly less than len.
84 *
85 * If there's not enough space in buf, return an estimate of the buffer size
86 * needed to succeed (this *must* be more than the given len, else callers
87 * might loop infinitely).
88 *
89 * Other error cases do not return, but exit via elog(ERROR) or exit().
90 * Hence, this shouldn't be used inside libpq.
91 *
92 * This function exists mainly to centralize our workarounds for
93 * non-C99-compliant vsnprintf implementations. Generally, any call that
94 * pays any attention to the return value should go through here rather
95 * than calling snprintf or vsnprintf directly.
96 *
97 * Note that the semantics of the return value are not exactly C99's.
98 * First, we don't promise that the estimated buffer size is exactly right;
99 * callers must be prepared to loop multiple times to get the right size.
100 * Second, we return the recommended buffer size, not one less than that;
101 * this lets overflow concerns be handled here rather than in the callers.
102 */
103 size_t
pvsnprintf(char * buf,size_t len,const char * fmt,va_list args)104 pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
105 {
106 int nprinted;
107
108 Assert(len > 0);
109
110 errno = 0;
111
112 /*
113 * Assert check here is to catch buggy vsnprintf that overruns the
114 * specified buffer length. Solaris 7 in 64-bit mode is an example of a
115 * platform with such a bug.
116 */
117 #ifdef USE_ASSERT_CHECKING
118 buf[len - 1] = '\0';
119 #endif
120
121 nprinted = vsnprintf(buf, len, fmt, args);
122
123 Assert(buf[len - 1] == '\0');
124
125 /*
126 * If vsnprintf reports an error other than ENOMEM, fail. The possible
127 * causes of this are not user-facing errors, so elog should be enough.
128 */
129 if (nprinted < 0 && errno != 0 && errno != ENOMEM)
130 {
131 #ifndef FRONTEND
132 elog(ERROR, "vsnprintf failed: %m with format string \"%s\"", fmt);
133 #else
134 fprintf(stderr, "vsnprintf failed: %s with format string \"%s\"\n",
135 strerror(errno), fmt);
136 exit(EXIT_FAILURE);
137 #endif
138 }
139
140 /*
141 * Note: some versions of vsnprintf return the number of chars actually
142 * stored, not the total space needed as C99 specifies. And at least one
143 * returns -1 on failure. Be conservative about believing whether the
144 * print worked.
145 */
146 if (nprinted >= 0 && (size_t) nprinted < len - 1)
147 {
148 /* Success. Note nprinted does not include trailing null. */
149 return (size_t) nprinted;
150 }
151
152 if (nprinted >= 0 && (size_t) nprinted > len)
153 {
154 /*
155 * This appears to be a C99-compliant vsnprintf, so believe its
156 * estimate of the required space. (If it's wrong, the logic will
157 * still work, but we may loop multiple times.) Note that the space
158 * needed should be only nprinted+1 bytes, but we'd better allocate
159 * one more than that so that the test above will succeed next time.
160 *
161 * In the corner case where the required space just barely overflows,
162 * fall through so that we'll error out below (possibly after
163 * looping).
164 */
165 if ((size_t) nprinted <= MaxAllocSize - 2)
166 return nprinted + 2;
167 }
168
169 /*
170 * Buffer overrun, and we don't know how much space is needed. Estimate
171 * twice the previous buffer size, but not more than MaxAllocSize; if we
172 * are already at MaxAllocSize, choke. Note we use this palloc-oriented
173 * overflow limit even when in frontend.
174 */
175 if (len >= MaxAllocSize)
176 {
177 #ifndef FRONTEND
178 ereport(ERROR,
179 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
180 errmsg("out of memory")));
181 #else
182 fprintf(stderr, _("out of memory\n"));
183 exit(EXIT_FAILURE);
184 #endif
185 }
186
187 if (len >= MaxAllocSize / 2)
188 return MaxAllocSize;
189
190 return len * 2;
191 }
192