postgresql/src/bin/psql/stringutils.c

193 lines
3.5 KiB
C
Raw Normal View History

2000-01-19 00:30:24 +01:00
/*
* psql - the PostgreSQL interactive terminal
*
* Copyright 2000 by PostgreSQL Global Development Group
2000-01-19 00:30:24 +01:00
*
* $Header: /cvsroot/pgsql/src/bin/psql/stringutils.c,v 1.30 2002/08/27 20:16:49 petere Exp $
2000-01-19 00:30:24 +01:00
*/
#include "postgres_fe.h"
#include "stringutils.h"
2001-02-27 09:13:31 +01:00
#include "settings.h"
1999-11-05 00:14:30 +01:00
#include <ctype.h>
#include <assert.h>
2000-02-16 14:15:26 +01:00
#include "libpq-fe.h"
static void unescape_quotes(char *source, int quote, int escape);
/*
* 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
* 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
*
* 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,
int escape,
1999-11-05 00:14:30 +01:00
char *was_quoted,
unsigned int *token_pos,
int encoding)
{
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;
if (s)
{
free(storage);
storage = strdup(s);
string = storage;
}
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-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);
p += PQmblen(p, encoding)
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;
}
}
1999-11-05 00:14:30 +01:00
/* otherwise no quoting character. scan till next delimiter */
start = &string[offset];
1999-11-05 00:14:30 +01:00
if (token_pos)
*token_pos = start - storage;
1999-11-05 00:14:30 +01:00
offset = strcspn(start, delim);
if (was_quoted)
*was_quoted = 0;
1999-11-05 00:14:30 +01:00
if (start[offset] != '\0')
{
start[offset] = '\0';
string = &start[offset] + 1;
1999-11-05 00:14:30 +01:00
return start;
}
else
{
string = &start[offset];
return start;
}
}
/*
* unescape_quotes
*
* Resolves escaped quotes. Used by strtokx above.
*/
static void
unescape_quotes(char *source, int quote, int escape)
{
1999-11-05 00:14:30 +01:00
char *p;
char *destination,
*tmp;
#ifdef USE_ASSERT_CHECKING
1999-11-05 00:14:30 +01:00
assert(source);
#endif
destination = calloc(1, strlen(source) + 1);
1999-11-05 00:14:30 +01:00
if (!destination)
{
perror("calloc");
exit(EXIT_FAILURE);
}
1999-11-05 00:14:30 +01:00
tmp = destination;
1999-11-05 00:14:30 +01:00
for (p = source; *p; p++)
{
char c;
1999-11-05 00:14:30 +01:00
if (*p == escape && *(p + 1) && quote == *(p + 1))
{
c = *(p + 1);
p++;
}
else
c = *p;
1999-11-05 00:14:30 +01:00
*tmp = c;
tmp++;
}
1999-11-05 00:14:30 +01:00
/* Terminating null character */
*tmp = '\0';
1999-11-05 00:14:30 +01:00
strcpy(source, destination);
free(destination);
}