1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * psprintf.c |
4 | * sprintf into an allocated-on-demand buffer |
5 | * |
6 | * |
7 | * Portions Copyright (c) 1996-2019, 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 * |
46 | psprintf(const char *fmt,...) |
47 | { |
48 | int save_errno = errno; |
49 | size_t len = 128; /* initial assumption about buffer size */ |
50 | |
51 | for (;;) |
52 | { |
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 | result = (char *) palloc(len); |
62 | |
63 | /* Try to format the data. */ |
64 | errno = save_errno; |
65 | va_start(args, fmt); |
66 | newlen = pvsnprintf(result, len, fmt, args); |
67 | va_end(args); |
68 | |
69 | if (newlen < len) |
70 | return result; /* success */ |
71 | |
72 | /* Release buffer and loop around to try again with larger len. */ |
73 | pfree(result); |
74 | 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 |
106 | pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) |
107 | { |
108 | int nprinted; |
109 | |
110 | nprinted = vsnprintf(buf, len, fmt, args); |
111 | |
112 | /* We assume failure means the fmt is bogus, hence hard failure is OK */ |
113 | if (unlikely(nprinted < 0)) |
114 | { |
115 | #ifndef FRONTEND |
116 | elog(ERROR, "vsnprintf failed: %m with format string \"%s\"" , fmt); |
117 | #else |
118 | fprintf(stderr, "vsnprintf failed: %s with format string \"%s\"\n" , |
119 | strerror(errno), fmt); |
120 | exit(EXIT_FAILURE); |
121 | #endif |
122 | } |
123 | |
124 | if ((size_t) nprinted < len) |
125 | { |
126 | /* Success. Note nprinted does not include trailing null. */ |
127 | 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 | */ |
138 | if (unlikely((size_t) nprinted > MaxAllocSize - 1)) |
139 | { |
140 | #ifndef FRONTEND |
141 | ereport(ERROR, |
142 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
143 | errmsg("out of memory" ))); |
144 | #else |
145 | fprintf(stderr, _("out of memory\n" )); |
146 | exit(EXIT_FAILURE); |
147 | #endif |
148 | } |
149 | |
150 | return nprinted + 1; |
151 | } |
152 | |