> I could envision a helper procedure, known only within libpq, that has > a signature like formatNotice(PGconn* conn, const char *fmtstring, ...) > and encapsulates the work needed to handle a format string. But I see > no reason to push that work out to client applications.
I could make fnoticeProc private if you'd like, but that doesn't seem smart if the user can override noticeFormat but can't with the internal bits used by libpq... unless you're envisioning a private method that constructs a msg for you, calls noticeProc with the buffer as the arg, then then free()'s the result. All regression tests pass with this case and no ABI or source incompatabilities are introduced. -sc -- Sean Chittenden
Index: fe-connect.c =================================================================== RCS file: /home/ncvs/pgsql/pgsql-server/src/interfaces/libpq/fe-connect.c,v retrieving revision 1.250 diff -u -r1.250 fe-connect.c --- fe-connect.c 21 Jun 2003 21:51:33 -0000 1.250 +++ fe-connect.c 23 Jun 2003 07:06:56 -0000 @@ -21,6 +21,9 @@ #include <errno.h> #include <ctype.h> #include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> #ifndef HAVE_STRDUP #include "strdup.h" @@ -178,11 +181,11 @@ const char *keyword); static void defaultNoticeReceiver(void *arg, const PGresult *res); static void defaultNoticeProcessor(void *arg, const char *message); +static void defaultfNoticeProcessor(void *arg, const char *fmt, ...); static int parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage); static char *pwdfMatchesString(char *buf, char *token); -static char *PasswordFromFile(char *hostname, char *port, char *dbname, - char *username); +static char *PasswordFromFile(PGconn *conn); /* * Connecting to a Database @@ -386,8 +389,7 @@ { if (conn->pgpass) free(conn->pgpass); - conn->pgpass = PasswordFromFile(conn->pghost, conn->pgport, - conn->dbName, conn->pguser); + conn->pgpass = PasswordFromFile(conn); if (conn->pgpass == NULL) conn->pgpass = strdup(DefaultPassword); } @@ -1805,6 +1807,7 @@ /* Zero all pointers and booleans */ MemSet((char *) conn, 0, sizeof(PGconn)); + conn->noticeHooks.fnoticeProc = defaultfNoticeProcessor; conn->noticeHooks.noticeRec = defaultNoticeReceiver; conn->noticeHooks.noticeProc = defaultNoticeProcessor; conn->status = CONNECTION_BAD; @@ -2836,6 +2839,23 @@ return old; } +PQfnoticeProcessor +PQsetfNoticeProcessor(PGconn *conn, PQfnoticeProcessor proc, void *arg) +{ + PQfnoticeProcessor old; + + if (conn == NULL) + return NULL; + + old = conn->noticeHooks.fnoticeProc; + if (proc) + { + conn->noticeHooks.fnoticeProc = proc; + conn->noticeHooks.fnoticeProcArg = arg; + } + return old; +} + /* * The default notice message receiver just gets the standard notice text * and sends it to the notice processor. This two-level setup exists @@ -2860,8 +2880,25 @@ defaultNoticeProcessor(void *arg, const char *message) { (void) arg; /* not used */ - /* Note: we expect the supplied string to end with a newline already. */ - fprintf(stderr, "%s", message); + defaultfNoticeProcessor(NULL, "%s", message); +} + +/* + * This is the same as the defaultNoticeProcessor, however it takes a + * format as its 2nd argument and has a variable length function + * argument. Applications can override this if they want the messages + * to go elsewhere (a window, for example). Note that simply + * discarding notices is probably a bad idea. + */ +static void +defaultfNoticeProcessor(void *arg, const char *fmt, ...) +{ + va_list ap; + (void) arg; /* not used */ + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); } /* @@ -2906,27 +2943,36 @@ /* Get a password from the password file. Return value is malloc'd. */ static char * -PasswordFromFile(char *hostname, char *port, char *dbname, char *username) +PasswordFromFile(PGconn *conn) { FILE *fp; char *pgpassfile; char *home; + char *hostname; + char *port; struct stat stat_buf; #define LINELEN NAMEDATALEN*5 char buf[LINELEN]; - if (dbname == NULL || strlen(dbname) == 0) + if (conn == NULL) + return NULL; + + if (conn->dbName == NULL || strlen(conn->dbName) == 0) return NULL; - if (username == NULL || strlen(username) == 0) + if (conn->pguser == NULL || strlen(conn->pguser) == 0) return NULL; - if (hostname == NULL) + if (conn->pghost == NULL) hostname = DefaultHost; + else + hostname = conn->pghost; - if (port == NULL) + if (conn->pgport == NULL) port = DEF_PGPORT_STR; + else + port = conn->pgport; /* Look for it in the home dir */ home = getenv("HOME"); @@ -2936,7 +2982,7 @@ pgpassfile = malloc(strlen(home) + 1 + strlen(PGPASSFILE) + 1); if (!pgpassfile) { - fprintf(stderr, libpq_gettext("out of memory\n")); + conn->noticeHooks.fnoticeProc(NULL, libpq_gettext("out of memory\n")); return NULL; } @@ -2953,8 +2999,8 @@ /* If password file is insecure, alert the user and ignore it. */ if (stat_buf.st_mode & (S_IRWXG | S_IRWXO)) { - fprintf(stderr, - libpq_gettext("WARNING: Password file %s has world or group read access; permission should be u=rw (0600)\n"), + conn->noticeHooks.fnoticeProc(NULL, + libpq_gettext("WARNING: Password file %s has world or group read access; permission should be u=rw (0600)\n"), pgpassfile); free(pgpassfile); return NULL; @@ -2962,9 +3008,14 @@ #endif fp = fopen(pgpassfile, "r"); - free(pgpassfile); - if (fp == NULL) + if (fp == NULL) { + conn->noticeHooks.fnoticeProc(NULL, + libpq_gettext("WARNING: Unable to read password file %s: %s\n"), + pgpassfile, strerror(errno)); + free(pgpassfile); return NULL; + } + free(pgpassfile); while (!feof(fp)) { @@ -2982,10 +3033,10 @@ if (buf[len - 1] == '\n') buf[len - 1] = 0; - if ((t = pwdfMatchesString(t, hostname)) == NULL || - (t = pwdfMatchesString(t, port)) == NULL || - (t = pwdfMatchesString(t, dbname)) == NULL || - (t = pwdfMatchesString(t, username)) == NULL) + if ((t = pwdfMatchesString(t, conn->pghost)) == NULL || + (t = pwdfMatchesString(t, conn->pgport)) == NULL || + (t = pwdfMatchesString(t, conn->dbName)) == NULL || + (t = pwdfMatchesString(t, conn->pguser)) == NULL) continue; ret = strdup(t); fclose(fp); Index: fe-exec.c =================================================================== RCS file: /home/ncvs/pgsql/pgsql-server/src/interfaces/libpq/fe-exec.c,v retrieving revision 1.139 diff -u -r1.139 fe-exec.c --- fe-exec.c 21 Jun 2003 21:51:34 -0000 1.139 +++ fe-exec.c 23 Jun 2003 07:02:49 -0000 @@ -170,6 +170,8 @@ else { /* defaults... */ + result->noticeHooks.fnoticeProc = NULL; + result->noticeHooks.fnoticeProcArg = NULL; result->noticeHooks.noticeRec = NULL; result->noticeHooks.noticeRecArg = NULL; result->noticeHooks.noticeProc = NULL; Index: libpq-fe.h =================================================================== RCS file: /home/ncvs/pgsql/pgsql-server/src/interfaces/libpq/libpq-fe.h,v retrieving revision 1.94 diff -u -r1.94 libpq-fe.h --- libpq-fe.h 21 Jun 2003 21:51:34 -0000 1.94 +++ libpq-fe.h 23 Jun 2003 07:07:31 -0000 @@ -132,6 +132,7 @@ /* Function types for notice-handling callbacks */ typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res); typedef void (*PQnoticeProcessor) (void *arg, const char *message); +typedef void (*PQfnoticeProcessor) (void *arg, const char *fmt, ...); /* Print options for PQprint() */ typedef char pqbool; @@ -272,6 +273,10 @@ void *arg); extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, + void *arg); + +extern PQfnoticeProcessor PQsetfNoticeProcessor(PGconn *conn, + PQfnoticeProcessor proc, void *arg); /* === in fe-exec.c === */ Index: libpq-int.h =================================================================== RCS file: /home/ncvs/pgsql/pgsql-server/src/interfaces/libpq/libpq-int.h,v retrieving revision 1.75 diff -u -r1.75 libpq-int.h --- libpq-int.h 21 Jun 2003 21:51:34 -0000 1.75 +++ libpq-int.h 23 Jun 2003 07:01:58 -0000 @@ -129,6 +129,8 @@ void *noticeRecArg; PQnoticeProcessor noticeProc; /* notice message processor */ void *noticeProcArg; + PQfnoticeProcessor fnoticeProc; /* notice message processor */ + void *fnoticeProcArg; } PGNoticeHooks; struct pg_result
---------------------------(end of broadcast)--------------------------- TIP 2: you can get off all lists at once with the unregister command (send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])