postgresql/src/common/psprintf.c

146 lines
4.0 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* psprintf.c
* sprintf into an allocated-on-demand buffer
*
*
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/common/psprintf.c
*
*-------------------------------------------------------------------------
*/
#ifndef FRONTEND
#include "postgres.h"
#include "utils/memutils.h"
#else
#include "postgres_fe.h"
/* It's possible we could use a different value for this in frontend code */
Phase 2 of pgindent updates. Change pg_bsd_indent to follow upstream rules for placement of comments to the right of code, and remove pgindent hack that caused comments following #endif to not obey the general rule. Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using the published version of pg_bsd_indent, but a hacked-up version that tried to minimize the amount of movement of comments to the right of code. The situation of interest is where such a comment has to be moved to the right of its default placement at column 33 because there's code there. BSD indent has always moved right in units of tab stops in such cases --- but in the previous incarnation, indent was working in 8-space tab stops, while now it knows we use 4-space tabs. So the net result is that in about half the cases, such comments are placed one tab stop left of before. This is better all around: it leaves more room on the line for comment text, and it means that in such cases the comment uniformly starts at the next 4-space tab stop after the code, rather than sometimes one and sometimes two tabs after. Also, ensure that comments following #endif are indented the same as comments following other preprocessor commands such as #else. That inconsistency turns out to have been self-inflicted damage from a poorly-thought-through post-indent "fixup" in pgindent. This patch is much less interesting than the first round of indent changes, but also bulkier, so I thought it best to separate the effects. Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02:00
#define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
#endif
/*
* psprintf
*
* Format text data under the control of fmt (an sprintf-style format string)
* and return it in an allocated-on-demand buffer. The buffer is allocated
* with palloc in the backend, or malloc in frontend builds. Caller is
* responsible to free the buffer when no longer needed, if appropriate.
*
* Errors are not returned to the caller, but are reported via elog(ERROR)
* in the backend, or printf-to-stderr-and-exit() in frontend builds.
* One should therefore think twice about using this in libpq.
*/
char *
psprintf(const char *fmt,...)
{
size_t len = 128; /* initial assumption about buffer size */
for (;;)
{
char *result;
va_list args;
size_t newlen;
/*
* Allocate result buffer. Note that in frontend this maps to malloc
* with exit-on-error.
*/
result = (char *) palloc(len);
/* Try to format the data. */
va_start(args, fmt);
newlen = pvsnprintf(result, len, fmt, args);
va_end(args);
if (newlen < len)
return result; /* success */
/* Release buffer and loop around to try again with larger len. */
pfree(result);
len = newlen;
}
}
/*
* pvsnprintf
*
* Attempt to format text data under the control of fmt (an sprintf-style
* format string) and insert it into buf (which has length len).
*
* If successful, return the number of bytes emitted, not counting the
* trailing zero byte. This will always be strictly less than len.
*
* If there's not enough space in buf, return an estimate of the buffer size
* needed to succeed (this *must* be more than the given len, else callers
* might loop infinitely).
*
* Other error cases do not return, but exit via elog(ERROR) or exit().
* Hence, this shouldn't be used inside libpq.
*
* Note that the semantics of the return value are not exactly C99's.
* First, we don't promise that the estimated buffer size is exactly right;
* callers must be prepared to loop multiple times to get the right size.
* (Given a C99-compliant vsnprintf, that won't happen, but it is rumored
* that some implementations don't always return the same value ...)
* Second, we return the recommended buffer size, not one less than that;
* this lets overflow concerns be handled here rather than in the callers.
*/
size_t
pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
{
int nprinted;
nprinted = vsnprintf(buf, len, fmt, args);
/* We assume failure means the fmt is bogus, hence hard failure is OK */
if (unlikely(nprinted < 0))
{
#ifndef FRONTEND
elog(ERROR, "vsnprintf failed: %m");
#else
fprintf(stderr, "vsnprintf failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
#endif
}
if ((size_t) nprinted < len)
{
/* Success. Note nprinted does not include trailing null. */
return (size_t) nprinted;
}
/*
* We assume a C99-compliant vsnprintf, so believe its estimate of the
* required space, and add one for the trailing null. (If it's wrong, the
* logic will still work, but we may loop multiple times.)
*
* Choke if the required space would exceed MaxAllocSize. Note we use
* this palloc-oriented overflow limit even when in frontend.
*/
if (unlikely((size_t) nprinted > MaxAllocSize - 1))
{
#ifndef FRONTEND
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("out of memory")));
#else
fprintf(stderr, _("out of memory\n"));
exit(EXIT_FAILURE);
#endif
}
return nprinted + 1;
}