2000-01-19 00:30:24 +01:00
|
|
|
/*
|
|
|
|
* psql - the PostgreSQL interactive terminal
|
|
|
|
*
|
2000-01-29 17:58:54 +01:00
|
|
|
* Copyright 2000 by PostgreSQL Global Development Group
|
2000-01-19 00:30:24 +01:00
|
|
|
*
|
2000-04-12 19:17:23 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/bin/psql/stringutils.c,v 1.26 2000/04/12 17:16:23 momjian Exp $
|
2000-01-19 00:30:24 +01:00
|
|
|
*/
|
2000-02-16 14:15:26 +01:00
|
|
|
#include "postgres.h"
|
1999-11-04 22:56:02 +01:00
|
|
|
#include "stringutils.h"
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
#include <ctype.h>
|
1999-11-04 22:56:02 +01:00
|
|
|
#include <assert.h>
|
1996-11-26 04:20:35 +01:00
|
|
|
|
2000-02-16 14:15:26 +01:00
|
|
|
#include "libpq-fe.h"
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1996-11-26 04:20:35 +01:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
static void unescape_quotes(char *source, int quote, int escape);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1996-11-14 17:08:05 +01:00
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
/*
|
|
|
|
* Replacement for strtok() (a.k.a. poor man's flex)
|
|
|
|
*
|
|
|
|
* The calling convention is similar to that of strtok.
|
1999-11-05 00:14:30 +01:00
|
|
|
* 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
|
1999-11-04 22:56:02 +01:00
|
|
|
* was_quoted - if not NULL, stores the quoting character if any was encountered
|
1999-11-05 00:14:30 +01:00
|
|
|
* token_pos - if not NULL, receives a count to the start of the token in the
|
|
|
|
* parsed string
|
1999-11-04 22:56:02 +01:00
|
|
|
*
|
|
|
|
* Note that the string s is _not_ overwritten in this implementation.
|
|
|
|
*/
|
1999-11-05 00:14:30 +01:00
|
|
|
char *
|
|
|
|
strtokx(const char *s,
|
|
|
|
const char *delim,
|
|
|
|
const char *quote,
|
2000-02-08 00:10:11 +01:00
|
|
|
int escape,
|
1999-11-05 00:14:30 +01:00
|
|
|
char *was_quoted,
|
2000-01-15 06:38:50 +01:00
|
|
|
unsigned int *token_pos,
|
|
|
|
int encoding)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-11-05 00:14:30 +01:00
|
|
|
static char *storage = NULL;/* store the local copy of the users
|
|
|
|
* string here */
|
|
|
|
static char *string = NULL; /* pointer into storage where to continue
|
|
|
|
* on next call */
|
|
|
|
|
|
|
|
/* variously abused variables: */
|
|
|
|
unsigned int offset;
|
|
|
|
char *start;
|
|
|
|
char *cp = NULL;
|
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
#ifndef MULTIBYTE
|
2000-04-12 19:17:23 +02:00
|
|
|
(void) encoding; /* not used */
|
2000-02-08 00:10:11 +01:00
|
|
|
#endif
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (s)
|
|
|
|
{
|
|
|
|
free(storage);
|
|
|
|
storage = strdup(s);
|
|
|
|
string = storage;
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!storage)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* skip leading "whitespace" */
|
|
|
|
offset = strspn(string, delim);
|
|
|
|
|
|
|
|
/* end of string reached */
|
|
|
|
if (string[offset] == '\0')
|
|
|
|
{
|
|
|
|
/* technically we don't need to free here, but we're nice */
|
|
|
|
free(storage);
|
|
|
|
storage = NULL;
|
|
|
|
string = NULL;
|
|
|
|
return NULL;
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* test if quoting character */
|
|
|
|
if (quote)
|
|
|
|
cp = strchr(quote, string[offset]);
|
|
|
|
|
|
|
|
if (cp)
|
|
|
|
{
|
|
|
|
/* okay, we have a quoting character, now scan for the closer */
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
start = &string[offset + 1];
|
|
|
|
|
|
|
|
if (token_pos)
|
|
|
|
*token_pos = start - storage;
|
|
|
|
|
|
|
|
for (p = start;
|
|
|
|
*p && (*p != *cp || *(p - 1) == escape);
|
1999-11-04 22:56:02 +01:00
|
|
|
#ifdef MULTIBYTE
|
2000-01-15 06:38:50 +01:00
|
|
|
p += PQmblen(p, encoding)
|
1999-11-04 22:56:02 +01:00
|
|
|
#else
|
1999-11-05 00:14:30 +01:00
|
|
|
p++
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
1999-11-05 00:14:30 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
/* not yet end of string? */
|
|
|
|
if (*p != '\0')
|
|
|
|
{
|
|
|
|
*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;
|
|
|
|
string = p;
|
|
|
|
|
|
|
|
unescape_quotes(start, *cp, escape);
|
|
|
|
return start;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* otherwise no quoting character. scan till next delimiter */
|
|
|
|
start = &string[offset];
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (token_pos)
|
|
|
|
*token_pos = start - storage;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
offset = strcspn(start, delim);
|
|
|
|
if (was_quoted)
|
|
|
|
*was_quoted = 0;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (start[offset] != '\0')
|
|
|
|
{
|
|
|
|
start[offset] = '\0';
|
|
|
|
string = &start[offset] + 1;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
return start;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
string = &start[offset];
|
|
|
|
return start;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* unescape_quotes
|
|
|
|
*
|
|
|
|
* Resolves escaped quotes. Used by strtokx above.
|
|
|
|
*/
|
|
|
|
static void
|
2000-02-08 00:10:11 +01:00
|
|
|
unescape_quotes(char *source, int quote, int escape)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-11-05 00:14:30 +01:00
|
|
|
char *p;
|
|
|
|
char *destination,
|
|
|
|
*tmp;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
#ifdef USE_ASSERT_CHECKING
|
1999-11-05 00:14:30 +01:00
|
|
|
assert(source);
|
1997-08-19 23:40:56 +02:00
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
destination = calloc(1, strlen(source) + 1);
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!destination)
|
|
|
|
{
|
|
|
|
perror("calloc");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
tmp = destination;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
for (p = source; *p; p++)
|
|
|
|
{
|
|
|
|
char c;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (*p == escape && *(p + 1) && quote == *(p + 1))
|
|
|
|
{
|
|
|
|
c = *(p + 1);
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
c = *p;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
*tmp = c;
|
|
|
|
tmp++;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* Terminating null character */
|
|
|
|
*tmp = '\0';
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
strcpy(source, destination);
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|