1999-11-04 22:56:02 +01:00
|
|
|
#include <config.h>
|
|
|
|
#include <c.h>
|
|
|
|
#include "stringutils.h"
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
#include <ctype.h>
|
1999-11-04 22:56:02 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
1999-11-26 18:26:38 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
#include <stdio.h>
|
1996-11-26 04:20:35 +01:00
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
#include <postgres.h>
|
1997-02-13 09:32:20 +01:00
|
|
|
#ifndef HAVE_STRDUP
|
1999-11-04 22:56:02 +01:00
|
|
|
#include <strdup.h>
|
1996-11-26 08:39:11 +01:00
|
|
|
#endif
|
1999-11-04 22:56:02 +01:00
|
|
|
#include <libpq-fe.h>
|
|
|
|
|
1996-11-26 04:20:35 +01:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
static void
|
1999-11-05 00:14:30 +01:00
|
|
|
unescape_quotes(char *source, char quote, char 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,
|
|
|
|
char escape,
|
|
|
|
char *was_quoted,
|
|
|
|
unsigned int *token_pos)
|
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;
|
|
|
|
|
|
|
|
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
|
1999-11-05 00:14:30 +01:00
|
|
|
p += PQmblen(p)
|
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
|
|
|
|
unescape_quotes(char *source, char quote, char 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
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
destination = (char *) calloc(1, strlen(source) + 1);
|
|
|
|
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
|
|
|
}
|