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 : #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 *
43 4080352 : psprintf(const char *fmt,...)
44 : {
45 4080352 : int save_errno = errno;
46 4080352 : size_t len = 128; /* initial assumption about buffer size */
47 :
48 : for (;;)
49 40422 : {
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 4120774 : result = (char *) palloc(len);
59 :
60 : /* Try to format the data. */
61 4120774 : errno = save_errno;
62 4120774 : va_start(args, fmt);
63 4120774 : newlen = pvsnprintf(result, len, fmt, args);
64 4120774 : va_end(args);
65 :
66 4120774 : if (newlen < len)
67 4080352 : return result; /* success */
68 :
69 : /* Release buffer and loop around to try again with larger len. */
70 40422 : pfree(result);
71 40422 : 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
103 401633950 : pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
104 : {
105 : int nprinted;
106 :
107 401633950 : nprinted = vsnprintf(buf, len, fmt, args);
108 :
109 : /* We assume failure means the fmt is bogus, hence hard failure is OK */
110 401633950 : if (unlikely(nprinted < 0))
111 : {
112 : #ifndef FRONTEND
113 0 : elog(ERROR, "vsnprintf failed: %m with format string \"%s\"", fmt);
114 : #else
115 0 : fprintf(stderr, "vsnprintf failed: %m with format string \"%s\"\n",
116 : fmt);
117 0 : exit(EXIT_FAILURE);
118 : #endif
119 : }
120 :
121 401633950 : if ((size_t) nprinted < len)
122 : {
123 : /* Success. Note nprinted does not include trailing null. */
124 401483394 : 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 : */
135 150556 : if (unlikely((size_t) nprinted > MaxAllocSize - 1))
136 : {
137 : #ifndef FRONTEND
138 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 :
147 150556 : return nprinted + 1;
148 : }
|