Age Owner Branch data 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-2025, 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 : : #endif
28 : :
29 : :
30 : : /*
31 : : * psprintf
32 : : *
33 : : * Format text data under the control of fmt (an sprintf-style format string)
34 : : * and return it in an allocated-on-demand buffer. The buffer is allocated
35 : : * with palloc in the backend, or malloc in frontend builds. Caller is
36 : : * responsible to free the buffer when no longer needed, if appropriate.
37 : : *
38 : : * Errors are not returned to the caller, but are reported via elog(ERROR)
39 : : * in the backend, or printf-to-stderr-and-exit() in frontend builds.
40 : : * One should therefore think twice about using this in libpq.
41 : : */
42 : : char *
4337 tgl@sss.pgh.pa.us 43 :CBC 504665 : psprintf(const char *fmt,...)
44 : : {
2537 45 : 504665 : int save_errno = errno;
4337 46 : 504665 : size_t len = 128; /* initial assumption about buffer size */
47 : :
48 : : for (;;)
49 : 136524 : {
50 : : char *result;
51 : : va_list args;
52 : : size_t newlen;
53 : :
54 : : /*
55 : : * Allocate result buffer. Note that in frontend this maps to malloc
56 : : * with exit-on-error.
57 : : */
58 : 641189 : result = (char *) palloc(len);
59 : :
60 : : /* Try to format the data. */
2537 61 : 641189 : errno = save_errno;
4337 62 : 641189 : va_start(args, fmt);
4335 63 : 641189 : newlen = pvsnprintf(result, len, fmt, args);
4337 64 : 641189 : va_end(args);
65 : :
4335 66 [ + + ]: 641189 : if (newlen < len)
4337 67 : 504665 : return result; /* success */
68 : :
69 : : /* Release buffer and loop around to try again with larger len. */
70 : 136524 : pfree(result);
4335 71 : 136524 : len = newlen;
72 : : }
73 : : }
74 : :
75 : : /*
76 : : * pvsnprintf
77 : : *
78 : : * Attempt to format text data under the control of fmt (an sprintf-style
79 : : * format string) and insert it into buf (which has length len).
80 : : *
81 : : * If successful, return the number of bytes emitted, not counting the
82 : : * trailing zero byte. This will always be strictly less than len.
83 : : *
84 : : * If there's not enough space in buf, return an estimate of the buffer size
85 : : * needed to succeed (this *must* be more than the given len, else callers
86 : : * might loop infinitely).
87 : : *
88 : : * Other error cases do not return, but exit via elog(ERROR) or exit().
89 : : * Hence, this shouldn't be used inside libpq.
90 : : *
91 : : * Caution: callers must be sure to preserve their entry-time errno
92 : : * when looping, in case the fmt contains "%m".
93 : : *
94 : : * Note that the semantics of the return value are not exactly C99's.
95 : : * First, we don't promise that the estimated buffer size is exactly right;
96 : : * callers must be prepared to loop multiple times to get the right size.
97 : : * (Given a C99-compliant vsnprintf, that won't happen, but it is rumored
98 : : * that some implementations don't always return the same value ...)
99 : : * Second, we return the recommended buffer size, not one less than that;
100 : : * this lets overflow concerns be handled here rather than in the callers.
101 : : */
102 : : size_t
4337 103 : 15096156 : pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
104 : : {
105 : : int nprinted;
106 : :
107 : 15096156 : nprinted = vsnprintf(buf, len, fmt, args);
108 : :
109 : : /* We assume failure means the fmt is bogus, hence hard failure is OK */
2578 110 [ - + ]: 15096156 : if (unlikely(nprinted < 0))
111 : : {
112 : : #ifndef FRONTEND
2466 tgl@sss.pgh.pa.us 113 [ # # ]:UBC 0 : elog(ERROR, "vsnprintf failed: %m with format string \"%s\"", fmt);
114 : : #else
543 michael@paquier.xyz 115 : 0 : fprintf(stderr, "vsnprintf failed: %m with format string \"%s\"\n",
116 : : fmt);
4337 tgl@sss.pgh.pa.us 117 : 0 : exit(EXIT_FAILURE);
118 : : #endif
119 : : }
120 : :
2578 tgl@sss.pgh.pa.us 121 [ + + ]:CBC 15096156 : if ((size_t) nprinted < len)
122 : : {
123 : : /* Success. Note nprinted does not include trailing null. */
4335 124 : 14942676 : return (size_t) nprinted;
125 : : }
126 : :
127 : : /*
128 : : * We assume a C99-compliant vsnprintf, so believe its estimate of the
129 : : * required space, and add one for the trailing null. (If it's wrong, the
130 : : * logic will still work, but we may loop multiple times.)
131 : : *
132 : : * Choke if the required space would exceed MaxAllocSize. Note we use
133 : : * this palloc-oriented overflow limit even when in frontend.
134 : : */
2578 135 [ - + ]: 153480 : if (unlikely((size_t) nprinted > MaxAllocSize - 1))
136 : : {
137 : : #ifndef FRONTEND
4337 tgl@sss.pgh.pa.us 138 [ # # ]:UBC 0 : ereport(ERROR,
139 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
140 : : errmsg("out of memory")));
141 : : #else
142 : 0 : fprintf(stderr, _("out of memory\n"));
143 : 0 : exit(EXIT_FAILURE);
144 : : #endif
145 : : }
146 : :
2578 tgl@sss.pgh.pa.us 147 :CBC 153480 : return nprinted + 1;
148 : : }
|