Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * psprintf.c
4 : * sprintf into an allocated-on-demand buffer
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2023, 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 *
3456 tgl 46 CBC 2897502 : psprintf(const char *fmt,...)
47 : {
1656 48 2897502 : int save_errno = errno;
3456 49 2897502 : size_t len = 128; /* initial assumption about buffer size */
50 :
51 : for (;;)
52 9439 : {
53 : char *result;
54 : va_list args;
55 : size_t newlen;
56 :
57 : /*
58 : * Allocate result buffer. Note that in frontend this maps to malloc
59 : * with exit-on-error.
60 : */
61 2906941 : result = (char *) palloc(len);
62 :
63 : /* Try to format the data. */
1656 64 2906941 : errno = save_errno;
3456 65 2906941 : va_start(args, fmt);
3454 66 2906941 : newlen = pvsnprintf(result, len, fmt, args);
3456 67 2906941 : va_end(args);
68 :
3454 69 2906941 : if (newlen < len)
3456 70 2897502 : return result; /* success */
71 :
72 : /* Release buffer and loop around to try again with larger len. */
73 9439 : pfree(result);
3454 74 9439 : len = newlen;
75 : }
76 : }
77 :
78 : /*
79 : * pvsnprintf
80 : *
81 : * Attempt to format text data under the control of fmt (an sprintf-style
82 : * format string) and insert it into buf (which has length len).
83 : *
84 : * If successful, return the number of bytes emitted, not counting the
85 : * trailing zero byte. This will always be strictly less than len.
86 : *
87 : * If there's not enough space in buf, return an estimate of the buffer size
88 : * needed to succeed (this *must* be more than the given len, else callers
89 : * might loop infinitely).
90 : *
91 : * Other error cases do not return, but exit via elog(ERROR) or exit().
92 : * Hence, this shouldn't be used inside libpq.
93 : *
94 : * Caution: callers must be sure to preserve their entry-time errno
95 : * when looping, in case the fmt contains "%m".
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 : * (Given a C99-compliant vsnprintf, that won't happen, but it is rumored
101 : * that some implementations don't always return the same value ...)
102 : * Second, we return the recommended buffer size, not one less than that;
103 : * this lets overflow concerns be handled here rather than in the callers.
104 : */
105 : size_t
3456 106 59911571 : pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
107 : {
108 : int nprinted;
109 :
110 59911571 : nprinted = vsnprintf(buf, len, fmt, args);
111 :
112 : /* We assume failure means the fmt is bogus, hence hard failure is OK */
1697 113 59911571 : if (unlikely(nprinted < 0))
114 : {
115 : #ifndef FRONTEND
1585 tgl 116 UBC 0 : elog(ERROR, "vsnprintf failed: %m with format string \"%s\"", fmt);
117 : #else
1565 118 0 : fprintf(stderr, "vsnprintf failed: %s with format string \"%s\"\n",
119 0 : strerror(errno), fmt);
3456 120 0 : exit(EXIT_FAILURE);
121 : #endif
122 : }
123 :
1697 tgl 124 CBC 59911571 : if ((size_t) nprinted < len)
125 : {
126 : /* Success. Note nprinted does not include trailing null. */
3454 127 59891794 : return (size_t) nprinted;
128 : }
129 :
130 : /*
131 : * We assume a C99-compliant vsnprintf, so believe its estimate of the
132 : * required space, and add one for the trailing null. (If it's wrong, the
133 : * logic will still work, but we may loop multiple times.)
134 : *
135 : * Choke if the required space would exceed MaxAllocSize. Note we use
136 : * this palloc-oriented overflow limit even when in frontend.
137 : */
1697 138 19777 : if (unlikely((size_t) nprinted > MaxAllocSize - 1))
139 : {
140 : #ifndef FRONTEND
3456 tgl 141 UBC 0 : ereport(ERROR,
142 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
143 : errmsg("out of memory")));
144 : #else
145 0 : fprintf(stderr, _("out of memory\n"));
146 0 : exit(EXIT_FAILURE);
147 : #endif
148 : }
149 :
1697 tgl 150 CBC 19777 : return nprinted + 1;
151 : }
|