aboutsummaryrefslogtreecommitdiff
path: root/src/port
diff options
context:
space:
mode:
Diffstat (limited to 'src/port')
-rw-r--r--src/port/Makefile2
-rw-r--r--src/port/snprintf.c94
-rw-r--r--src/port/syswrap.c155
3 files changed, 211 insertions, 40 deletions
diff --git a/src/port/Makefile b/src/port/Makefile
index 835b034fe96..02b2791cc37 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -33,7 +33,7 @@ LIBS += $(PTHREAD_LIBS)
OBJS = $(LIBOBJS) chklocale.o erand48.o fls.o inet_net_ntop.o \
noblock.o path.o pgcheckdir.o pg_crc.o pgmkdirp.o pgsleep.o \
pgstrcasecmp.o pqsignal.o \
- qsort.o qsort_arg.o quotes.o sprompt.o tar.o thread.o
+ qsort.o qsort_arg.o quotes.o sprompt.o syswrap.o tar.o thread.o
# foo_srv.o and foo.o are both built from foo.c, but only foo.o has -DFRONTEND
OBJS_SRV = $(OBJS:%.o=%_srv.o)
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index 0c779a601fc..91c97d487cd 100644
--- a/src/port/snprintf.c
+++ b/src/port/snprintf.c
@@ -114,6 +114,7 @@ typedef struct
/* bufend == NULL is for sprintf, where we assume buf is big enough */
FILE *stream; /* eventual output destination, or NULL */
int nchars; /* # chars already sent to stream */
+ bool failed; /* call is a failure; errno is set */
} PrintfTarget;
/*
@@ -143,7 +144,7 @@ typedef union
static void flushbuffer(PrintfTarget *target);
-static int dopr(PrintfTarget *target, const char *format, va_list args);
+static void dopr(PrintfTarget *target, const char *format, va_list args);
int
@@ -157,14 +158,10 @@ pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
target.bufend = str + count - 1;
target.stream = NULL;
/* target.nchars is unused in this case */
- if (dopr(&target, fmt, args))
- {
- *(target.bufptr) = '\0';
- errno = EINVAL; /* bad format */
- return -1;
- }
+ target.failed = false;
+ dopr(&target, fmt, args);
*(target.bufptr) = '\0';
- return target.bufptr - target.bufstart;
+ return target.failed ? -1 : (target.bufptr - target.bufstart);
}
int
@@ -190,14 +187,10 @@ pg_vsprintf(char *str, const char *fmt, va_list args)
target.bufend = NULL;
target.stream = NULL;
/* target.nchars is unused in this case */
- if (dopr(&target, fmt, args))
- {
- *(target.bufptr) = '\0';
- errno = EINVAL; /* bad format */
- return -1;
- }
+ target.failed = false;
+ dopr(&target, fmt, args);
*(target.bufptr) = '\0';
- return target.bufptr - target.bufstart;
+ return target.failed ? -1 : (target.bufptr - target.bufstart);
}
int
@@ -227,14 +220,11 @@ pg_vfprintf(FILE *stream, const char *fmt, va_list args)
target.bufend = buffer + sizeof(buffer) - 1;
target.stream = stream;
target.nchars = 0;
- if (dopr(&target, fmt, args))
- {
- errno = EINVAL; /* bad format */
- return -1;
- }
+ target.failed = false;
+ dopr(&target, fmt, args);
/* dump any remaining buffer contents */
flushbuffer(&target);
- return target.nchars;
+ return target.failed ? -1 : target.nchars;
}
int
@@ -261,14 +251,24 @@ pg_printf(const char *fmt,...)
return len;
}
-/* call this only when stream is defined */
+/*
+ * Attempt to write the entire buffer to target->stream; discard the entire
+ * buffer in any case. Call this only when target->stream is defined.
+ */
static void
flushbuffer(PrintfTarget *target)
{
size_t nc = target->bufptr - target->bufstart;
- if (nc > 0)
- target->nchars += fwrite(target->bufstart, 1, nc, target->stream);
+ if (!target->failed && nc > 0)
+ {
+ size_t written;
+
+ written = fwrite(target->bufstart, 1, nc, target->stream);
+ target->nchars += written;
+ if (written != nc)
+ target->failed = true;
+ }
target->bufptr = target->bufstart;
}
@@ -295,7 +295,7 @@ static void trailing_pad(int *padlen, PrintfTarget *target);
/*
* dopr(): poor man's version of doprintf
*/
-static int
+static void
dopr(PrintfTarget *target, const char *format, va_list args)
{
const char *format_start = format;
@@ -372,12 +372,12 @@ nextch1:
case '$':
have_dollar = true;
if (accum <= 0 || accum > NL_ARGMAX)
- return -1;
+ goto bad_format;
if (afterstar)
{
if (argtypes[accum] &&
argtypes[accum] != ATYPE_INT)
- return -1;
+ goto bad_format;
argtypes[accum] = ATYPE_INT;
last_dollar = Max(last_dollar, accum);
afterstar = false;
@@ -427,7 +427,7 @@ nextch1:
atype = ATYPE_INT;
if (argtypes[fmtpos] &&
argtypes[fmtpos] != atype)
- return -1;
+ goto bad_format;
argtypes[fmtpos] = atype;
last_dollar = Max(last_dollar, fmtpos);
}
@@ -439,7 +439,7 @@ nextch1:
{
if (argtypes[fmtpos] &&
argtypes[fmtpos] != ATYPE_INT)
- return -1;
+ goto bad_format;
argtypes[fmtpos] = ATYPE_INT;
last_dollar = Max(last_dollar, fmtpos);
}
@@ -452,7 +452,7 @@ nextch1:
{
if (argtypes[fmtpos] &&
argtypes[fmtpos] != ATYPE_CHARPTR)
- return -1;
+ goto bad_format;
argtypes[fmtpos] = ATYPE_CHARPTR;
last_dollar = Max(last_dollar, fmtpos);
}
@@ -468,7 +468,7 @@ nextch1:
{
if (argtypes[fmtpos] &&
argtypes[fmtpos] != ATYPE_DOUBLE)
- return -1;
+ goto bad_format;
argtypes[fmtpos] = ATYPE_DOUBLE;
last_dollar = Max(last_dollar, fmtpos);
}
@@ -489,7 +489,7 @@ nextch1:
/* Per spec, you use either all dollar or all not. */
if (have_dollar && have_non_dollar)
- return -1;
+ goto bad_format;
/*
* In dollar mode, collect the arguments in physical order.
@@ -499,7 +499,7 @@ nextch1:
switch (argtypes[i])
{
case ATYPE_NONE:
- return -1; /* invalid format */
+ goto bad_format;
case ATYPE_INT:
argvalues[i].i = va_arg(args, int);
break;
@@ -524,6 +524,9 @@ nextch1:
format = format_start;
while ((ch = *format++) != '\0')
{
+ if (target->failed)
+ break;
+
if (ch != '%')
{
dopr_outch(ch, target);
@@ -781,7 +784,11 @@ nextch2:
}
}
- return 0;
+ return;
+
+bad_format:
+ errno = EINVAL;
+ target->failed = true;
}
static size_t
@@ -831,8 +838,10 @@ fmtptr(void *value, PrintfTarget *target)
/* we rely on regular C library's sprintf to do the basic conversion */
vallen = sprintf(convert, "%p", value);
-
- dostr(convert, vallen, target);
+ if (vallen < 0)
+ target->failed = true;
+ else
+ dostr(convert, vallen, target);
}
static void
@@ -965,16 +974,19 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
if (pointflag)
{
- sprintf(fmt, "%%.%d%c", prec, type);
+ if (sprintf(fmt, "%%.%d%c", prec, type) < 0)
+ goto fail;
zeropadlen = precision - prec;
}
- else
- sprintf(fmt, "%%%c", type);
+ else if (sprintf(fmt, "%%%c", type) < 0)
+ goto fail;
if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue))
value = -value;
vallen = sprintf(convert, fmt, value);
+ if (vallen < 0)
+ goto fail;
/* If it's infinity or NaN, forget about doing any zero-padding */
if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1]))
@@ -1014,6 +1026,10 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
}
trailing_pad(&padlen, target);
+ return;
+
+fail:
+ target->failed = true;
}
static void
diff --git a/src/port/syswrap.c b/src/port/syswrap.c
new file mode 100644
index 00000000000..8415a336303
--- /dev/null
+++ b/src/port/syswrap.c
@@ -0,0 +1,155 @@
+/*-------------------------------------------------------------------------
+ *
+ * syswrap.c
+ * error-throwing wrappers around POSIX functions that rarely fail
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/port/syswrap.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+/* Prevent recursion */
+#undef vsnprintf
+#undef snprintf
+#undef vsprintf
+#undef sprintf
+#undef vfprintf
+#undef fprintf
+#undef printf
+
+/* When the libc primitives are lacking, use our own. */
+#ifdef USE_REPL_SNPRINTF
+#ifdef __GNUC__
+#define vsnprintf(...) pg_vsnprintf(__VA_ARGS__)
+#define snprintf(...) pg_snprintf(__VA_ARGS__)
+#define vsprintf(...) pg_vsprintf(__VA_ARGS__)
+#define sprintf(...) pg_sprintf(__VA_ARGS__)
+#define vfprintf(...) pg_vfprintf(__VA_ARGS__)
+#define fprintf(...) pg_fprintf(__VA_ARGS__)
+#define printf(...) pg_printf(__VA_ARGS__)
+#else
+#define vsnprintf pg_vsnprintf
+#define snprintf pg_snprintf
+#define vsprintf pg_vsprintf
+#define sprintf pg_sprintf
+#define vfprintf pg_vfprintf
+#define fprintf pg_fprintf
+#define printf pg_printf
+#endif
+#endif /* USE_REPL_SNPRINTF */
+
+/*
+ * We abort() in the frontend, rather than exit(), because libpq in particular
+ * has no business calling exit(). These failures had better be rare.
+ */
+#ifdef FRONTEND
+#define LIB_ERR(func) \
+do { \
+ int discard = fprintf(stderr, "%s failed: %s\n", func, strerror(errno)); \
+ (void) discard; \
+ abort(); \
+} while (0)
+#else
+#define LIB_ERR(func) elog(ERROR, "%s failed: %m", func)
+#endif
+
+int
+vsnprintf_throw_on_fail(char *str, size_t count, const char *fmt, va_list args)
+{
+ int save_errno;
+ int ret;
+
+ /*
+ * On HP-UX B.11.31, a call that truncates output returns -1 without
+ * setting errno. (SUSv2 allowed this until the approval of Base Working
+ * Group Resolution BWG98-006.) We could avoid the save and restore of
+ * errno on most platforms.
+ */
+ save_errno = errno;
+ errno = 0;
+ ret = vsnprintf(str, count, fmt, args);
+ if (ret < 0 && errno != 0)
+ LIB_ERR("vsnprintf");
+ errno = save_errno;
+ return ret;
+}
+
+int
+snprintf_throw_on_fail(char *str, size_t count, const char *fmt,...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, fmt);
+ ret = vsnprintf_throw_on_fail(str, count, fmt, args);
+ va_end(args);
+ return ret;
+}
+
+int
+vsprintf_throw_on_fail(char *str, const char *fmt, va_list args)
+{
+ int ret;
+
+ ret = vsprintf(str, fmt, args);
+ if (ret < 0)
+ LIB_ERR("vsprintf");
+ return ret;
+}
+
+int
+sprintf_throw_on_fail(char *str, const char *fmt,...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, fmt);
+ ret = vsprintf_throw_on_fail(str, fmt, args);
+ va_end(args);
+ return ret;
+}
+
+int
+vfprintf_throw_on_fail(FILE *stream, const char *fmt, va_list args)
+{
+ int ret;
+
+ ret = vfprintf(stream, fmt, args);
+ if (ret < 0)
+ LIB_ERR("vfprintf");
+ return ret;
+}
+
+int
+fprintf_throw_on_fail(FILE *stream, const char *fmt,...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, fmt);
+ ret = vfprintf_throw_on_fail(stream, fmt, args);
+ va_end(args);
+ return ret;
+}
+
+int
+printf_throw_on_fail(const char *fmt,...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, fmt);
+ ret = vfprintf_throw_on_fail(stdout, fmt, args);
+ va_end(args);
+ return ret;
+}