postgres/src/bin/psql/stringutils.c
Tom Lane d08741eab5 Restructure the key include files per recent pghackers discussion: there
are now separate files "postgres.h" and "postgres_fe.h", which are meant
to be the primary include files for backend .c files and frontend .c files
respectively.  By default, only include files meant for frontend use are
installed into the installation include directory.  There is a new make
target 'make install-all-headers' that adds the whole content of the
src/include tree to the installed fileset, for use by people who want to
develop server-side code without keeping the complete source tree on hand.
Cleaned up a whole lot of crufty and inconsistent header inclusions.
2001-02-10 02:31:31 +00:00

197 lines
3.5 KiB
C

/*
* psql - the PostgreSQL interactive terminal
*
* Copyright 2000 by PostgreSQL Global Development Group
*
* $Header: /cvsroot/pgsql/src/bin/psql/stringutils.c,v 1.27 2001/02/10 02:31:28 tgl Exp $
*/
#include "postgres_fe.h"
#include "stringutils.h"
#include <ctype.h>
#include <assert.h>
#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.
* 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
*
* Note that the string s is _not_ overwritten in this implementation.
*/
char *
strtokx(const char *s,
const char *delim,
const char *quote,
int escape,
char *was_quoted,
unsigned int *token_pos,
int encoding)
{
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;
#ifndef MULTIBYTE
(void) encoding; /* not used */
#endif
if (s)
{
free(storage);
storage = strdup(s);
string = storage;
}
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;
}
/* 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);
#ifdef MULTIBYTE
p += PQmblen(p, encoding)
#else
p++
#endif
);
/* 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;
}
}
/* otherwise no quoting character. scan till next delimiter */
start = &string[offset];
if (token_pos)
*token_pos = start - storage;
offset = strcspn(start, delim);
if (was_quoted)
*was_quoted = 0;
if (start[offset] != '\0')
{
start[offset] = '\0';
string = &start[offset] + 1;
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)
{
char *p;
char *destination,
*tmp;
#ifdef USE_ASSERT_CHECKING
assert(source);
#endif
destination = calloc(1, strlen(source) + 1);
if (!destination)
{
perror("calloc");
exit(EXIT_FAILURE);
}
tmp = destination;
for (p = source; *p; p++)
{
char c;
if (*p == escape && *(p + 1) && quote == *(p + 1))
{
c = *(p + 1);
p++;
}
else
c = *p;
*tmp = c;
tmp++;
}
/* Terminating null character */
*tmp = '\0';
strcpy(source, destination);
}