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-2024, 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 *
3827 tgl@sss.pgh.pa.us 46 :CBC 2115390 : psprintf(const char *fmt,...)
47 : : {
2027 48 : 2115390 : int save_errno = errno;
3827 49 : 2115390 : size_t len = 128; /* initial assumption about buffer size */
50 : :
51 : : for (;;)
52 : 137621 : {
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 : 2253011 : result = (char *) palloc(len);
62 : :
63 : : /* Try to format the data. */
2027 64 : 2253011 : errno = save_errno;
3827 65 : 2253011 : va_start(args, fmt);
3825 66 : 2253011 : newlen = pvsnprintf(result, len, fmt, args);
3827 67 : 2253011 : va_end(args);
68 : :
3825 69 [ + + ]: 2253011 : if (newlen < len)
3827 70 : 2115390 : return result; /* success */
71 : :
72 : : /* Release buffer and loop around to try again with larger len. */
73 : 137621 : pfree(result);
3825 74 : 137621 : 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
3827 106 : 15181108 : pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
107 : : {
108 : : int nprinted;
109 : :
110 : 15181108 : nprinted = vsnprintf(buf, len, fmt, args);
111 : :
112 : : /* We assume failure means the fmt is bogus, hence hard failure is OK */
2068 113 [ - + ]: 15181108 : if (unlikely(nprinted < 0))
114 : : {
115 : : #ifndef FRONTEND
1956 tgl@sss.pgh.pa.us 116 [ # # ]:UBC 0 : elog(ERROR, "vsnprintf failed: %m with format string \"%s\"", fmt);
117 : : #else
33 michael@paquier.xyz 118 :UNC 0 : fprintf(stderr, "vsnprintf failed: %m with format string \"%s\"\n",
119 : : fmt);
3827 tgl@sss.pgh.pa.us 120 :UBC 0 : exit(EXIT_FAILURE);
121 : : #endif
122 : : }
123 : :
2068 tgl@sss.pgh.pa.us 124 [ + + ]:CBC 15181108 : if ((size_t) nprinted < len)
125 : : {
126 : : /* Success. Note nprinted does not include trailing null. */
3825 127 : 15032661 : 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 : : */
2068 138 [ - + ]: 148447 : if (unlikely((size_t) nprinted > MaxAllocSize - 1))
139 : : {
140 : : #ifndef FRONTEND
3827 tgl@sss.pgh.pa.us 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 : :
2068 tgl@sss.pgh.pa.us 150 :CBC 148447 : return nprinted + 1;
151 : : }
|