Fossil SCM

fossil-scm / src / glob.c
Blame History Raw 296 lines
1
/*
2
** Copyright (c) 2011 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** This file contains code used to pattern matching using "glob" syntax.
19
*/
20
#include "config.h"
21
#include "glob.h"
22
#include <assert.h>
23
24
/*
25
** Construct and return a string which is an SQL expression that will
26
** be TRUE if value zVal matches any of the GLOB expressions in the list
27
** zGlobList. For example:
28
**
29
** zVal: "x"
30
** zGlobList: "*.o,*.obj"
31
**
32
** Result: "(x GLOB '*.o' OR x GLOB '*.obj')"
33
**
34
** Commas and whitespace are considered to be element delimiters. Each
35
** element of the GLOB list may optionally be enclosed in either '...' or
36
** "...". This allows commas and/or whitespace to be used in the elements
37
** themselves.
38
**
39
** The returned string is owned by the caller, who must fossil_free()
40
** it.
41
*/
42
char *glob_expr(const char *zVal, const char *zGlobList){
43
Blob expr;
44
const char *zSep = "(";
45
int nTerm = 0;
46
int i;
47
int cTerm;
48
49
if( zGlobList==0 || zGlobList[0]==0 ) return fossil_strdup("0");
50
blob_zero(&expr);
51
while( zGlobList[0] ){
52
while( fossil_isspace(zGlobList[0]) || zGlobList[0]==',' ){
53
zGlobList++; /* Skip leading commas, spaces, and newlines */
54
}
55
if( zGlobList[0]==0 ) break;
56
if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){
57
cTerm = zGlobList[0];
58
zGlobList++;
59
}else{
60
cTerm = ',';
61
}
62
/* Find the next delimiter (or the end of the string). */
63
for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){
64
if( cTerm!=',' ) continue; /* If quoted, keep going. */
65
if( fossil_isspace(zGlobList[i]) ) break; /* If space, stop. */
66
}
67
blob_appendf(&expr, "%s%s GLOB '%#q'", zSep, zVal, i, zGlobList);
68
zSep = " OR ";
69
if( cTerm!=',' && zGlobList[i] ) i++;
70
zGlobList += i;
71
if( zGlobList[0] ) zGlobList++;
72
nTerm++;
73
}
74
if( nTerm ){
75
blob_appendf(&expr, ")");
76
return blob_str(&expr);
77
}else{
78
return fossil_strdup("0");
79
}
80
}
81
82
#if INTERFACE
83
/*
84
** A Glob object holds a set of patterns read to be matched against
85
** a string.
86
*/
87
struct Glob {
88
int nPattern; /* Number of patterns */
89
char **azPattern; /* Array of pointers to patterns */
90
};
91
#endif /* INTERFACE */
92
93
/*
94
** zPatternList is a comma- or whitespace-separated list of glob patterns.
95
** Parse that list and use it to create a new Glob object.
96
**
97
** Elements of the glob list may be optionally enclosed in single- or
98
** double-quotes. This allows commas and whitespace to be part of a
99
** glob pattern.
100
**
101
** Leading and trailing spaces on glob patterns are ignored unless quoted.
102
**
103
** An empty or null pattern list results in a null glob, which will
104
** match nothing.
105
*/
106
Glob *glob_create(const char *zPatternList){
107
int nList; /* Size of zPatternList in bytes */
108
int i; /* Loop counters */
109
Glob *p; /* The glob being created */
110
char *z; /* Copy of the pattern list */
111
char delimiter; /* '\'' or '\"' or 0 */
112
113
if( zPatternList==0 || zPatternList[0]==0 ) return 0;
114
nList = strlen(zPatternList);
115
p = fossil_malloc( sizeof(*p) + nList+1 );
116
memset(p, 0, sizeof(*p));
117
z = (char*)&p[1];
118
memcpy(z, zPatternList, nList+1);
119
while( z[0] ){
120
while( fossil_isspace(z[0]) || z[0]==',' ){
121
z++; /* Skip leading commas, spaces, and newlines */
122
}
123
if( z[0]==0 ) break;
124
if( z[0]=='\'' || z[0]=='"' ){
125
delimiter = z[0];
126
z++;
127
}else{
128
delimiter = ',';
129
}
130
p->azPattern = fossil_realloc(p->azPattern, (p->nPattern+1)*sizeof(char*) );
131
p->azPattern[p->nPattern++] = z;
132
/* Find the next delimiter (or the end of the string). */
133
for(i=0; z[i] && z[i]!=delimiter &&
134
!(delimiter==',' && fossil_isspace(z[i])); i++){
135
/* keep looking for the end of the glob pattern */
136
}
137
if( z[i]==0 ) break;
138
z[i] = 0;
139
z += i+1;
140
}
141
return p;
142
}
143
144
/*
145
** Return TRUE if zString matches any of the GLOB patterns in the
146
** string zPatternList.
147
**
148
** This is a like calling glob_create(), glob_match(), and glob_free()
149
** in sequence, without the overhead of creating the reusable Glob object.
150
** Use this for one-time matches against a comma-separated GLOB list.
151
*/
152
int glob_multi_match(const char *zPatternList, const char *zString){
153
int i; /* Loop counters */
154
int n = 0; /* Pattern counter */
155
const char *z; /* Current GLOB pattern */
156
char delimiter; /* '\'' or '\"' or 0 */
157
int rc; /* Result of comparison */
158
char zPat[100]; /* Copy of just the current pattern */
159
160
if( zPatternList==0 ) return 0;
161
z = zPatternList;
162
while( z[0] ){
163
while( fossil_isspace(z[0]) || z[0]==',' ){
164
z++; /* Skip leading commas, spaces, and newlines */
165
}
166
if( z[0]==0 ) break;
167
if( z[0]=='\'' || z[0]=='"' ){
168
delimiter = z[0];
169
z++;
170
}else{
171
delimiter = ',';
172
}
173
/* Find the next delimiter (or the end of the string). */
174
for(i=0; z[i] && z[i]!=delimiter &&
175
!(delimiter==',' && fossil_isspace(z[i])); i++){
176
/* keep looking for the end of the glob pattern */
177
}
178
n++;
179
if( i>sizeof(zPat)-1 ){
180
char *zMPat = fossil_strndup(z, i);
181
rc = sqlite3_strglob(zMPat, zString);
182
fossil_free(zMPat);
183
}else{
184
memcpy(zPat, z, i);
185
zPat[i] = 0;
186
rc = sqlite3_strglob(zPat, zString);
187
}
188
if( rc==0 ) return n;
189
if( z[i]==0 ) break;
190
z += i+1;
191
}
192
return 0;
193
}
194
195
/*
196
** Return true (non-zero) if zString matches any of the patterns in
197
** the Glob. The value returned is actually a 1-based index of the pattern
198
** that matched. Return 0 if none of the patterns match zString.
199
**
200
** A NULL glob matches nothing.
201
*/
202
int glob_match(Glob *pGlob, const char *zString){
203
int i;
204
if( pGlob==0 ) return 0;
205
for(i=0; i<pGlob->nPattern; i++){
206
if( sqlite3_strglob(pGlob->azPattern[i], zString)==0 ) return i+1;
207
}
208
return 0;
209
}
210
211
/*
212
** Free all memory associated with the given Glob object
213
*/
214
void glob_free(Glob *pGlob){
215
if( pGlob ){
216
fossil_free(pGlob->azPattern);
217
fossil_free(pGlob);
218
}
219
}
220
221
/*
222
** Appends the given glob to the given buffer in the form of a
223
** JS/JSON-compatible array. It requires that pDest have been
224
** initialized. If pGlob is NULL or empty it emits [] (an empty
225
** array).
226
*/
227
void glob_render_json_to_blob(Glob *pGlob, Blob *pDest){
228
int i = 0;
229
blob_append(pDest, "[", 1);
230
for( ; pGlob && i < pGlob->nPattern; ++i ){
231
if(i){
232
blob_append(pDest, ",", 1);
233
}
234
blob_appendf(pDest, "%!j", pGlob->azPattern[i]);
235
}
236
blob_append(pDest, "]", 1);
237
}
238
/*
239
** Functionally equivalent to glob_render_json_to_blob()
240
** but outputs via cgi_print().
241
*/
242
void glob_render_json_to_cgi(Glob *pGlob){
243
int i = 0;
244
CX("[");
245
for( ; pGlob && i < pGlob->nPattern; ++i ){
246
if(i){
247
CX(",");
248
}
249
CX("%!j", pGlob->azPattern[i]);
250
}
251
CX("]");
252
}
253
254
/*
255
** COMMAND: test-glob
256
**
257
** Usage: %fossil test-glob PATTERN STRING...
258
**
259
** PATTERN is a comma- and whitespace-separated list of optionally
260
** quoted glob patterns. Show which of the STRINGs that follow match
261
** the PATTERN.
262
**
263
** If PATTERN begins with "@" the rest of the pattern is understood
264
** to be a setting name (such as binary-glob, crln-glob, or encoding-glob)
265
** and the value of that setting is used as the actually glob pattern.
266
**
267
** The output consists of two numbers and a STRING. The first number
268
** is the result of glob_match() and the second is the result of
269
** glob_multi_match().
270
*/
271
void glob_test_cmd(void){
272
Glob *pGlob;
273
int i;
274
char *zPattern;
275
if( g.argc<4 ) usage("PATTERN STRING ...");
276
zPattern = g.argv[2];
277
if( zPattern[0]=='@' ){
278
db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
279
zPattern = db_get(zPattern+1, 0);
280
if( zPattern==0 ) fossil_fatal("no such setting: %s", g.argv[2]+1);
281
fossil_print("GLOB pattern: %s\n", zPattern);
282
}
283
fossil_print("SQL expression: %s\n", glob_expr("x", zPattern));
284
pGlob = glob_create(zPattern);
285
for(i=0; i<pGlob->nPattern; i++){
286
fossil_print("pattern[%d] = [%s]\n", i, pGlob->azPattern[i]);
287
}
288
for(i=3; i<g.argc; i++){
289
fossil_print("%d %d %s\n",
290
glob_match(pGlob, g.argv[i]),
291
glob_multi_match(zPattern, g.argv[i]),
292
g.argv[i]);
293
}
294
glob_free(pGlob);
295
}
296

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button