From: Tom Lane Date: Tue, 22 Oct 2013 22:42:13 +0000 (-0400) Subject: Get rid of use of asprintf() in favor of a more portable implementation. X-Git-Tag: REL9_4_BETA1~1025 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=09a89cb5fc29b47c26d151e82293fd3bef592b7b;p=postgresql Get rid of use of asprintf() in favor of a more portable implementation. asprintf(), aside from not being particularly portable, has a fundamentally badly-designed API; the psprintf() function that was added in passing in the previous patch has a much better API choice. Moreover, the NetBSD implementation that was borrowed for the previous patch doesn't work with non-C99-compliant vsnprintf, which is something we still have to cope with on some platforms; and it depends on va_copy which isn't all that portable either. Get rid of that code in favor of an implementation similar to what we've used for many years in stringinfo.c. Also, move it into libpgcommon since it's not really libpgport material. I think this patch will be enough to turn the buildfarm green again, but there's still cosmetic work left to do, namely get rid of pg_asprintf() in favor of using psprintf(). That will come in a followon patch. --- diff --git a/configure b/configure index c20afde8db..74e34ebae7 100755 --- a/configure +++ b/configure @@ -21500,8 +21500,7 @@ fi - -for ac_func in asprintf crypt fls getopt getrusage inet_aton random rint srandom strerror strlcat strlcpy +for ac_func in crypt fls getopt getrusage inet_aton random rint srandom strerror strlcat strlcpy do as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` { $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 diff --git a/configure.in b/configure.in index d2bab32589..a4baeaf4c9 100644 --- a/configure.in +++ b/configure.in @@ -1344,7 +1344,7 @@ else AC_CHECK_FUNCS([fpclass fp_class fp_class_d class], [break]) fi -AC_REPLACE_FUNCS([asprintf crypt fls getopt getrusage inet_aton random rint srandom strerror strlcat strlcpy]) +AC_REPLACE_FUNCS([crypt fls getopt getrusage inet_aton random rint srandom strerror strlcat strlcpy]) case $host_os in diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 7e65d2814c..2dbf7e53a1 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -1015,15 +1015,17 @@ pg_GSS_recvauth(Port *port) */ if (getenv("KRB5_KTNAME") == NULL) { - char *kt_path; + size_t kt_len = strlen(pg_krb_server_keyfile) + 14; + char *kt_path = malloc(kt_len); - if (asprintf(&kt_path, "KRB5_KTNAME=%s", pg_krb_server_keyfile) < 0) + if (!kt_path) { ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); return STATUS_ERROR; } + snprintf(kt_path, kt_len, "KRB5_KTNAME=%s", pg_krb_server_keyfile); putenv(kt_path); } } diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 381a629334..a3c82398dd 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -165,10 +165,12 @@ make_absolute_path(const char *path) } } - if (asprintf(&new, "%s/%s", buf, path) < 0) + new = malloc(strlen(buf) + strlen(path) + 2); + if (!new) ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); + sprintf(new, "%s/%s", buf, path); free(buf); } else diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c index b7beb130ea..9574fd3c7a 100644 --- a/src/backend/utils/mmgr/mcxt.c +++ b/src/backend/utils/mmgr/mcxt.c @@ -852,52 +852,3 @@ pnstrdup(const char *in, Size len) out[len] = '\0'; return out; } - -/* - * asprintf()-like functions around palloc, adapted from - * http://ftp.netbsd.org/pub/pkgsrc/current/pkgsrc/pkgtools/libnbcompat/files/asprintf.c - */ - -char * -psprintf(const char *format, ...) -{ - va_list ap; - char *retval; - - va_start(ap, format); - retval = pvsprintf(format, ap); - va_end(ap); - - return retval; -} - -char * -pvsprintf(const char *format, va_list ap) -{ - char *buf, *new_buf; - size_t len; - int retval; - va_list ap2; - - len = 128; - buf = palloc(len); - - va_copy(ap2, ap); - retval = vsnprintf(buf, len, format, ap); - Assert(retval >= 0); - - if (retval < len) - { - new_buf = repalloc(buf, retval + 1); - va_end(ap2); - return new_buf; - } - - len = (size_t)retval + 1; - pfree(buf); - buf = palloc(len); - retval = vsnprintf(buf, len, format, ap2); - va_end(ap2); - Assert(retval == len - 1); - return buf; -} diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c index 065e0c1cb2..faaecce614 100644 --- a/src/bin/psql/large_obj.c +++ b/src/bin/psql/large_obj.c @@ -200,12 +200,12 @@ do_lo_import(const char *filename_arg, const char *comment_arg) char *cmdbuf; char *bufptr; size_t slen = strlen(comment_arg); - int rv; - rv = asprintf(&cmdbuf, "COMMENT ON LARGE OBJECT %u IS '", loid); - if (rv < 0) + cmdbuf = malloc(slen * 2 + 256); + if (!cmdbuf) return fail_lo_xact("\\lo_import", own_transaction); - bufptr = cmdbuf + rv; + sprintf(cmdbuf, "COMMENT ON LARGE OBJECT %u IS '", loid); + bufptr = cmdbuf + strlen(cmdbuf); bufptr += PQescapeStringConn(pset.db, bufptr, comment_arg, slen, NULL); strcpy(bufptr, "'"); diff --git a/src/common/Makefile b/src/common/Makefile index d4ef2f1a30..62ca8ab0c3 100644 --- a/src/common/Makefile +++ b/src/common/Makefile @@ -23,7 +23,7 @@ include $(top_builddir)/src/Makefile.global override CPPFLAGS := -DFRONTEND $(CPPFLAGS) LIBS += $(PTHREAD_LIBS) -OBJS_COMMON = exec.o pgfnames.o relpath.o rmtree.o wait_error.o +OBJS_COMMON = exec.o pgfnames.o psprintf.o relpath.o rmtree.o wait_error.o OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o diff --git a/src/common/fe_memutils.c b/src/common/fe_memutils.c index 7b96a4b6ba..bfe79f8e43 100644 --- a/src/common/fe_memutils.c +++ b/src/common/fe_memutils.c @@ -93,25 +93,6 @@ pg_free(void *ptr) free(ptr); } -int -pg_asprintf(char **ret, const char *format, ...) -{ - va_list ap; - int rc; - - va_start(ap, format); - rc = vasprintf(ret, format, ap); - va_end(ap); - - if (rc < 0) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } - - return rc; -} - /* * Frontend emulation of backend memory management functions. Useful for * programs that compile backend files. @@ -145,23 +126,3 @@ repalloc(void *pointer, Size size) { return pg_realloc(pointer, size); } - -char * -psprintf(const char *format, ...) -{ - va_list ap; - int rc; - char *ret; - - va_start(ap, format); - rc = vasprintf(&ret, format, ap); - va_end(ap); - - if (rc < 0) - { - fprintf(stderr, _("out of memory\n")); - exit(EXIT_FAILURE); - } - - return ret; -} diff --git a/src/common/psprintf.c b/src/common/psprintf.c new file mode 100644 index 0000000000..87fd013f84 --- /dev/null +++ b/src/common/psprintf.c @@ -0,0 +1,207 @@ +/*------------------------------------------------------------------------- + * + * psprintf.c + * sprintf into an allocated-on-demand buffer + * + * + * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/common/psprintf.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include "utils/memutils.h" + + +static size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) +__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 0))); + + +/* + * 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; + + /* + * 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); + len = pvsnprintf(result, len, fmt, args); + va_end(args); + + if (len == 0) + return result; /* success */ + + /* Release buffer and loop around to try again with larger len. */ + pfree(result); + } +} + +/* + * 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 zero. If there's not enough space in buf, return + * an estimate of the buffer size needed to succeed (this *must* be more + * than "len", else psprintf might loop infinitely). + * Other error cases do not return. + * + * XXX This API is ugly, but there seems no alternative given the C spec's + * restrictions on what can portably be done with va_list arguments: you have + * to redo va_start before you can rescan the argument list, and we can't do + * that from here. + */ +static size_t +pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) +{ + int nprinted; + + Assert(len > 0); + + errno = 0; + + /* + * Assert check here is to catch buggy vsnprintf that overruns the + * specified buffer length. Solaris 7 in 64-bit mode is an example of a + * platform with such a bug. + */ +#ifdef USE_ASSERT_CHECKING + buf[len - 1] = '\0'; +#endif + + nprinted = vsnprintf(buf, len, fmt, args); + + Assert(buf[len - 1] == '\0'); + + /* + * If vsnprintf reports an error other than ENOMEM, fail. The possible + * causes of this are not user-facing errors, so elog should be enough. + */ + if (nprinted < 0 && errno != 0 && errno != ENOMEM) + { +#ifndef FRONTEND + elog(ERROR, "vsnprintf failed: %m"); +#else + fprintf(stderr, "vsnprintf failed: %s\n", strerror(errno)); + exit(EXIT_FAILURE); +#endif + } + + /* + * Note: some versions of vsnprintf return the number of chars actually + * stored, not the total space needed as C99 specifies. And at least one + * returns -1 on failure. Be conservative about believing whether the + * print worked. + */ + if (nprinted >= 0 && (size_t) nprinted < len - 1) + { + /* Success. Note nprinted does not include trailing null. */ + return 0; + } + + if (nprinted >= 0 && (size_t) nprinted > len) + { + /* + * This appears to be a C99-compliant vsnprintf, so believe its + * estimate of the required space. (If it's wrong, this code will + * still work, but may loop multiple times.) Note that the space + * needed should be only nprinted+1 bytes, but we'd better allocate + * one more than that so that the test above will succeed next time. + * + * In the corner case where the required space just barely overflows, + * fall through so that we'll error out below (possibly after looping). + */ + if ((size_t) nprinted <= MaxAllocSize - 2) + return nprinted + 2; + } + + /* + * Buffer overrun, and we don't know how much space is needed. Estimate + * twice the previous buffer size. If this would overflow, choke. We use + * a palloc-oriented overflow limit even when in frontend. + */ + if (len > MaxAllocSize / 2) + { +#ifndef FRONTEND + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); +#else + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); +#endif + } + + return len * 2; +} + + +/* + * XXX this is going away shortly. + */ +#ifdef FRONTEND +int +pg_asprintf(char **ret, const char *fmt, ...) +{ + size_t len = 128; /* initial assumption about buffer size */ + + for (;;) + { + char *result; + va_list args; + + /* + * 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); + len = pvsnprintf(result, len, fmt, args); + va_end(args); + + if (len == 0) + { + *ret = result; + return 0; + } + + /* Release buffer and loop around to try again with larger len. */ + pfree(result); + } +} +#endif diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 0250e39dbb..5eac52d93a 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -87,9 +87,6 @@ /* Define to 1 if you have the `append_history' function. */ #undef HAVE_APPEND_HISTORY -/* Define to 1 if you have the `asprintf' function. */ -#undef HAVE_ASPRINTF - /* Define to 1 if you have the `cbrt' function. */ #undef HAVE_CBRT diff --git a/src/include/port.h b/src/include/port.h index 0b9dfc81e2..5ef4b0a0b1 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -404,11 +404,6 @@ extern double rint(double x); extern int inet_aton(const char *cp, struct in_addr * addr); #endif -#ifndef HAVE_ASPRINTF -extern int asprintf(char **ret, const char *fmt, ...) __attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3))); -extern int vasprintf(char **ret, const char *fmt, va_list ap) __attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 0))); -#endif - #if !HAVE_DECL_STRLCAT extern size_t strlcat(char *dst, const char *src, size_t siz); #endif diff --git a/src/include/port/win32.h b/src/include/port/win32.h index 70175d14a5..2c2d93765e 100644 --- a/src/include/port/win32.h +++ b/src/include/port/win32.h @@ -237,15 +237,6 @@ int setitimer(int which, const struct itimerval * value, struct itimerval * ov #endif #endif -/* - * Supplement to - */ - -/* Visual Studios 2012 and earlier don't have va_copy() */ -#if defined(_MSC_VER) && _MSC_VER <= 1700 -#define va_copy(dest, src) ((dest) = (src)) -#endif - /* * Supplement to . * diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h index 03ef87e2d7..676333fd6d 100644 --- a/src/include/utils/palloc.h +++ b/src/include/utils/palloc.h @@ -97,11 +97,15 @@ extern char *MemoryContextStrdup(MemoryContext context, const char *string); extern char *pstrdup(const char *in); extern char *pnstrdup(const char *in, Size len); + +/* sprintf into a palloc'd buffer */ +extern char *psprintf(const char *fmt,...) +__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2))); + +/* basic memory allocation functions */ extern void *palloc(Size size); extern void *palloc0(Size size); extern void pfree(void *pointer); extern void *repalloc(void *pointer, Size size); -extern char *psprintf(const char *format, ...) __attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2))); -extern char *pvsprintf(const char *format, va_list ap) __attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 0))); #endif /* PALLOC_H */ diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c index 8d716e58a2..d3ebb0e106 100644 --- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c +++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c @@ -127,9 +127,12 @@ main(void) { for (j = 0; times[j]; j++) { - char* t; - if (asprintf(&t, "%s %s", dates[i], times[j]) < 0) - abort(); + int length = strlen(dates[i]) + + 1 + + strlen(times[j]) + + 1; + char* t = malloc(length); + sprintf(t, "%s %s", dates[i], times[j]); ts1 = PGTYPEStimestamp_from_asc(t, NULL); text = PGTYPEStimestamp_to_asc(ts1); if (i != 19 || j != 3) /* timestamp as integer or double differ for this case */ diff --git a/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc b/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc index 2a1c4a61dd..0edf012fd1 100644 --- a/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc +++ b/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc @@ -92,9 +92,12 @@ main(void) { for (j = 0; times[j]; j++) { - char* t; - if (asprintf(&t, "%s %s", dates[i], times[j]) < 0) - abort(); + int length = strlen(dates[i]) + + 1 + + strlen(times[j]) + + 1; + char* t = malloc(length); + sprintf(t, "%s %s", dates[i], times[j]); ts1 = PGTYPEStimestamp_from_asc(t, NULL); text = PGTYPEStimestamp_to_asc(ts1); if (i != 19 || j != 3) /* timestamp as integer or double differ for this case */ diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index dfc9cfb1fb..975f7958d1 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -420,6 +420,7 @@ pg_GSS_startup(PGconn *conn) { OM_uint32 maj_stat, min_stat; + int maxlen; gss_buffer_desc temp_gbuf; if (!(conn->pghost && conn->pghost[0] != '\0')) @@ -440,14 +441,16 @@ pg_GSS_startup(PGconn *conn) * Import service principal name so the proper ticket can be acquired by * the GSSAPI system. */ - if (asprintf((char **)&temp_gbuf.value, "%s@%s", - conn->krbsrvname, conn->pghost) < 0) + maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2; + temp_gbuf.value = (char *) malloc(maxlen); + if (!temp_gbuf.value) { printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory")); + libpq_gettext("out of memory\n")); return STATUS_ERROR; } - + snprintf(temp_gbuf.value, maxlen, "%s@%s", + conn->krbsrvname, conn->pghost); temp_gbuf.length = strlen(temp_gbuf.value); maj_stat = gss_import_name(&min_stat, &temp_gbuf, @@ -659,11 +662,13 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate) libpq_gettext("host name must be specified\n")); return STATUS_ERROR; } - if (asprintf(&conn->sspitarget, "%s/%s", conn->krbsrvname, conn->pghost) < 0) + conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(conn->pghost) + 2); + if (!conn->sspitarget) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return STATUS_ERROR; } + sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, conn->pghost); /* * Indicate that we're in SSPI authentication mode to make sure that diff --git a/src/port/asprintf.c b/src/port/asprintf.c deleted file mode 100644 index fafc03aa9e..0000000000 --- a/src/port/asprintf.c +++ /dev/null @@ -1,111 +0,0 @@ -/* src/port/asprintf.c */ - -/* $NetBSD: asprintf.c,v 1.3 2012/07/02 16:02:53 joerg Exp $ */ - -/*- - * Copyright (c) 2007 Joerg Sonnenberger . - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "c.h" - -#define HAVE_VA_COPY 1 - -int -asprintf(char **ret, const char *fmt, ...) -{ - va_list ap; - int retval; - - va_start(ap, fmt); - retval = vasprintf(ret, fmt, ap); - va_end(ap); - - return retval; -} - -int -vasprintf(char **ret, const char *fmt, va_list ap) -{ - char *buf, *new_buf; - size_t len; - int retval; - va_list ap2; - - len = 128; - buf = malloc(len); - if (buf == NULL) { - *ret = NULL; - return -1; - } - -#if defined(HAVE_VA_COPY) - va_copy(ap2, ap); -#define my_va_end(ap2) va_end(ap2) -#elif defined(HAVE___BUILTIN_VA_COPY) - __builtin_va_copy(ap2, ap); -#define my_va_end(ap2) __builtin_va_end(ap2) -#else - ap2 = ap; -#define my_va_end(ap2) do {} while (0) -#endif - retval = vsnprintf(buf, len, fmt, ap); - if (retval < 0) { - free(buf); - *ret = NULL; - va_end(ap2); - return -1; - } - - if (retval < len) { - new_buf = realloc(buf, retval + 1); - if (new_buf == NULL) - *ret = buf; - else - *ret = new_buf; - my_va_end(ap2); - return retval; - } - - len = (size_t)retval + 1; - free(buf); - buf = malloc(len); - if (buf == NULL) { - *ret = NULL; - my_va_end(ap2); - return -1; - } - retval = vsnprintf(buf, len, fmt, ap2); - my_va_end(ap2); - if (retval != len - 1) { - free(buf); - *ret = NULL; - return -1; - } - *ret = buf; - return retval; -} diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 0e84348e4d..bc7f4496e0 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -65,7 +65,7 @@ sub mkvcbuild $solution = CreateSolution($vsVersion, $config); our @pgportfiles = qw( - asprintf.c chklocale.c crypt.c fls.c fseeko.c getrusage.c inet_aton.c random.c + chklocale.c crypt.c fls.c fseeko.c getrusage.c inet_aton.c random.c srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c pgcheckdir.c pg_crc.c pgmkdirp.c pgsleep.c pgstrcasecmp.c pqsignal.c @@ -74,7 +74,7 @@ sub mkvcbuild win32error.c win32setlocale.c); our @pgcommonallfiles = qw( - exec.c pgfnames.c relpath.c rmtree.c wait_error.c); + exec.c pgfnames.c psprintf.c relpath.c rmtree.c wait_error.c); our @pgcommonfrontendfiles = (@pgcommonallfiles, qw(fe_memutils.c));