aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/ref/psql-ref.sgml10
-rw-r--r--src/bin/psql/copy.c319
-rw-r--r--src/bin/psql/stringutils.c260
-rw-r--r--src/bin/psql/stringutils.h10
4 files changed, 372 insertions, 227 deletions
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index d232ef895ac..6e3525d7687 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1,5 +1,5 @@
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.78 2002/10/11 23:03:48 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.79 2002/10/19 00:22:14 tgl Exp $
PostgreSQL documentation
-->
@@ -692,6 +692,7 @@ testdb=>
<varlistentry>
<term><literal>\copy <replaceable class="parameter">table</replaceable>
+ [ ( <replaceable class="parameter">column_list</replaceable> ) ]
{ <literal>from</literal> | <literal>to</literal> }
<replaceable class="parameter">filename</replaceable> | stdin | stdout
[ <literal>with</literal> ]
@@ -705,11 +706,12 @@ testdb=>
Performs a frontend (client) copy. This is an operation that
runs an <acronym>SQL</acronym> <xref linkend="SQL-COPY"
endterm="SQL-COPY-title"> command, but instead of the backend's
- reading or writing the specified file, and consequently
- requiring backend access and special user privilege, as well as
- being bound to the file system accessible by the backend,
+ reading or writing the specified file,
<application>psql</application> reads or writes the file and
routes the data between the backend and the local file system.
+ This means that file accessibility and privileges are those
+ of the local user, not the server, and no SQL superuser
+ privileges are required.
</para>
<para>
diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c
index 7e1b05d5fe8..b70519be159 100644
--- a/src/bin/psql/copy.c
+++ b/src/bin/psql/copy.c
@@ -3,7 +3,7 @@
*
* Copyright 2000 by PostgreSQL Global Development Group
*
- * $Header: /cvsroot/pgsql/src/bin/psql/copy.c,v 1.27 2002/10/15 02:24:16 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/copy.c,v 1.28 2002/10/19 00:22:14 tgl Exp $
*/
#include "postgres_fe.h"
#include "copy.h"
@@ -38,11 +38,15 @@ bool copy_in_state;
* parse_slash_copy
* -- parses \copy command line
*
- * Accepted syntax: \copy table|"table" [with oids] from|to filename|'filename' [with ] [ oids ] [ delimiter '<char>'] [ null as 'string' ]
+ * Accepted syntax: \copy table [(columnlist)] [with oids] from|to filename [with ] [ oids ] [ delimiter char] [ null as string ]
* (binary is not here yet)
*
* Old syntax for backward compatibility: (2002-06-19):
- * \copy table|"table" [with oids] from|to filename|'filename' [ using delimiters '<char>'] [ with null as 'string' ]
+ * \copy table [(columnlist)] [with oids] from|to filename [ using delimiters char] [ with null as string ]
+ *
+ * table name can be double-quoted and can have a schema part.
+ * column names can be double-quoted.
+ * filename, char, and string can be single-quoted like SQL literals.
*
* returns a malloc'ed structure with the options, or NULL on parsing error
*/
@@ -50,6 +54,7 @@ bool copy_in_state;
struct copy_options
{
char *table;
+ char *column_list;
char *file; /* NULL = stdin/stdout */
bool from;
bool binary;
@@ -65,6 +70,7 @@ free_copy_options(struct copy_options * ptr)
if (!ptr)
return;
free(ptr->table);
+ free(ptr->column_list);
free(ptr->file);
free(ptr->delim);
free(ptr->null);
@@ -72,14 +78,32 @@ free_copy_options(struct copy_options * ptr)
}
+/* catenate "more" onto "var", freeing the original value of *var */
+static void
+xstrcat(char **var, const char *more)
+{
+ char *newvar;
+
+ newvar = (char *) malloc(strlen(*var) + strlen(more) + 1);
+ if (!newvar)
+ {
+ psql_error("out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+ strcpy(newvar, *var);
+ strcat(newvar, more);
+ free(*var);
+ *var = newvar;
+}
+
+
static struct copy_options *
parse_slash_copy(const char *args)
{
struct copy_options *result;
char *line;
char *token;
- bool error = false;
- char quote;
+ const char *whitespace = " \t\n\r";
if (args)
line = xstrdup(args);
@@ -95,152 +119,183 @@ parse_slash_copy(const char *args)
exit(EXIT_FAILURE);
}
- token = strtokx(line, " \t\n\r", "\"", '\\', &quote, NULL, pset.encoding);
+ token = strtokx(line, whitespace, ".,()", "\"",
+ 0, false, pset.encoding);
if (!token)
- error = true;
- else
- {
+ goto error;
+
#ifdef NOT_USED
- /* this is not implemented yet */
- if (!quote && strcasecmp(token, "binary") == 0)
+ /* this is not implemented yet */
+ if (strcasecmp(token, "binary") == 0)
+ {
+ result->binary = true;
+ token = strtokx(NULL, whitespace, ".,()", "\"",
+ 0, false, pset.encoding);
+ if (!token)
+ goto error;
+ }
+#endif
+
+ result->table = xstrdup(token);
+
+ token = strtokx(NULL, whitespace, ".,()", "\"",
+ 0, false, pset.encoding);
+ if (!token)
+ goto error;
+
+ /*
+ * strtokx() will not have returned a multi-character token starting with
+ * '.', so we don't need strcmp() here. Likewise for '(', etc, below.
+ */
+ if (token[0] == '.')
+ {
+ /* handle schema . table */
+ xstrcat(&result->table, token);
+ token = strtokx(NULL, whitespace, ".,()", "\"",
+ 0, false, pset.encoding);
+ if (!token)
+ goto error;
+ xstrcat(&result->table, token);
+ token = strtokx(NULL, whitespace, ".,()", "\"",
+ 0, false, pset.encoding);
+ if (!token)
+ goto error;
+ }
+
+ if (token[0] == '(')
+ {
+ /* handle parenthesized column list */
+ result->column_list = xstrdup(token);
+ for (;;)
{
- result->binary = true;
- token = strtokx(NULL, " \t\n\r", "\"", '\\', &quote, NULL, pset.encoding);
+ token = strtokx(NULL, whitespace, ".,()", "\"",
+ 0, false, pset.encoding);
+ if (!token || strchr(".,()", token[0]))
+ goto error;
+ xstrcat(&result->column_list, token);
+ token = strtokx(NULL, whitespace, ".,()", "\"",
+ 0, false, pset.encoding);
if (!token)
- error = true;
+ goto error;
+ xstrcat(&result->column_list, token);
+ if (token[0] == ')')
+ break;
+ if (token[0] != ',')
+ goto error;
}
- if (token)
-#endif
- result->table = xstrdup(token);
+ token = strtokx(NULL, whitespace, ".,()", "\"",
+ 0, false, pset.encoding);
+ if (!token)
+ goto error;
}
-#ifdef USE_ASSERT_CHECKING
- assert(error || result->table);
-#endif
-
- if (!error)
+ /*
+ * Allows old COPY syntax for backward compatibility
+ * 2002-06-19
+ */
+ if (strcasecmp(token, "with") == 0)
{
- token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
+ token = strtokx(NULL, whitespace, NULL, NULL,
+ 0, false, pset.encoding);
+ if (!token || strcasecmp(token, "oids") != 0)
+ goto error;
+ result->oids = true;
+
+ token = strtokx(NULL, whitespace, NULL, NULL,
+ 0, false, pset.encoding);
if (!token)
- error = true;
- else
- {
- /*
- * Allows old COPY syntax for backward compatibility
- * 2002-06-19
- */
- if (strcasecmp(token, "with") == 0)
- {
- token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
- if (!token || strcasecmp(token, "oids") != 0)
- error = true;
- else
- result->oids = true;
+ goto error;
+ }
- if (!error)
- {
- token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
- if (!token)
- error = true;
- }
- }
+ if (strcasecmp(token, "from") == 0)
+ result->from = true;
+ else if (strcasecmp(token, "to") == 0)
+ result->from = false;
+ else
+ goto error;
- if (!error && strcasecmp(token, "from") == 0)
- result->from = true;
- else if (!error && strcasecmp(token, "to") == 0)
- result->from = false;
- else
- error = true;
- }
- }
+ token = strtokx(NULL, whitespace, NULL, "'",
+ '\\', true, pset.encoding);
+ if (!token)
+ goto error;
+
+ if (strcasecmp(token, "stdin") == 0 ||
+ strcasecmp(token, "stdout") == 0)
+ result->file = NULL;
+ else
+ result->file = xstrdup(token);
+
+ token = strtokx(NULL, whitespace, NULL, NULL,
+ 0, false, pset.encoding);
- if (!error)
+ /*
+ * Allows old COPY syntax for backward compatibility
+ * 2002-06-19
+ */
+ if (token && strcasecmp(token, "using") == 0)
{
- token = strtokx(NULL, " \t\n\r", "'", '\\', &quote, NULL, pset.encoding);
+ token = strtokx(NULL, whitespace, NULL, NULL,
+ 0, false, pset.encoding);
+ if (!(token && strcasecmp(token, "delimiters") == 0))
+ goto error;
+ token = strtokx(NULL, whitespace, NULL, "'",
+ '\\', false, pset.encoding);
if (!token)
- error = true;
- else if (!quote && (strcasecmp(token, "stdin") == 0 || strcasecmp(token, "stdout") == 0))
- result->file = NULL;
- else
- result->file = xstrdup(token);
+ goto error;
+ result->delim = xstrdup(token);
+ token = strtokx(NULL, whitespace, NULL, NULL,
+ 0, false, pset.encoding);
}
- if (!error)
+ if (token)
{
- token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
- if (token)
+ if (strcasecmp(token, "with") != 0)
+ goto error;
+ while ((token = strtokx(NULL, whitespace, NULL, NULL,
+ 0, false, pset.encoding)) != NULL)
{
- /*
- * Allows old COPY syntax for backward compatibility
- * 2002-06-19
- */
- if (strcasecmp(token, "using") == 0)
+ if (strcasecmp(token, "delimiter") == 0)
{
- token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
- if (token && strcasecmp(token, "delimiters") == 0)
- {
- token = strtokx(NULL, " \t\n\r", "'", '\\', NULL, NULL, pset.encoding);
- if (token)
- {
- result->delim = xstrdup(token);
- token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding);
- }
- else
- error = true;
- }
+ token = strtokx(NULL, whitespace, NULL, "'",
+ '\\', false, pset.encoding);
+ if (token && strcasecmp(token, "as") == 0)
+ token = strtokx(NULL, whitespace, NULL, "'",
+ '\\', false, pset.encoding);
+ if (token)
+ result->delim = xstrdup(token);
else
- error = true;
+ goto error;
}
- }
- }
-
- if (!error && token)
- {
- if (strcasecmp(token, "with") == 0)
- {
- while (!error && (token = strtokx(NULL, " \t\n\r", NULL, '\\', NULL, NULL, pset.encoding)))
+ else if (strcasecmp(token, "null") == 0)
{
- if (strcasecmp(token, "delimiter") == 0)
- {
- token = strtokx(NULL, " \t\n\r", "'", '\\', NULL, NULL, pset.encoding);
- if (token && strcasecmp(token, "as") == 0)
- token = strtokx(NULL, " \t\n\r", "'", '\\', NULL, NULL, pset.encoding);
- if (token)
- result->delim = xstrdup(token);
- else
- error = true;
- }
- else if (strcasecmp(token, "null") == 0)
- {
- token = strtokx(NULL, " \t\n\r", "'", '\\', NULL, NULL, pset.encoding);
- if (token && strcasecmp(token, "as") == 0)
- token = strtokx(NULL, " \t\n\r", "'", '\\', NULL, NULL, pset.encoding);
- if (token)
- result->null = xstrdup(token);
- else
- error = true;
- }
+ token = strtokx(NULL, whitespace, NULL, "'",
+ '\\', false, pset.encoding);
+ if (token && strcasecmp(token, "as") == 0)
+ token = strtokx(NULL, whitespace, NULL, "'",
+ '\\', false, pset.encoding);
+ if (token)
+ result->null = xstrdup(token);
else
- error = true;
+ goto error;
}
+ else
+ goto error;
}
- else
- error = true;
}
free(line);
- if (error)
- {
- if (token)
- psql_error("\\copy: parse error at '%s'\n", token);
- else
- psql_error("\\copy: parse error at end of line\n");
- free_copy_options(result);
- return NULL;
- }
+ return result;
+
+error:
+ if (token)
+ psql_error("\\copy: parse error at '%s'\n", token);
else
- return result;
+ psql_error("\\copy: parse error at end of line\n");
+ free_copy_options(result);
+ free(line);
+
+ return NULL;
}
@@ -272,7 +327,11 @@ do_copy(const char *args)
if (options->binary)
appendPQExpBuffer(&query, "BINARY ");
- appendPQExpBuffer(&query, "\"%s\" ", options->table);
+ appendPQExpBuffer(&query, "%s ", options->table);
+
+ if (options->column_list)
+ appendPQExpBuffer(&query, "%s ", options->column_list);
+
/* Uses old COPY syntax for backward compatibility 2002-06-19 */
if (options->oids)
appendPQExpBuffer(&query, "WITH OIDS ");
@@ -285,10 +344,22 @@ do_copy(const char *args)
/* Uses old COPY syntax for backward compatibility 2002-06-19 */
if (options->delim)
- appendPQExpBuffer(&query, " USING DELIMITERS '%s'", options->delim);
+ {
+ if (options->delim[0] == '\'')
+ appendPQExpBuffer(&query, " USING DELIMITERS %s",
+ options->delim);
+ else
+ appendPQExpBuffer(&query, " USING DELIMITERS '%s'",
+ options->delim);
+ }
if (options->null)
- appendPQExpBuffer(&query, " WITH NULL AS '%s'", options->null);
+ {
+ if (options->null[0] == '\'')
+ appendPQExpBuffer(&query, " WITH NULL AS %s", options->null);
+ else
+ appendPQExpBuffer(&query, " WITH NULL AS '%s'", options->null);
+ }
if (options->from)
{
diff --git a/src/bin/psql/stringutils.c b/src/bin/psql/stringutils.c
index 8ff58c46430..0401c92718c 100644
--- a/src/bin/psql/stringutils.c
+++ b/src/bin/psql/stringutils.c
@@ -1,45 +1,61 @@
/*
* psql - the PostgreSQL interactive terminal
*
- * Copyright 2000 by PostgreSQL Global Development Group
+ * Copyright 2000-2002 by PostgreSQL Global Development Group
*
- * $Header: /cvsroot/pgsql/src/bin/psql/stringutils.c,v 1.30 2002/08/27 20:16:49 petere Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/stringutils.c,v 1.31 2002/10/19 00:22:14 tgl Exp $
*/
#include "postgres_fe.h"
-#include "stringutils.h"
-#include "settings.h"
-#include <ctype.h>
#include <assert.h>
+#include <ctype.h>
#include "libpq-fe.h"
+#include "settings.h"
+#include "stringutils.h"
-
-static void unescape_quotes(char *source, int quote, int escape);
+static void strip_quotes(char *source, char quote, char escape, int encoding);
/*
* Replacement for strtok() (a.k.a. poor man's flex)
*
- * The calling convention is similar to that of strtok.
+ * Splits a string into tokens, returning one token per call, then NULL
+ * when no more tokens exist in the given string.
+ *
+ * The calling convention is similar to that of strtok, but with more
+ * frammishes.
+ *
* s - string to parse, if NULL continue parsing the last string
- * delim - set of characters that delimit tokens (usually whitespace)
- * quote - set of characters that quote stuff, they're not part of the token
- * escape - character than can quote quotes
- * was_quoted - if not NULL, stores the quoting character if any was encountered
- * token_pos - if not NULL, receives a count to the start of the token in the
- * parsed string
+ * whitespace - set of whitespace characters that separate tokens
+ * delim - set of non-whitespace separator characters (or NULL)
+ * quote - set of characters that can quote a token (NULL if none)
+ * escape - character that can quote quotes (0 if none)
+ * del_quotes - if TRUE, strip quotes from the returned token, else return
+ * it exactly as found in the string
+ * encoding - the active character-set encoding
+ *
+ * Characters in 'delim', if any, will be returned as single-character
+ * tokens unless part of a quoted token.
+ *
+ * Double occurences of the quoting character are always taken to represent
+ * a single quote character in the data. If escape isn't 0, then escape
+ * followed by anything (except \0) is a data character too.
*
* Note that the string s is _not_ overwritten in this implementation.
+ *
+ * NB: it's okay to vary delim, quote, and escape from one call to the
+ * next on a single source string, but changing whitespace is a bad idea
+ * since you might lose data.
*/
char *
strtokx(const char *s,
+ const char *whitespace,
const char *delim,
const char *quote,
- int escape,
- char *was_quoted,
- unsigned int *token_pos,
+ char escape,
+ bool del_quotes,
int encoding)
{
static char *storage = NULL;/* store the local copy of the users
@@ -50,23 +66,32 @@ strtokx(const char *s,
/* variously abused variables: */
unsigned int offset;
char *start;
- char *cp = NULL;
+ char *p;
if (s)
{
free(storage);
- storage = strdup(s);
+ /*
+ * We may need extra space to insert delimiter nulls for adjacent
+ * tokens. 2X the space is a gross overestimate, but it's
+ * unlikely that this code will be used on huge strings anyway.
+ */
+ storage = (char *) malloc(2 * strlen(s) + 1);
+ if (!storage)
+ return NULL; /* really "out of memory" */
+ strcpy(storage, s);
string = storage;
}
if (!storage)
return NULL;
- /* skip leading "whitespace" */
- offset = strspn(string, delim);
+ /* skip leading whitespace */
+ offset = strspn(string, whitespace);
+ start = &string[offset];
- /* end of string reached */
- if (string[offset] == '\0')
+ /* end of string reached? */
+ if (*start == '\0')
{
/* technically we don't need to free here, but we're nice */
free(storage);
@@ -75,118 +100,165 @@ strtokx(const char *s,
return NULL;
}
- /* test if quoting character */
- if (quote)
- cp = strchr(quote, string[offset]);
-
- if (cp)
+ /* test if delimiter character */
+ if (delim && strchr(delim, *start))
{
- /* okay, we have a quoting character, now scan for the closer */
- char *p;
+ /*
+ * If not at end of string, we need to insert a null to terminate
+ * the returned token. We can just overwrite the next character
+ * if it happens to be in the whitespace set ... otherwise move over
+ * the rest of the string to make room. (This is why we allocated
+ * extra space above).
+ */
+ p = start + 1;
+ if (*p != '\0')
+ {
+ if (!strchr(whitespace, *p))
+ memmove(p + 1, p, strlen(p) + 1);
+ *p = '\0';
+ string = p + 1;
+ }
+ else
+ {
+ /* at end of string, so no extra work */
+ string = p;
+ }
- start = &string[offset + 1];
+ return start;
+ }
- if (token_pos)
- *token_pos = start - storage;
+ /* test if quoting character */
+ if (quote && strchr(quote, *start))
+ {
+ /* okay, we have a quoted token, now scan for the closer */
+ char thisquote = *start;
- for (p = start;
- *p && (*p != *cp || *(p - 1) == escape);
- p += PQmblen(p, encoding)
- );
+ for (p = start + 1; *p; p += PQmblen(p, encoding))
+ {
+ if (*p == escape && p[1] != '\0')
+ p++; /* process escaped anything */
+ else if (*p == thisquote && p[1] == thisquote)
+ p++; /* process doubled quote */
+ else if (*p == thisquote)
+ {
+ p++; /* skip trailing quote */
+ break;
+ }
+ }
- /* not yet end of string? */
+ /*
+ * If not at end of string, we need to insert a null to terminate
+ * the returned token. See notes above.
+ */
if (*p != '\0')
{
+ if (!strchr(whitespace, *p))
+ memmove(p + 1, p, strlen(p) + 1);
*p = '\0';
string = p + 1;
- if (was_quoted)
- *was_quoted = *cp;
- unescape_quotes(start, *cp, escape);
- return start;
}
else
{
- if (was_quoted)
- *was_quoted = *cp;
+ /* at end of string, so no extra work */
string = p;
-
- unescape_quotes(start, *cp, escape);
- return start;
}
+
+ /* Clean up the token if caller wants that */
+ if (del_quotes)
+ strip_quotes(start, thisquote, escape, encoding);
+
+ return start;
}
- /* otherwise no quoting character. scan till next delimiter */
- start = &string[offset];
+ /*
+ * Otherwise no quoting character. Scan till next whitespace,
+ * delimiter or quote. NB: at this point, *start is known not to be
+ * '\0', whitespace, delim, or quote, so we will consume at least
+ * one character.
+ */
+ offset = strcspn(start, whitespace);
- if (token_pos)
- *token_pos = start - storage;
+ if (delim)
+ {
+ unsigned int offset2 = strcspn(start, delim);
- offset = strcspn(start, delim);
- if (was_quoted)
- *was_quoted = 0;
+ if (offset > offset2)
+ offset = offset2;
+ }
- if (start[offset] != '\0')
+ if (quote)
{
- start[offset] = '\0';
- string = &start[offset] + 1;
+ unsigned int offset2 = strcspn(start, quote);
- return start;
+ if (offset > offset2)
+ offset = offset2;
+ }
+
+ p = start + offset;
+
+ /*
+ * If not at end of string, we need to insert a null to terminate
+ * the returned token. See notes above.
+ */
+ if (*p != '\0')
+ {
+ if (!strchr(whitespace, *p))
+ memmove(p + 1, p, strlen(p) + 1);
+ *p = '\0';
+ string = p + 1;
}
else
{
- string = &start[offset];
- return start;
+ /* at end of string, so no extra work */
+ string = p;
}
-}
-
+ return start;
+}
/*
- * unescape_quotes
+ * strip_quotes
*
- * Resolves escaped quotes. Used by strtokx above.
+ * Remove quotes from the string at *source. Leading and trailing occurrences
+ * of 'quote' are removed; embedded double occurrences of 'quote' are reduced
+ * to single occurrences; if 'escape' is not 0 then 'escape' removes special
+ * significance of next character.
+ *
+ * Note that the source string is overwritten in-place.
*/
static void
-unescape_quotes(char *source, int quote, int escape)
+strip_quotes(char *source, char quote, char escape, int encoding)
{
- char *p;
- char *destination,
- *tmp;
+ char *src;
+ char *dst;
#ifdef USE_ASSERT_CHECKING
assert(source);
+ assert(quote);
#endif
- destination = calloc(1, strlen(source) + 1);
- if (!destination)
- {
- perror("calloc");
- exit(EXIT_FAILURE);
- }
+ src = dst = source;
- tmp = destination;
+ if (*src && *src == quote)
+ src++; /* skip leading quote */
- for (p = source; *p; p++)
+ while (*src)
{
- char c;
-
- if (*p == escape && *(p + 1) && quote == *(p + 1))
- {
- c = *(p + 1);
- p++;
- }
- else
- c = *p;
-
- *tmp = c;
- tmp++;
+ char c = *src;
+ int i;
+
+ if (c == quote && src[1] == '\0')
+ break; /* skip trailing quote */
+ else if (c == quote && src[1] == quote)
+ src++; /* process doubled quote */
+ else if (c == escape && src[1] != '\0')
+ src++; /* process escaped character */
+
+ i = PQmblen(src, encoding);
+ while (i--)
+ *dst++ = *src++;
}
- /* Terminating null character */
- *tmp = '\0';
-
- strcpy(source, destination);
-
- free(destination);
+ *dst = '\0';
}
diff --git a/src/bin/psql/stringutils.h b/src/bin/psql/stringutils.h
index faafc606d5e..62c8ba9e3af 100644
--- a/src/bin/psql/stringutils.h
+++ b/src/bin/psql/stringutils.h
@@ -1,9 +1,9 @@
/*
* psql - the PostgreSQL interactive terminal
*
- * Copyright 2000 by PostgreSQL Global Development Group
+ * Copyright 2000-2002 by PostgreSQL Global Development Group
*
- * $Header: /cvsroot/pgsql/src/bin/psql/stringutils.h,v 1.17 2001/11/05 17:46:31 momjian Exp $
+ * $Id: stringutils.h,v 1.18 2002/10/19 00:22:14 tgl Exp $
*/
#ifndef STRINGUTILS_H
#define STRINGUTILS_H
@@ -11,11 +11,11 @@
/* The cooler version of strtok() which knows about quotes and doesn't
* overwrite your input */
extern char *strtokx(const char *s,
+ const char *whitespace,
const char *delim,
const char *quote,
- int escape,
- char *was_quoted,
- unsigned int *token_pos,
+ char escape,
+ bool del_quotes,
int encoding);
#endif /* STRINGUTILS_H */