Fossil SCM

Add a new setting "regexp-limit" that determines the maximum size of a REGEXP virtual machine. Default value 1000.

drh 2025-09-26 20:07 trunk
Commit 82888a0d35a80944460eff38b34a25d942e141b1778af1e56451d7029361660c
+1 -1
--- src/browse.c
+++ src/browse.c
@@ -747,11 +747,11 @@
747747
}
748748
749749
/* If a regular expression is specified, compile it */
750750
zRE = P("re");
751751
if( zRE ){
752
- re_compile(&pRE, zRE, 0);
752
+ fossil_re_compile(&pRE, zRE, 0);
753753
zREx = mprintf("&re=%T", zRE);
754754
}
755755
cgi_check_for_malice();
756756
757757
/* If the name= parameter is an empty string, make it a NULL pointer */
758758
--- src/browse.c
+++ src/browse.c
@@ -747,11 +747,11 @@
747 }
748
749 /* If a regular expression is specified, compile it */
750 zRE = P("re");
751 if( zRE ){
752 re_compile(&pRE, zRE, 0);
753 zREx = mprintf("&re=%T", zRE);
754 }
755 cgi_check_for_malice();
756
757 /* If the name= parameter is an empty string, make it a NULL pointer */
758
--- src/browse.c
+++ src/browse.c
@@ -747,11 +747,11 @@
747 }
748
749 /* If a regular expression is specified, compile it */
750 zRE = P("re");
751 if( zRE ){
752 fossil_re_compile(&pRE, zRE, 0);
753 zREx = mprintf("&re=%T", zRE);
754 }
755 cgi_check_for_malice();
756
757 /* If the name= parameter is an empty string, make it a NULL pointer */
758
+2 -2
--- src/diff.c
+++ src/diff.c
@@ -3386,11 +3386,11 @@
33863386
find_option("i",0,0);
33873387
find_option("v",0,0);
33883388
diff_options(&DCfg, 0, 0);
33893389
zRe = find_option("regexp","e",1);
33903390
if( zRe ){
3391
- const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
3391
+ const char *zErr = fossil_re_compile(&DCfg.pRe, zRe, 0);
33923392
if( zErr ) fossil_fatal("regex error: %s", zErr);
33933393
}
33943394
verify_all_options();
33953395
if( g.argc!=4 ) usage("FILE1 FILE2");
33963396
blob_zero(&out);
@@ -3429,11 +3429,11 @@
34293429
find_option("i",0,0);
34303430
find_option("v",0,0);
34313431
diff_options(&DCfg, 0, 0);
34323432
zRe = find_option("regexp","e",1);
34333433
if( zRe ){
3434
- const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
3434
+ const char *zErr = fossil_re_compile(&DCfg.pRe, zRe, 0);
34353435
if( zErr ) fossil_fatal("regex error: %s", zErr);
34363436
}
34373437
db_find_and_open_repository(0, 0);
34383438
verify_all_options();
34393439
if( g.argc!=4 ) usage("HASH1 HASH2");
34403440
--- src/diff.c
+++ src/diff.c
@@ -3386,11 +3386,11 @@
3386 find_option("i",0,0);
3387 find_option("v",0,0);
3388 diff_options(&DCfg, 0, 0);
3389 zRe = find_option("regexp","e",1);
3390 if( zRe ){
3391 const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
3392 if( zErr ) fossil_fatal("regex error: %s", zErr);
3393 }
3394 verify_all_options();
3395 if( g.argc!=4 ) usage("FILE1 FILE2");
3396 blob_zero(&out);
@@ -3429,11 +3429,11 @@
3429 find_option("i",0,0);
3430 find_option("v",0,0);
3431 diff_options(&DCfg, 0, 0);
3432 zRe = find_option("regexp","e",1);
3433 if( zRe ){
3434 const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
3435 if( zErr ) fossil_fatal("regex error: %s", zErr);
3436 }
3437 db_find_and_open_repository(0, 0);
3438 verify_all_options();
3439 if( g.argc!=4 ) usage("HASH1 HASH2");
3440
--- src/diff.c
+++ src/diff.c
@@ -3386,11 +3386,11 @@
3386 find_option("i",0,0);
3387 find_option("v",0,0);
3388 diff_options(&DCfg, 0, 0);
3389 zRe = find_option("regexp","e",1);
3390 if( zRe ){
3391 const char *zErr = fossil_re_compile(&DCfg.pRe, zRe, 0);
3392 if( zErr ) fossil_fatal("regex error: %s", zErr);
3393 }
3394 verify_all_options();
3395 if( g.argc!=4 ) usage("FILE1 FILE2");
3396 blob_zero(&out);
@@ -3429,11 +3429,11 @@
3429 find_option("i",0,0);
3430 find_option("v",0,0);
3431 diff_options(&DCfg, 0, 0);
3432 zRe = find_option("regexp","e",1);
3433 if( zRe ){
3434 const char *zErr = fossil_re_compile(&DCfg.pRe, zRe, 0);
3435 if( zErr ) fossil_fatal("regex error: %s", zErr);
3436 }
3437 db_find_and_open_repository(0, 0);
3438 verify_all_options();
3439 if( g.argc!=4 ) usage("HASH1 HASH2");
3440
+4 -4
--- src/dispatch.c
+++ src/dispatch.c
@@ -1155,11 +1155,11 @@
11551155
}else{
11561156
zPattern = mprintf("> ?fossil [-a-z]+ .*\\b%s\\b", zQSub);
11571157
}
11581158
fossil_free(zQTop);
11591159
fossil_free(zQSub);
1160
- re_compile(&pRe, zPattern, 0);
1160
+ fossil_re_compile(&pRe, zPattern, 0);
11611161
fossil_free(zPattern);
11621162
blob_init(&in, z, -1);
11631163
while( blob_line(&in, &line) ){
11641164
if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_size(&line)) ){
11651165
int atStart = 1;
@@ -1252,13 +1252,13 @@
12521252
ReCompiled *pRe = 0;
12531253
Blob in, line;
12541254
int n = 0;
12551255
12561256
if( bAbbrevSubcmd ){
1257
- re_compile(&pRe, "^(Usage: | [a-z][-a-z|]+ .*)", 0);
1257
+ fossil_re_compile(&pRe, "^(Usage: | [a-z][-a-z|]+ .*)", 0);
12581258
}else{
1259
- re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |> ?fossil )", 0);
1259
+ fossil_re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |> ?fossil )", 0);
12601260
}
12611261
blob_init(&in, z, -1);
12621262
while( blob_line(&in, &line) ){
12631263
if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_strlen(&line)) ){
12641264
simplify_usage_line(&line, pOut, bAbbrevSubcmd, zTopic);
@@ -1285,11 +1285,11 @@
12851285
int n = 0;
12861286
int bSubsectionSeen = 0;
12871287
12881288
blob_init(&txt, z, -1);
12891289
blob_init(&subsection, 0, 0);
1290
- re_compile(&pRe, "^ +-.* ", 0);
1290
+ fossil_re_compile(&pRe, "^ +-.* ", 0);
12911291
while( blob_line(&txt, &line) ){
12921292
int len = blob_size(&line);
12931293
unsigned char *zLine = (unsigned char *)blob_buffer(&line);
12941294
if( re_match(pRe, zLine, len) ){
12951295
if( blob_size(&subsection) ){
12961296
--- src/dispatch.c
+++ src/dispatch.c
@@ -1155,11 +1155,11 @@
1155 }else{
1156 zPattern = mprintf("> ?fossil [-a-z]+ .*\\b%s\\b", zQSub);
1157 }
1158 fossil_free(zQTop);
1159 fossil_free(zQSub);
1160 re_compile(&pRe, zPattern, 0);
1161 fossil_free(zPattern);
1162 blob_init(&in, z, -1);
1163 while( blob_line(&in, &line) ){
1164 if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_size(&line)) ){
1165 int atStart = 1;
@@ -1252,13 +1252,13 @@
1252 ReCompiled *pRe = 0;
1253 Blob in, line;
1254 int n = 0;
1255
1256 if( bAbbrevSubcmd ){
1257 re_compile(&pRe, "^(Usage: | [a-z][-a-z|]+ .*)", 0);
1258 }else{
1259 re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |> ?fossil )", 0);
1260 }
1261 blob_init(&in, z, -1);
1262 while( blob_line(&in, &line) ){
1263 if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_strlen(&line)) ){
1264 simplify_usage_line(&line, pOut, bAbbrevSubcmd, zTopic);
@@ -1285,11 +1285,11 @@
1285 int n = 0;
1286 int bSubsectionSeen = 0;
1287
1288 blob_init(&txt, z, -1);
1289 blob_init(&subsection, 0, 0);
1290 re_compile(&pRe, "^ +-.* ", 0);
1291 while( blob_line(&txt, &line) ){
1292 int len = blob_size(&line);
1293 unsigned char *zLine = (unsigned char *)blob_buffer(&line);
1294 if( re_match(pRe, zLine, len) ){
1295 if( blob_size(&subsection) ){
1296
--- src/dispatch.c
+++ src/dispatch.c
@@ -1155,11 +1155,11 @@
1155 }else{
1156 zPattern = mprintf("> ?fossil [-a-z]+ .*\\b%s\\b", zQSub);
1157 }
1158 fossil_free(zQTop);
1159 fossil_free(zQSub);
1160 fossil_re_compile(&pRe, zPattern, 0);
1161 fossil_free(zPattern);
1162 blob_init(&in, z, -1);
1163 while( blob_line(&in, &line) ){
1164 if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_size(&line)) ){
1165 int atStart = 1;
@@ -1252,13 +1252,13 @@
1252 ReCompiled *pRe = 0;
1253 Blob in, line;
1254 int n = 0;
1255
1256 if( bAbbrevSubcmd ){
1257 fossil_re_compile(&pRe, "^(Usage: | [a-z][-a-z|]+ .*)", 0);
1258 }else{
1259 fossil_re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |> ?fossil )", 0);
1260 }
1261 blob_init(&in, z, -1);
1262 while( blob_line(&in, &line) ){
1263 if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_strlen(&line)) ){
1264 simplify_usage_line(&line, pOut, bAbbrevSubcmd, zTopic);
@@ -1285,11 +1285,11 @@
1285 int n = 0;
1286 int bSubsectionSeen = 0;
1287
1288 blob_init(&txt, z, -1);
1289 blob_init(&subsection, 0, 0);
1290 fossil_re_compile(&pRe, "^ +-.* ", 0);
1291 while( blob_line(&txt, &line) ){
1292 int len = blob_size(&line);
1293 unsigned char *zLine = (unsigned char *)blob_buffer(&line);
1294 if( re_match(pRe, zLine, len) ){
1295 if( blob_size(&subsection) ){
1296
+3 -3
--- src/info.c
+++ src/info.c
@@ -936,11 +936,11 @@
936936
@ No such object: %h(zName)
937937
style_finish_page();
938938
return;
939939
}
940940
zRe = P("regex");
941
- if( zRe ) re_compile(&pRe, zRe, 0);
941
+ if( zRe ) fossil_re_compile(&pRe, zRe, 0);
942942
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
943943
zParent = db_text(0,
944944
"SELECT uuid FROM plink, blob"
945945
" WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
946946
rid
@@ -1448,11 +1448,11 @@
14481448
fossil_nice_default();
14491449
blob_init(&qp, 0, 0);
14501450
blob_init(&qpGlob, 0, 0);
14511451
diffType = preferred_diff_type();
14521452
zRe = P("regex");
1453
- if( zRe ) re_compile(&pRe, zRe, 0);
1453
+ if( zRe ) fossil_re_compile(&pRe, zRe, 0);
14541454
zBranch = P("branch");
14551455
if( zBranch && zBranch[0]==0 ) zBranch = 0;
14561456
if( zBranch ){
14571457
blob_appendf(&qp, "branch=%T", zBranch);
14581458
zMergeOrigin = mprintf("merge-in:%s", zBranch);
@@ -2051,11 +2051,11 @@
20512051
}
20522052
db_finalize(&q);
20532053
}
20542054
zRe = P("regex");
20552055
cgi_check_for_malice();
2056
- if( zRe ) re_compile(&pRe, zRe, 0);
2056
+ if( zRe ) fossil_re_compile(&pRe, zRe, 0);
20572057
if( verbose ) objdescFlags |= OBJDESC_DETAIL;
20582058
if( isPatch ){
20592059
Blob c1, c2, *pOut;
20602060
DiffConfig DCfg;
20612061
pOut = cgi_output_blob();
20622062
--- src/info.c
+++ src/info.c
@@ -936,11 +936,11 @@
936 @ No such object: %h(zName)
937 style_finish_page();
938 return;
939 }
940 zRe = P("regex");
941 if( zRe ) re_compile(&pRe, zRe, 0);
942 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
943 zParent = db_text(0,
944 "SELECT uuid FROM plink, blob"
945 " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
946 rid
@@ -1448,11 +1448,11 @@
1448 fossil_nice_default();
1449 blob_init(&qp, 0, 0);
1450 blob_init(&qpGlob, 0, 0);
1451 diffType = preferred_diff_type();
1452 zRe = P("regex");
1453 if( zRe ) re_compile(&pRe, zRe, 0);
1454 zBranch = P("branch");
1455 if( zBranch && zBranch[0]==0 ) zBranch = 0;
1456 if( zBranch ){
1457 blob_appendf(&qp, "branch=%T", zBranch);
1458 zMergeOrigin = mprintf("merge-in:%s", zBranch);
@@ -2051,11 +2051,11 @@
2051 }
2052 db_finalize(&q);
2053 }
2054 zRe = P("regex");
2055 cgi_check_for_malice();
2056 if( zRe ) re_compile(&pRe, zRe, 0);
2057 if( verbose ) objdescFlags |= OBJDESC_DETAIL;
2058 if( isPatch ){
2059 Blob c1, c2, *pOut;
2060 DiffConfig DCfg;
2061 pOut = cgi_output_blob();
2062
--- src/info.c
+++ src/info.c
@@ -936,11 +936,11 @@
936 @ No such object: %h(zName)
937 style_finish_page();
938 return;
939 }
940 zRe = P("regex");
941 if( zRe ) fossil_re_compile(&pRe, zRe, 0);
942 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
943 zParent = db_text(0,
944 "SELECT uuid FROM plink, blob"
945 " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
946 rid
@@ -1448,11 +1448,11 @@
1448 fossil_nice_default();
1449 blob_init(&qp, 0, 0);
1450 blob_init(&qpGlob, 0, 0);
1451 diffType = preferred_diff_type();
1452 zRe = P("regex");
1453 if( zRe ) fossil_re_compile(&pRe, zRe, 0);
1454 zBranch = P("branch");
1455 if( zBranch && zBranch[0]==0 ) zBranch = 0;
1456 if( zBranch ){
1457 blob_appendf(&qp, "branch=%T", zBranch);
1458 zMergeOrigin = mprintf("merge-in:%s", zBranch);
@@ -2051,11 +2051,11 @@
2051 }
2052 db_finalize(&q);
2053 }
2054 zRe = P("regex");
2055 cgi_check_for_malice();
2056 if( zRe ) fossil_re_compile(&pRe, zRe, 0);
2057 if( verbose ) objdescFlags |= OBJDESC_DETAIL;
2058 if( isPatch ){
2059 Blob c1, c2, *pOut;
2060 DiffConfig DCfg;
2061 pOut = cgi_output_blob();
2062
+1 -1
--- src/json.c
+++ src/json.c
@@ -78,11 +78,11 @@
7878
** whether or not we're really running in json mode we have to try
7979
** a bit harder. Problem reported here:
8080
** https://fossil-scm.org/forum/forumpost/e4953666d6
8181
*/
8282
ReCompiled * pReg = 0;
83
- const char * zErr = re_compile(&pReg, "^/[^/]+/json(/.*)?", 0);
83
+ const char * zErr = fossil_re_compile(&pReg, "^/[^/]+/json(/.*)?", 0);
8484
assert(zErr==0 && "Regex compilation failed?");
8585
if(zErr==0 &&
8686
re_match(pReg, (const unsigned char *)zPathInfo, -1)){
8787
rc = 2;
8888
}
8989
--- src/json.c
+++ src/json.c
@@ -78,11 +78,11 @@
78 ** whether or not we're really running in json mode we have to try
79 ** a bit harder. Problem reported here:
80 ** https://fossil-scm.org/forum/forumpost/e4953666d6
81 */
82 ReCompiled * pReg = 0;
83 const char * zErr = re_compile(&pReg, "^/[^/]+/json(/.*)?", 0);
84 assert(zErr==0 && "Regex compilation failed?");
85 if(zErr==0 &&
86 re_match(pReg, (const unsigned char *)zPathInfo, -1)){
87 rc = 2;
88 }
89
--- src/json.c
+++ src/json.c
@@ -78,11 +78,11 @@
78 ** whether or not we're really running in json mode we have to try
79 ** a bit harder. Problem reported here:
80 ** https://fossil-scm.org/forum/forumpost/e4953666d6
81 */
82 ReCompiled * pReg = 0;
83 const char * zErr = fossil_re_compile(&pReg, "^/[^/]+/json(/.*)?", 0);
84 assert(zErr==0 && "Regex compilation failed?");
85 if(zErr==0 &&
86 re_match(pReg, (const unsigned char *)zPathInfo, -1)){
87 rc = 2;
88 }
89
+2 -2
--- src/match.c
+++ src/match.c
@@ -142,11 +142,11 @@
142142
if( zPat[0] ) zPat++;
143143
144144
/* Check for regular expression syntax errors. */
145145
if( style==MS_REGEXP ){
146146
ReCompiled *regexp;
147
- const char *zFail = re_compile(&regexp, zOne, 0);
147
+ const char *zFail = fossil_re_compile(&regexp, zOne, 0);
148148
if( zFail ){
149149
re_free(regexp);
150150
continue;
151151
}
152152
p->nPattern++;
@@ -375,11 +375,11 @@
375375
376376
/* Check for regular expression syntax errors. */
377377
if( matchStyle==MS_REGEXP ){
378378
ReCompiled *regexp;
379379
char *zTagDup = fossil_strndup(zTag, i);
380
- zFail = re_compile(&regexp, zTagDup, 0);
380
+ zFail = fossil_re_compile(&regexp, zTagDup, 0);
381381
re_free(regexp);
382382
fossil_free(zTagDup);
383383
}
384384
385385
/* Process success and error results. */
386386
--- src/match.c
+++ src/match.c
@@ -142,11 +142,11 @@
142 if( zPat[0] ) zPat++;
143
144 /* Check for regular expression syntax errors. */
145 if( style==MS_REGEXP ){
146 ReCompiled *regexp;
147 const char *zFail = re_compile(&regexp, zOne, 0);
148 if( zFail ){
149 re_free(regexp);
150 continue;
151 }
152 p->nPattern++;
@@ -375,11 +375,11 @@
375
376 /* Check for regular expression syntax errors. */
377 if( matchStyle==MS_REGEXP ){
378 ReCompiled *regexp;
379 char *zTagDup = fossil_strndup(zTag, i);
380 zFail = re_compile(&regexp, zTagDup, 0);
381 re_free(regexp);
382 fossil_free(zTagDup);
383 }
384
385 /* Process success and error results. */
386
--- src/match.c
+++ src/match.c
@@ -142,11 +142,11 @@
142 if( zPat[0] ) zPat++;
143
144 /* Check for regular expression syntax errors. */
145 if( style==MS_REGEXP ){
146 ReCompiled *regexp;
147 const char *zFail = fossil_re_compile(&regexp, zOne, 0);
148 if( zFail ){
149 re_free(regexp);
150 continue;
151 }
152 p->nPattern++;
@@ -375,11 +375,11 @@
375
376 /* Check for regular expression syntax errors. */
377 if( matchStyle==MS_REGEXP ){
378 ReCompiled *regexp;
379 char *zTagDup = fossil_strndup(zTag, i);
380 zFail = fossil_re_compile(&regexp, zTagDup, 0);
381 re_free(regexp);
382 fossil_free(zTagDup);
383 }
384
385 /* Process success and error results. */
386
+51 -17
--- src/regexp.c
+++ src/regexp.c
@@ -24,11 +24,11 @@
2424
** The following regular expression syntax is supported:
2525
**
2626
** X* zero or more occurrences of X
2727
** X+ one or more occurrences of X
2828
** X? zero or one occurrences of X
29
-** X{p,q} between p and q occurrences of X, 0 <= p,q <= 999
29
+** X{p,q} between p and q occurrences of X
3030
** (X) match X
3131
** X|Y X or Y
3232
** ^X X occurring at the beginning of the string
3333
** X$ X occurring at the end of the string
3434
** . Match any single character
@@ -53,13 +53,10 @@
5353
** expression and M is the size of the input string. The matcher never
5454
** exhibits exponential behavior. Note that the X{p,q} operator expands
5555
** to p copies of X following by q-p copies of X? and that the size of the
5656
** regular expression in the O(N*M) performance bound is computed after
5757
** this expansion.
58
-**
59
-** To help prevent DoS attacks, the values of p and q in the "{p,q}" syntax
60
-** are limited to SQLITE_MAX_REGEXP_REPEAT, default 999.
6158
*/
6259
#include "config.h"
6360
#include "regexp.h"
6461
6562
#ifndef SQLITE_MAX_REGEXP_REPEAT
@@ -125,10 +122,11 @@
125122
unsigned (*xNextChar)(ReInput*); /* Next character function */
126123
unsigned char zInit[12]; /* Initial text to match */
127124
int nInit; /* Number of characters in zInit */
128125
unsigned nState; /* Number of entries in aOp[] and aArg[] */
129126
unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
127
+ unsigned mxAlloc; /* Complexity limit */
130128
};
131129
#endif
132130
133131
/* Add a state to the given state set if it is not already there */
134132
static void re_add_state(ReStateSet *pSet, int newState){
@@ -341,15 +339,16 @@
341339
/* Resize the opcode and argument arrays for an RE under construction.
342340
*/
343341
static int re_resize(ReCompiled *p, int N){
344342
char *aOp;
345343
int *aArg;
344
+ if( N>p->mxAlloc ){ p->zErr = "REGEXP pattern too big"; return 1; }
346345
aOp = fossil_realloc(p->aOp, N*sizeof(p->aOp[0]));
347
- if( aOp==0 ) return 1;
346
+ if( aOp==0 ){ p->zErr = "out of memory"; return 1; }
348347
p->aOp = aOp;
349348
aArg = fossil_realloc(p->aArg, N*sizeof(p->aArg[0]));
350
- if( aArg==0 ) return 1;
349
+ if( aArg==0 ){ p->zErr = "out of memory"; return 1; }
351350
p->aArg = aArg;
352351
p->nAlloc = N;
353352
return 0;
354353
}
355354
@@ -534,20 +533,20 @@
534533
unsigned int m = 0, n = 0;
535534
unsigned int sz, j;
536535
if( iPrev<0 ) return "'{m,n}' without operand";
537536
while( (c=rePeek(p))>='0' && c<='9' ){
538537
m = m*10 + c - '0';
539
- if( m>SQLITE_MAX_REGEXP_REPEAT ) return "integer too large";
538
+ if( m*2>p->mxAlloc ) return "REGEXP pattern too big";
540539
p->sIn.i++;
541540
}
542541
n = m;
543542
if( c==',' ){
544543
p->sIn.i++;
545544
n = 0;
546545
while( (c=rePeek(p))>='0' && c<='9' ){
547546
n = n*10 + c-'0';
548
- if( n>SQLITE_MAX_REGEXP_REPEAT ) return "integer too large";
547
+ if( n*2>p->mxAlloc ) return "REGEXP pattern too big";
549548
p->sIn.i++;
550549
}
551550
}
552551
if( c!='}' ) return "unmatched '{'";
553552
if( n>0 && n<m ) return "n less than m in '{m,n}'";
@@ -564,11 +563,11 @@
564563
for(j=m; j<n; j++){
565564
re_append(p, RE_OP_FORK, sz+1);
566565
re_copy(p, iPrev, sz);
567566
}
568567
if( n==0 && m>0 ){
569
- re_append(p, RE_OP_FORK, -sz);
568
+ re_append(p, RE_OP_FORK, -(int)sz);
570569
}
571570
break;
572571
}
573572
case '[': {
574573
unsigned int iFirst = p->nState;
@@ -644,11 +643,16 @@
644643
** Compile a textual regular expression in zIn[] into a compiled regular
645644
** expression suitable for us by re_match() and return a pointer to the
646645
** compiled regular expression in *ppRe. Return NULL on success or an
647646
** error message if something goes wrong.
648647
*/
649
-const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
648
+const char *re_compile(
649
+ ReCompiled **ppRe, /* OUT: write compiled NFA here */
650
+ const char *zIn, /* Input regular expression */
651
+ int mxRe, /* Complexity limit */
652
+ int noCase /* True for caseless comparisons */
653
+){
650654
ReCompiled *pRe;
651655
const char *zErr;
652656
int i, j;
653657
654658
*ppRe = 0;
@@ -656,13 +660,15 @@
656660
if( pRe==0 ){
657661
return "out of memory";
658662
}
659663
memset(pRe, 0, sizeof(*pRe));
660664
pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
665
+ pRe->mxAlloc = mxRe;
661666
if( re_resize(pRe, 30) ){
667
+ zErr = pRe->zErr;
662668
re_free(pRe);
663
- return "out of memory";
669
+ return zErr;
664670
}
665671
if( zIn[0]=='^' ){
666672
zIn++;
667673
}else{
668674
re_append(pRe, RE_OP_ANYSTAR, 0);
@@ -749,10 +755,36 @@
749755
zIn++;
750756
}
751757
blob_materialize(&out);
752758
return out.aData;
753759
}
760
+
761
+/*
762
+** SETTING: regexp-limit width=8 default=1000
763
+**
764
+** Limit the size of the bytecode used to implement a regular expression
765
+** to this many steps. It is important to limit this to avoid possible
766
+** DoS attacks.
767
+*/
768
+
769
+/*
770
+** Compute a reasonable limit on the length of the REGEXP NFA.
771
+*/
772
+int re_maxlen(void){
773
+ return g.db ? db_get_int("regexp-limit", 1000) : 1000;
774
+}
775
+
776
+/*
777
+** Compile an RE using re_maxlen().
778
+*/
779
+const char *fossil_re_compile(
780
+ ReCompiled **ppRe, /* OUT: write compiled NFA here */
781
+ const char *zIn, /* Input regular expression */
782
+ int noCase /* True for caseless comparisons */
783
+){
784
+ return re_compile(ppRe, zIn, re_maxlen(), noCase);
785
+}
754786
755787
/*
756788
** Implementation of the regexp() SQL function. This function implements
757789
** the build-in REGEXP operator. The first argument to the function is the
758790
** pattern and the second argument is the string. So, the SQL statements:
@@ -775,11 +807,11 @@
775807
(void)argc; /* Unused */
776808
pRe = sqlite3_get_auxdata(context, 0);
777809
if( pRe==0 ){
778810
zPattern = (const char*)sqlite3_value_text(argv[0]);
779811
if( zPattern==0 ) return;
780
- zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
812
+ zErr = fossil_re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
781813
if( zErr ){
782814
re_free(pRe);
783815
sqlite3_result_int(context, 0);
784816
/* sqlite3_result_error(context, zErr, -1); */
785817
return;
@@ -803,16 +835,18 @@
803835
** Invoke this routine to register the regexp() function with the
804836
** SQLite database connection.
805837
*/
806838
int re_add_sql_func(sqlite3 *db){
807839
int rc;
808
- rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8|SQLITE_INNOCUOUS,
840
+ rc = sqlite3_create_function(db, "regexp", 2,
841
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
809842
0, re_sql_func, 0, 0);
810843
if( rc==SQLITE_OK ){
811844
/* The regexpi(PATTERN,STRING) function is a case-insensitive version
812845
** of regexp(PATTERN,STRING). */
813
- rc = sqlite3_create_function(db, "regexpi", 2, SQLITE_UTF8|SQLITE_INNOCUOUS,
846
+ rc = sqlite3_create_function(db, "regexpi", 2,
847
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
814848
(void*)db, re_sql_func, 0, 0);
815849
}
816850
return rc;
817851
}
818852
@@ -891,18 +925,18 @@
891925
if( bRobot ){
892926
const char *zRe;
893927
db_find_and_open_repository(0,0);
894928
verify_all_options();
895929
zRe = db_get("robot-exception","^$");
896
- zErr = re_compile(&pRe, zRe, ignoreCase);
930
+ zErr = fossil_re_compile(&pRe, zRe, ignoreCase);
897931
iFileList = 2;
898932
}else{
899933
verify_all_options();
900934
if( g.argc<3 ){
901935
usage("REGEXP [FILE...]");
902936
}
903
- zErr = re_compile(&pRe, g.argv[2], ignoreCase);
937
+ zErr = fossil_re_compile(&pRe, g.argv[2], ignoreCase);
904938
}
905939
if( zErr ) fossil_fatal("%s", zErr);
906940
if( g.argc==iFileList ){
907941
grep_file(pRe, "-", stdin);
908942
}else{
@@ -980,11 +1014,11 @@
9801014
db_find_and_open_repository(0, 0);
9811015
verify_all_options();
9821016
if( g.argc<4 ){
9831017
usage("REGEXP FILENAME ...");
9841018
}
985
- zErr = re_compile(&pRe, g.argv[2], ignoreCase);
1019
+ zErr = fossil_re_compile(&pRe, g.argv[2], ignoreCase);
9861020
if( zErr ) fossil_fatal("%s", zErr);
9871021
9881022
add_content_sql_commands(g.db);
9891023
db_multi_exec("CREATE TEMP TABLE arglist(iname,fname,fnid);");
9901024
for(ii=3; ii<g.argc; ii++){
9911025
--- src/regexp.c
+++ src/regexp.c
@@ -24,11 +24,11 @@
24 ** The following regular expression syntax is supported:
25 **
26 ** X* zero or more occurrences of X
27 ** X+ one or more occurrences of X
28 ** X? zero or one occurrences of X
29 ** X{p,q} between p and q occurrences of X, 0 <= p,q <= 999
30 ** (X) match X
31 ** X|Y X or Y
32 ** ^X X occurring at the beginning of the string
33 ** X$ X occurring at the end of the string
34 ** . Match any single character
@@ -53,13 +53,10 @@
53 ** expression and M is the size of the input string. The matcher never
54 ** exhibits exponential behavior. Note that the X{p,q} operator expands
55 ** to p copies of X following by q-p copies of X? and that the size of the
56 ** regular expression in the O(N*M) performance bound is computed after
57 ** this expansion.
58 **
59 ** To help prevent DoS attacks, the values of p and q in the "{p,q}" syntax
60 ** are limited to SQLITE_MAX_REGEXP_REPEAT, default 999.
61 */
62 #include "config.h"
63 #include "regexp.h"
64
65 #ifndef SQLITE_MAX_REGEXP_REPEAT
@@ -125,10 +122,11 @@
125 unsigned (*xNextChar)(ReInput*); /* Next character function */
126 unsigned char zInit[12]; /* Initial text to match */
127 int nInit; /* Number of characters in zInit */
128 unsigned nState; /* Number of entries in aOp[] and aArg[] */
129 unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
 
130 };
131 #endif
132
133 /* Add a state to the given state set if it is not already there */
134 static void re_add_state(ReStateSet *pSet, int newState){
@@ -341,15 +339,16 @@
341 /* Resize the opcode and argument arrays for an RE under construction.
342 */
343 static int re_resize(ReCompiled *p, int N){
344 char *aOp;
345 int *aArg;
 
346 aOp = fossil_realloc(p->aOp, N*sizeof(p->aOp[0]));
347 if( aOp==0 ) return 1;
348 p->aOp = aOp;
349 aArg = fossil_realloc(p->aArg, N*sizeof(p->aArg[0]));
350 if( aArg==0 ) return 1;
351 p->aArg = aArg;
352 p->nAlloc = N;
353 return 0;
354 }
355
@@ -534,20 +533,20 @@
534 unsigned int m = 0, n = 0;
535 unsigned int sz, j;
536 if( iPrev<0 ) return "'{m,n}' without operand";
537 while( (c=rePeek(p))>='0' && c<='9' ){
538 m = m*10 + c - '0';
539 if( m>SQLITE_MAX_REGEXP_REPEAT ) return "integer too large";
540 p->sIn.i++;
541 }
542 n = m;
543 if( c==',' ){
544 p->sIn.i++;
545 n = 0;
546 while( (c=rePeek(p))>='0' && c<='9' ){
547 n = n*10 + c-'0';
548 if( n>SQLITE_MAX_REGEXP_REPEAT ) return "integer too large";
549 p->sIn.i++;
550 }
551 }
552 if( c!='}' ) return "unmatched '{'";
553 if( n>0 && n<m ) return "n less than m in '{m,n}'";
@@ -564,11 +563,11 @@
564 for(j=m; j<n; j++){
565 re_append(p, RE_OP_FORK, sz+1);
566 re_copy(p, iPrev, sz);
567 }
568 if( n==0 && m>0 ){
569 re_append(p, RE_OP_FORK, -sz);
570 }
571 break;
572 }
573 case '[': {
574 unsigned int iFirst = p->nState;
@@ -644,11 +643,16 @@
644 ** Compile a textual regular expression in zIn[] into a compiled regular
645 ** expression suitable for us by re_match() and return a pointer to the
646 ** compiled regular expression in *ppRe. Return NULL on success or an
647 ** error message if something goes wrong.
648 */
649 const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
 
 
 
 
 
650 ReCompiled *pRe;
651 const char *zErr;
652 int i, j;
653
654 *ppRe = 0;
@@ -656,13 +660,15 @@
656 if( pRe==0 ){
657 return "out of memory";
658 }
659 memset(pRe, 0, sizeof(*pRe));
660 pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
 
661 if( re_resize(pRe, 30) ){
 
662 re_free(pRe);
663 return "out of memory";
664 }
665 if( zIn[0]=='^' ){
666 zIn++;
667 }else{
668 re_append(pRe, RE_OP_ANYSTAR, 0);
@@ -749,10 +755,36 @@
749 zIn++;
750 }
751 blob_materialize(&out);
752 return out.aData;
753 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
754
755 /*
756 ** Implementation of the regexp() SQL function. This function implements
757 ** the build-in REGEXP operator. The first argument to the function is the
758 ** pattern and the second argument is the string. So, the SQL statements:
@@ -775,11 +807,11 @@
775 (void)argc; /* Unused */
776 pRe = sqlite3_get_auxdata(context, 0);
777 if( pRe==0 ){
778 zPattern = (const char*)sqlite3_value_text(argv[0]);
779 if( zPattern==0 ) return;
780 zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
781 if( zErr ){
782 re_free(pRe);
783 sqlite3_result_int(context, 0);
784 /* sqlite3_result_error(context, zErr, -1); */
785 return;
@@ -803,16 +835,18 @@
803 ** Invoke this routine to register the regexp() function with the
804 ** SQLite database connection.
805 */
806 int re_add_sql_func(sqlite3 *db){
807 int rc;
808 rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8|SQLITE_INNOCUOUS,
 
809 0, re_sql_func, 0, 0);
810 if( rc==SQLITE_OK ){
811 /* The regexpi(PATTERN,STRING) function is a case-insensitive version
812 ** of regexp(PATTERN,STRING). */
813 rc = sqlite3_create_function(db, "regexpi", 2, SQLITE_UTF8|SQLITE_INNOCUOUS,
 
814 (void*)db, re_sql_func, 0, 0);
815 }
816 return rc;
817 }
818
@@ -891,18 +925,18 @@
891 if( bRobot ){
892 const char *zRe;
893 db_find_and_open_repository(0,0);
894 verify_all_options();
895 zRe = db_get("robot-exception","^$");
896 zErr = re_compile(&pRe, zRe, ignoreCase);
897 iFileList = 2;
898 }else{
899 verify_all_options();
900 if( g.argc<3 ){
901 usage("REGEXP [FILE...]");
902 }
903 zErr = re_compile(&pRe, g.argv[2], ignoreCase);
904 }
905 if( zErr ) fossil_fatal("%s", zErr);
906 if( g.argc==iFileList ){
907 grep_file(pRe, "-", stdin);
908 }else{
@@ -980,11 +1014,11 @@
980 db_find_and_open_repository(0, 0);
981 verify_all_options();
982 if( g.argc<4 ){
983 usage("REGEXP FILENAME ...");
984 }
985 zErr = re_compile(&pRe, g.argv[2], ignoreCase);
986 if( zErr ) fossil_fatal("%s", zErr);
987
988 add_content_sql_commands(g.db);
989 db_multi_exec("CREATE TEMP TABLE arglist(iname,fname,fnid);");
990 for(ii=3; ii<g.argc; ii++){
991
--- src/regexp.c
+++ src/regexp.c
@@ -24,11 +24,11 @@
24 ** The following regular expression syntax is supported:
25 **
26 ** X* zero or more occurrences of X
27 ** X+ one or more occurrences of X
28 ** X? zero or one occurrences of X
29 ** X{p,q} between p and q occurrences of X
30 ** (X) match X
31 ** X|Y X or Y
32 ** ^X X occurring at the beginning of the string
33 ** X$ X occurring at the end of the string
34 ** . Match any single character
@@ -53,13 +53,10 @@
53 ** expression and M is the size of the input string. The matcher never
54 ** exhibits exponential behavior. Note that the X{p,q} operator expands
55 ** to p copies of X following by q-p copies of X? and that the size of the
56 ** regular expression in the O(N*M) performance bound is computed after
57 ** this expansion.
 
 
 
58 */
59 #include "config.h"
60 #include "regexp.h"
61
62 #ifndef SQLITE_MAX_REGEXP_REPEAT
@@ -125,10 +122,11 @@
122 unsigned (*xNextChar)(ReInput*); /* Next character function */
123 unsigned char zInit[12]; /* Initial text to match */
124 int nInit; /* Number of characters in zInit */
125 unsigned nState; /* Number of entries in aOp[] and aArg[] */
126 unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
127 unsigned mxAlloc; /* Complexity limit */
128 };
129 #endif
130
131 /* Add a state to the given state set if it is not already there */
132 static void re_add_state(ReStateSet *pSet, int newState){
@@ -341,15 +339,16 @@
339 /* Resize the opcode and argument arrays for an RE under construction.
340 */
341 static int re_resize(ReCompiled *p, int N){
342 char *aOp;
343 int *aArg;
344 if( N>p->mxAlloc ){ p->zErr = "REGEXP pattern too big"; return 1; }
345 aOp = fossil_realloc(p->aOp, N*sizeof(p->aOp[0]));
346 if( aOp==0 ){ p->zErr = "out of memory"; return 1; }
347 p->aOp = aOp;
348 aArg = fossil_realloc(p->aArg, N*sizeof(p->aArg[0]));
349 if( aArg==0 ){ p->zErr = "out of memory"; return 1; }
350 p->aArg = aArg;
351 p->nAlloc = N;
352 return 0;
353 }
354
@@ -534,20 +533,20 @@
533 unsigned int m = 0, n = 0;
534 unsigned int sz, j;
535 if( iPrev<0 ) return "'{m,n}' without operand";
536 while( (c=rePeek(p))>='0' && c<='9' ){
537 m = m*10 + c - '0';
538 if( m*2>p->mxAlloc ) return "REGEXP pattern too big";
539 p->sIn.i++;
540 }
541 n = m;
542 if( c==',' ){
543 p->sIn.i++;
544 n = 0;
545 while( (c=rePeek(p))>='0' && c<='9' ){
546 n = n*10 + c-'0';
547 if( n*2>p->mxAlloc ) return "REGEXP pattern too big";
548 p->sIn.i++;
549 }
550 }
551 if( c!='}' ) return "unmatched '{'";
552 if( n>0 && n<m ) return "n less than m in '{m,n}'";
@@ -564,11 +563,11 @@
563 for(j=m; j<n; j++){
564 re_append(p, RE_OP_FORK, sz+1);
565 re_copy(p, iPrev, sz);
566 }
567 if( n==0 && m>0 ){
568 re_append(p, RE_OP_FORK, -(int)sz);
569 }
570 break;
571 }
572 case '[': {
573 unsigned int iFirst = p->nState;
@@ -644,11 +643,16 @@
643 ** Compile a textual regular expression in zIn[] into a compiled regular
644 ** expression suitable for us by re_match() and return a pointer to the
645 ** compiled regular expression in *ppRe. Return NULL on success or an
646 ** error message if something goes wrong.
647 */
648 const char *re_compile(
649 ReCompiled **ppRe, /* OUT: write compiled NFA here */
650 const char *zIn, /* Input regular expression */
651 int mxRe, /* Complexity limit */
652 int noCase /* True for caseless comparisons */
653 ){
654 ReCompiled *pRe;
655 const char *zErr;
656 int i, j;
657
658 *ppRe = 0;
@@ -656,13 +660,15 @@
660 if( pRe==0 ){
661 return "out of memory";
662 }
663 memset(pRe, 0, sizeof(*pRe));
664 pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
665 pRe->mxAlloc = mxRe;
666 if( re_resize(pRe, 30) ){
667 zErr = pRe->zErr;
668 re_free(pRe);
669 return zErr;
670 }
671 if( zIn[0]=='^' ){
672 zIn++;
673 }else{
674 re_append(pRe, RE_OP_ANYSTAR, 0);
@@ -749,10 +755,36 @@
755 zIn++;
756 }
757 blob_materialize(&out);
758 return out.aData;
759 }
760
761 /*
762 ** SETTING: regexp-limit width=8 default=1000
763 **
764 ** Limit the size of the bytecode used to implement a regular expression
765 ** to this many steps. It is important to limit this to avoid possible
766 ** DoS attacks.
767 */
768
769 /*
770 ** Compute a reasonable limit on the length of the REGEXP NFA.
771 */
772 int re_maxlen(void){
773 return g.db ? db_get_int("regexp-limit", 1000) : 1000;
774 }
775
776 /*
777 ** Compile an RE using re_maxlen().
778 */
779 const char *fossil_re_compile(
780 ReCompiled **ppRe, /* OUT: write compiled NFA here */
781 const char *zIn, /* Input regular expression */
782 int noCase /* True for caseless comparisons */
783 ){
784 return re_compile(ppRe, zIn, re_maxlen(), noCase);
785 }
786
787 /*
788 ** Implementation of the regexp() SQL function. This function implements
789 ** the build-in REGEXP operator. The first argument to the function is the
790 ** pattern and the second argument is the string. So, the SQL statements:
@@ -775,11 +807,11 @@
807 (void)argc; /* Unused */
808 pRe = sqlite3_get_auxdata(context, 0);
809 if( pRe==0 ){
810 zPattern = (const char*)sqlite3_value_text(argv[0]);
811 if( zPattern==0 ) return;
812 zErr = fossil_re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
813 if( zErr ){
814 re_free(pRe);
815 sqlite3_result_int(context, 0);
816 /* sqlite3_result_error(context, zErr, -1); */
817 return;
@@ -803,16 +835,18 @@
835 ** Invoke this routine to register the regexp() function with the
836 ** SQLite database connection.
837 */
838 int re_add_sql_func(sqlite3 *db){
839 int rc;
840 rc = sqlite3_create_function(db, "regexp", 2,
841 SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
842 0, re_sql_func, 0, 0);
843 if( rc==SQLITE_OK ){
844 /* The regexpi(PATTERN,STRING) function is a case-insensitive version
845 ** of regexp(PATTERN,STRING). */
846 rc = sqlite3_create_function(db, "regexpi", 2,
847 SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
848 (void*)db, re_sql_func, 0, 0);
849 }
850 return rc;
851 }
852
@@ -891,18 +925,18 @@
925 if( bRobot ){
926 const char *zRe;
927 db_find_and_open_repository(0,0);
928 verify_all_options();
929 zRe = db_get("robot-exception","^$");
930 zErr = fossil_re_compile(&pRe, zRe, ignoreCase);
931 iFileList = 2;
932 }else{
933 verify_all_options();
934 if( g.argc<3 ){
935 usage("REGEXP [FILE...]");
936 }
937 zErr = fossil_re_compile(&pRe, g.argv[2], ignoreCase);
938 }
939 if( zErr ) fossil_fatal("%s", zErr);
940 if( g.argc==iFileList ){
941 grep_file(pRe, "-", stdin);
942 }else{
@@ -980,11 +1014,11 @@
1014 db_find_and_open_repository(0, 0);
1015 verify_all_options();
1016 if( g.argc<4 ){
1017 usage("REGEXP FILENAME ...");
1018 }
1019 zErr = fossil_re_compile(&pRe, g.argv[2], ignoreCase);
1020 if( zErr ) fossil_fatal("%s", zErr);
1021
1022 add_content_sql_commands(g.db);
1023 db_multi_exec("CREATE TEMP TABLE arglist(iname,fname,fnid);");
1024 for(ii=3; ii<g.argc; ii++){
1025
+1 -1
--- src/robot.c
+++ src/robot.c
@@ -367,11 +367,11 @@
367367
}else{
368368
n = strlen(zRE);
369369
}
370370
z = mprintf("%.*s", (int)(zNL - zRE)+1, zRE);
371371
zRE += n;
372
- zErr = re_compile(&pRe, z, 0);
372
+ zErr = fossil_re_compile(&pRe, z, 0);
373373
if( zErr ){
374374
fossil_warning("robot-exception error \"%s\" in expression \"%s\"\n",
375375
zErr, z);
376376
fossil_free(z);
377377
continue;
378378
--- src/robot.c
+++ src/robot.c
@@ -367,11 +367,11 @@
367 }else{
368 n = strlen(zRE);
369 }
370 z = mprintf("%.*s", (int)(zNL - zRE)+1, zRE);
371 zRE += n;
372 zErr = re_compile(&pRe, z, 0);
373 if( zErr ){
374 fossil_warning("robot-exception error \"%s\" in expression \"%s\"\n",
375 zErr, z);
376 fossil_free(z);
377 continue;
378
--- src/robot.c
+++ src/robot.c
@@ -367,11 +367,11 @@
367 }else{
368 n = strlen(zRE);
369 }
370 z = mprintf("%.*s", (int)(zNL - zRE)+1, zRE);
371 zRE += n;
372 zErr = fossil_re_compile(&pRe, z, 0);
373 if( zErr ){
374 fossil_warning("robot-exception error \"%s\" in expression \"%s\"\n",
375 zErr, z);
376 fossil_free(z);
377 continue;
378
+2 -2
--- src/th_main.c
+++ src/th_main.c
@@ -2145,11 +2145,11 @@
21452145
}
21462146
if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
21472147
if( nArg+2!=argc ){
21482148
return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
21492149
}
2150
- zErr = re_compile(&pRe, argv[nArg], noCase);
2150
+ zErr = fossil_re_compile(&pRe, argv[nArg], noCase);
21512151
if( !zErr ){
21522152
Th_SetResultInt(interp, re_match(pRe,
21532153
(const unsigned char *)argv[nArg+1], TH1_LEN(argl[nArg+1])));
21542154
rc = TH_OK;
21552155
}else{
@@ -2202,11 +2202,11 @@
22022202
Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0);
22032203
return TH_ERROR;
22042204
}
22052205
zRegexp = db_get("th1-uri-regexp", 0);
22062206
if( zRegexp && zRegexp[0] ){
2207
- const char *zErr = re_compile(&pRe, zRegexp, 0);
2207
+ const char *zErr = fossil_re_compile(&pRe, zRegexp, 0);
22082208
if( zErr ){
22092209
Th_SetResult(interp, zErr, -1);
22102210
return TH_ERROR;
22112211
}
22122212
}
22132213
--- src/th_main.c
+++ src/th_main.c
@@ -2145,11 +2145,11 @@
2145 }
2146 if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
2147 if( nArg+2!=argc ){
2148 return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
2149 }
2150 zErr = re_compile(&pRe, argv[nArg], noCase);
2151 if( !zErr ){
2152 Th_SetResultInt(interp, re_match(pRe,
2153 (const unsigned char *)argv[nArg+1], TH1_LEN(argl[nArg+1])));
2154 rc = TH_OK;
2155 }else{
@@ -2202,11 +2202,11 @@
2202 Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0);
2203 return TH_ERROR;
2204 }
2205 zRegexp = db_get("th1-uri-regexp", 0);
2206 if( zRegexp && zRegexp[0] ){
2207 const char *zErr = re_compile(&pRe, zRegexp, 0);
2208 if( zErr ){
2209 Th_SetResult(interp, zErr, -1);
2210 return TH_ERROR;
2211 }
2212 }
2213
--- src/th_main.c
+++ src/th_main.c
@@ -2145,11 +2145,11 @@
2145 }
2146 if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
2147 if( nArg+2!=argc ){
2148 return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
2149 }
2150 zErr = fossil_re_compile(&pRe, argv[nArg], noCase);
2151 if( !zErr ){
2152 Th_SetResultInt(interp, re_match(pRe,
2153 (const unsigned char *)argv[nArg+1], TH1_LEN(argl[nArg+1])));
2154 rc = TH_OK;
2155 }else{
@@ -2202,11 +2202,11 @@
2202 Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0);
2203 return TH_ERROR;
2204 }
2205 zRegexp = db_get("th1-uri-regexp", 0);
2206 if( zRegexp && zRegexp[0] ){
2207 const char *zErr = fossil_re_compile(&pRe, zRegexp, 0);
2208 if( zErr ){
2209 Th_SetResult(interp, zErr, -1);
2210 return TH_ERROR;
2211 }
2212 }
2213

Keyboard Shortcuts

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