~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/bin/psql/stringutils.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * psql - the PostgreSQL interactive terminal
 
3
 *
 
4
 * Copyright (c) 2000-2005, PostgreSQL Global Development Group
 
5
 *
 
6
 * $PostgreSQL: pgsql/src/bin/psql/stringutils.c,v 1.40 2005-01-01 05:43:08 momjian Exp $
 
7
 */
 
8
#include "postgres_fe.h"
 
9
 
 
10
#include <ctype.h>
 
11
 
 
12
#include "libpq-fe.h"
 
13
#include "common.h"
 
14
#include "settings.h"
 
15
#include "stringutils.h"
 
16
 
 
17
 
 
18
static void strip_quotes(char *source, char quote, char escape, int encoding);
 
19
 
 
20
 
 
21
/*
 
22
 * Replacement for strtok() (a.k.a. poor man's flex)
 
23
 *
 
24
 * Splits a string into tokens, returning one token per call, then NULL
 
25
 * when no more tokens exist in the given string.
 
26
 *
 
27
 * The calling convention is similar to that of strtok, but with more
 
28
 * frammishes.
 
29
 *
 
30
 * s -                  string to parse, if NULL continue parsing the last string
 
31
 * whitespace - set of whitespace characters that separate tokens
 
32
 * delim -              set of non-whitespace separator characters (or NULL)
 
33
 * quote -              set of characters that can quote a token (NULL if none)
 
34
 * escape -             character that can quote quotes (0 if none)
 
35
 * del_quotes - if TRUE, strip quotes from the returned token, else return
 
36
 *                              it exactly as found in the string
 
37
 * encoding -   the active character-set encoding
 
38
 *
 
39
 * Characters in 'delim', if any, will be returned as single-character
 
40
 * tokens unless part of a quoted token.
 
41
 *
 
42
 * Double occurrences of the quoting character are always taken to represent
 
43
 * a single quote character in the data.  If escape isn't 0, then escape
 
44
 * followed by anything (except \0) is a data character too.
 
45
 *
 
46
 * Note that the string s is _not_ overwritten in this implementation.
 
47
 *
 
48
 * NB: it's okay to vary delim, quote, and escape from one call to the
 
49
 * next on a single source string, but changing whitespace is a bad idea
 
50
 * since you might lose data.
 
51
 */
 
52
char *
 
53
strtokx(const char *s,
 
54
                const char *whitespace,
 
55
                const char *delim,
 
56
                const char *quote,
 
57
                char escape,
 
58
                bool del_quotes,
 
59
                int encoding)
 
60
{
 
61
        static char *storage = NULL;/* store the local copy of the users
 
62
                                                                 * string here */
 
63
        static char *string = NULL; /* pointer into storage where to continue
 
64
                                                                 * on next call */
 
65
 
 
66
        /* variously abused variables: */
 
67
        unsigned int offset;
 
68
        char       *start;
 
69
        char       *p;
 
70
 
 
71
        if (s)
 
72
        {
 
73
                free(storage);
 
74
 
 
75
                /*
 
76
                 * We may need extra space to insert delimiter nulls for adjacent
 
77
                 * tokens.      2X the space is a gross overestimate, but it's
 
78
                 * unlikely that this code will be used on huge strings anyway.
 
79
                 */
 
80
                storage = pg_malloc(2 * strlen(s) + 1);
 
81
                strcpy(storage, s);
 
82
                string = storage;
 
83
        }
 
84
 
 
85
        if (!storage)
 
86
                return NULL;
 
87
 
 
88
        /* skip leading whitespace */
 
89
        offset = strspn(string, whitespace);
 
90
        start = &string[offset];
 
91
 
 
92
        /* end of string reached? */
 
93
        if (*start == '\0')
 
94
        {
 
95
                /* technically we don't need to free here, but we're nice */
 
96
                free(storage);
 
97
                storage = NULL;
 
98
                string = NULL;
 
99
                return NULL;
 
100
        }
 
101
 
 
102
        /* test if delimiter character */
 
103
        if (delim && strchr(delim, *start))
 
104
        {
 
105
                /*
 
106
                 * If not at end of string, we need to insert a null to terminate
 
107
                 * the returned token.  We can just overwrite the next character
 
108
                 * if it happens to be in the whitespace set ... otherwise move
 
109
                 * over the rest of the string to make room.  (This is why we
 
110
                 * allocated extra space above).
 
111
                 */
 
112
                p = start + 1;
 
113
                if (*p != '\0')
 
114
                {
 
115
                        if (!strchr(whitespace, *p))
 
116
                                memmove(p + 1, p, strlen(p) + 1);
 
117
                        *p = '\0';
 
118
                        string = p + 1;
 
119
                }
 
120
                else
 
121
                {
 
122
                        /* at end of string, so no extra work */
 
123
                        string = p;
 
124
                }
 
125
 
 
126
                return start;
 
127
        }
 
128
 
 
129
        /* test if quoting character */
 
130
        if (quote && strchr(quote, *start))
 
131
        {
 
132
                /* okay, we have a quoted token, now scan for the closer */
 
133
                char            thisquote = *start;
 
134
 
 
135
                for (p = start + 1; *p; p += PQmblen(p, encoding))
 
136
                {
 
137
                        if (*p == escape && p[1] != '\0')
 
138
                                p++;                    /* process escaped anything */
 
139
                        else if (*p == thisquote && p[1] == thisquote)
 
140
                                p++;                    /* process doubled quote */
 
141
                        else if (*p == thisquote)
 
142
                        {
 
143
                                p++;                    /* skip trailing quote */
 
144
                                break;
 
145
                        }
 
146
                }
 
147
 
 
148
                /*
 
149
                 * If not at end of string, we need to insert a null to terminate
 
150
                 * the returned token.  See notes above.
 
151
                 */
 
152
                if (*p != '\0')
 
153
                {
 
154
                        if (!strchr(whitespace, *p))
 
155
                                memmove(p + 1, p, strlen(p) + 1);
 
156
                        *p = '\0';
 
157
                        string = p + 1;
 
158
                }
 
159
                else
 
160
                {
 
161
                        /* at end of string, so no extra work */
 
162
                        string = p;
 
163
                }
 
164
 
 
165
                /* Clean up the token if caller wants that */
 
166
                if (del_quotes)
 
167
                        strip_quotes(start, thisquote, escape, encoding);
 
168
 
 
169
                return start;
 
170
        }
 
171
 
 
172
        /*
 
173
         * Otherwise no quoting character.      Scan till next whitespace,
 
174
         * delimiter or quote.  NB: at this point, *start is known not to be
 
175
         * '\0', whitespace, delim, or quote, so we will consume at least one
 
176
         * character.
 
177
         */
 
178
        offset = strcspn(start, whitespace);
 
179
 
 
180
        if (delim)
 
181
        {
 
182
                unsigned int offset2 = strcspn(start, delim);
 
183
 
 
184
                if (offset > offset2)
 
185
                        offset = offset2;
 
186
        }
 
187
 
 
188
        if (quote)
 
189
        {
 
190
                unsigned int offset2 = strcspn(start, quote);
 
191
 
 
192
                if (offset > offset2)
 
193
                        offset = offset2;
 
194
        }
 
195
 
 
196
        p = start + offset;
 
197
 
 
198
        /*
 
199
         * If not at end of string, we need to insert a null to terminate the
 
200
         * returned token.      See notes above.
 
201
         */
 
202
        if (*p != '\0')
 
203
        {
 
204
                if (!strchr(whitespace, *p))
 
205
                        memmove(p + 1, p, strlen(p) + 1);
 
206
                *p = '\0';
 
207
                string = p + 1;
 
208
        }
 
209
        else
 
210
        {
 
211
                /* at end of string, so no extra work */
 
212
                string = p;
 
213
        }
 
214
 
 
215
        return start;
 
216
}
 
217
 
 
218
 
 
219
/*
 
220
 * strip_quotes
 
221
 *
 
222
 * Remove quotes from the string at *source.  Leading and trailing occurrences
 
223
 * of 'quote' are removed; embedded double occurrences of 'quote' are reduced
 
224
 * to single occurrences; if 'escape' is not 0 then 'escape' removes special
 
225
 * significance of next character.
 
226
 *
 
227
 * Note that the source string is overwritten in-place.
 
228
 */
 
229
static void
 
230
strip_quotes(char *source, char quote, char escape, int encoding)
 
231
{
 
232
        char       *src;
 
233
        char       *dst;
 
234
 
 
235
        psql_assert(source);
 
236
        psql_assert(quote);
 
237
 
 
238
        src = dst = source;
 
239
 
 
240
        if (*src && *src == quote)
 
241
                src++;                                  /* skip leading quote */
 
242
 
 
243
        while (*src)
 
244
        {
 
245
                char            c = *src;
 
246
                int                     i;
 
247
 
 
248
                if (c == quote && src[1] == '\0')
 
249
                        break;                          /* skip trailing quote */
 
250
                else if (c == quote && src[1] == quote)
 
251
                        src++;                          /* process doubled quote */
 
252
                else if (c == escape && src[1] != '\0')
 
253
                        src++;                          /* process escaped character */
 
254
 
 
255
                i = PQmblen(src, encoding);
 
256
                while (i--)
 
257
                        *dst++ = *src++;
 
258
        }
 
259
 
 
260
        *dst = '\0';
 
261
}