Fossil SCM

Check regular expressions for errors. Add error reporting mechanism. Leave bad patterns out of filter. Add showid and showsql to timeline help text.

andygoth 2016-11-05 05:51 andygoth-timeline-ms
Commit 5703ccb2e078ff0695f541bae31bd50a57966819
1 file changed +61 -22
+61 -22
--- src/timeline.c
+++ src/timeline.c
@@ -1274,25 +1274,31 @@
12741274
** The backslashes are not removed from the regular expression.
12751275
**
12761276
** In addition to assembling and returning an SQL expression, this function
12771277
** makes an English-language description of the patterns being matched, suitable
12781278
** for display in the web interface.
1279
+**
1280
+** If any errors arise during processing, *zError is set to an error message.
1281
+** Otherwise it is set to NULL.
12791282
*/
12801283
static const char *tagMatchExpression(
12811284
MatchStyle matchStyle, /* Match style code */
12821285
const char *zTag, /* Tag name, match pattern, or pattern list */
1283
- const char **zDesc /* Output expression description string */
1286
+ const char **zDesc, /* Output expression description string */
1287
+ const char **zError /* Output error string */
12841288
){
12851289
Blob expr = BLOB_INITIALIZER; /* SQL expression string assembly buffer */
12861290
Blob desc = BLOB_INITIALIZER; /* English description of match patterns */
1291
+ Blob err = BLOB_INITIALIZER; /* Error text assembly buffer */
12871292
const char *zStart; /* Text at start of expression */
12881293
const char *zDelimiter; /* Text between expression terms */
12891294
const char *zEnd; /* Text at end of expression */
12901295
const char *zPrefix; /* Text before each match pattern */
12911296
const char *zSuffix; /* Text after each match pattern */
12921297
const char *zIntro; /* Text introducing pattern description */
12931298
const char *zPattern = 0; /* Previous quoted pattern */
1299
+ const char *zFail = 0; /* Current failure message or NULL if okay */
12941300
const char *zOr = " or "; /* Text before final quoted pattern */
12951301
char cDel; /* Input delimiter character */
12961302
int i; /* Input match pattern length counter */
12971303
12981304
/* Optimize exact matches by looking up the ID in advance to create a simple
@@ -1328,10 +1334,11 @@
13281334
}
13291335
13301336
/* Convert the list of matches into an SQL expression and text description. */
13311337
blob_zero(&expr);
13321338
blob_zero(&desc);
1339
+ blob_zero(&err);
13331340
while( 1 ){
13341341
/* Skip leading delimiters. */
13351342
for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag );
13361343
13371344
/* Next non-delimiter character determines quoting. */
@@ -1358,30 +1365,50 @@
13581365
if( matchStyle==MS_REGEXP && zTag[i]=='\\' && zTag[i+1] ){
13591366
++i;
13601367
}
13611368
}
13621369
1363
- /* Incorporate the match word into the output expression. The %q format is
1364
- * used to protect against SQL injection attacks by replacing ' with ''. */
1365
- blob_appendf(&expr, "%s%s%#q%s", blob_size(&expr) ? zDelimiter : zStart,
1366
- zPrefix, i, zTag, zSuffix);
1367
-
1368
- /* Build up the description string. */
1369
- if( !blob_size(&desc) ){
1370
- /* First tag: start with intro followed by first quoted tag. */
1371
- blob_append(&desc, zIntro, -1);
1372
- blob_append(&desc, tagQuote(i, zTag), -1);
1370
+ /* Check for regular expression syntax errors. */
1371
+ if( matchStyle==MS_REGEXP ){
1372
+ ReCompiled *regexp;
1373
+ char *zTagDup = fossil_strndup(zTag, i);
1374
+ zFail = re_compile(&regexp, zTagDup, 0);
1375
+ re_free(regexp);
1376
+ fossil_free(zTagDup);
1377
+ }
1378
+
1379
+ /* Process success and error results. */
1380
+ if( !zFail ){
1381
+ /* Incorporate the match word into the output expression. %q is used to
1382
+ * protect against SQL injection attacks by replacing ' with ''. */
1383
+ blob_appendf(&expr, "%s%s%#q%s", blob_size(&expr) ? zDelimiter : zStart,
1384
+ zPrefix, i, zTag, zSuffix);
1385
+
1386
+ /* Build up the description string. */
1387
+ if( !blob_size(&desc) ){
1388
+ /* First tag: start with intro followed by first quoted tag. */
1389
+ blob_append(&desc, zIntro, -1);
1390
+ blob_append(&desc, tagQuote(i, zTag), -1);
1391
+ }else{
1392
+ if( zPattern ){
1393
+ /* Third and subsequent tags: append comma then previous tag. */
1394
+ blob_append(&desc, ", ", 2);
1395
+ blob_append(&desc, zPattern, -1);
1396
+ zOr = ", or ";
1397
+ }
1398
+
1399
+ /* Second and subsequent tags: store quoted tag for next iteration. */
1400
+ zPattern = tagQuote(i, zTag);
1401
+ }
13731402
}else{
1374
- if( zPattern ){
1375
- /* Third and subsequent tags: append comma then previous tag. */
1376
- blob_append(&desc, ", ", 2);
1377
- blob_append(&desc, zPattern, -1);
1378
- zOr = ", or ";
1379
- }
1380
-
1381
- /* Second and subsequent tags: store quoted tag for next iteration. */
1382
- zPattern = tagQuote(i, zTag);
1403
+ /* On error, skip the match word and build up the error message buffer. */
1404
+ if( !blob_size(&err) ){
1405
+ blob_append(&err, "Error: ", 7);
1406
+ }else{
1407
+ blob_append(&err, ", ", 2);
1408
+ }
1409
+ blob_appendf(&err, "(%s%s: %s)", zIntro, tagQuote(i, zTag), zFail);
13831410
}
13841411
13851412
/* Advance past all consumed input characters. */
13861413
zTag += i;
13871414
if( cDel!=',' && *zTag==cDel ){
@@ -1394,10 +1421,13 @@
13941421
blob_append(&desc, zOr, -1);
13951422
blob_append(&desc, zPattern, -1);
13961423
}
13971424
*zDesc = blob_str(&desc);
13981425
1426
+ /* Finalize and extract the error text. */
1427
+ *zError = blob_size(&err) ? blob_str(&err) : 0;
1428
+
13991429
/* Finalize and extract the SQL expression. */
14001430
if( blob_size(&expr) ){
14011431
blob_append(&expr, zEnd, -1);
14021432
return blob_str(&expr);
14031433
}
@@ -1443,10 +1473,12 @@
14431473
** ym=YYYY-MM Show only events for the given year/month.
14441474
** yw=YYYY-WW Show only events for the given week of the given year
14451475
** ymd=YYYY-MM-DD Show only events on the given day
14461476
** datefmt=N Override the date format
14471477
** bisect Show the check-ins that are in the current bisect
1478
+** showid Show RIDs
1479
+** showsql Show the SQL text
14481480
**
14491481
** p= and d= can appear individually or together. If either p= or d=
14501482
** appear, then u=, y=, a=, and b= are ignored.
14511483
**
14521484
** If both a= and b= appear then both upper and lower bounds are honored.
@@ -1469,10 +1501,11 @@
14691501
const char *zBrName = P("r"); /* Equivalent to t=TAG&rel */
14701502
int related = PB("rel"); /* Show events related to zTagName */
14711503
const char *zMatchStyle = P("ms"); /* Tag/branch match style string */
14721504
MatchStyle matchStyle = MS_EXACT; /* Match style code */
14731505
const char *zMatchDesc = 0; /* Tag match expression description text */
1506
+ const char *zError = 0; /* Tag match error string */
14741507
const char *zTagSql = 0; /* Tag/branch match SQL expression */
14751508
const char *zSearch = P("s"); /* Search string */
14761509
const char *zUses = P("uf"); /* Only show check-ins hold this file */
14771510
const char *zYearMonth = P("ym"); /* Show check-ins for the given YYYY-MM */
14781511
const char *zYearWeek = P("yw"); /* Check-ins for YYYY-WW (week-of-year) */
@@ -1564,11 +1597,11 @@
15641597
15651598
/* Display a checkbox to enable/disable display of related check-ins. */
15661599
style_submenu_checkbox("rel", "Related", 0);
15671600
15681601
/* Construct the tag match expression. */
1569
- zTagSql = tagMatchExpression(matchStyle, zTagName, &zMatchDesc);
1602
+ zTagSql = tagMatchExpression(matchStyle, zTagName, &zMatchDesc, &zError);
15701603
}
15711604
15721605
if( zMark && zMark[0]==0 ){
15731606
if( zAfter ) zMark = zAfter;
15741607
if( zBefore ) zMark = zBefore;
@@ -1976,11 +2009,11 @@
19762009
}
19772010
if( zUser ){
19782011
blob_appendf(&desc, " by user %h", zUser);
19792012
tmFlags |= TIMELINE_DISJOINT;
19802013
}
1981
- if( zTagName ){
2014
+ if( zTagSql ){
19822015
if( matchStyle==MS_EXACT ){
19832016
if( related ){
19842017
blob_appendf(&desc, " related to %h", zMatchDesc);
19852018
}else{
19862019
blob_appendf(&desc, " tagged with %h", zMatchDesc);
@@ -2070,10 +2103,16 @@
20702103
}
20712104
blob_zero(&sql);
20722105
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
20732106
@ <h2>%b(&desc)</h2>
20742107
blob_reset(&desc);
2108
+
2109
+ /* Report any errors. */
2110
+ if( zError ){
2111
+ @ <p class="generalError">%h(zError)</p>
2112
+ }
2113
+
20752114
www_print_timeline(&q, tmFlags, zThisUser, zThisTag, selectedRid, 0);
20762115
db_finalize(&q);
20772116
if( zOlderButton ){
20782117
@ %z(xhref("class='button'","%z",zOlderButton))Older</a>
20792118
}
20802119
--- src/timeline.c
+++ src/timeline.c
@@ -1274,25 +1274,31 @@
1274 ** The backslashes are not removed from the regular expression.
1275 **
1276 ** In addition to assembling and returning an SQL expression, this function
1277 ** makes an English-language description of the patterns being matched, suitable
1278 ** for display in the web interface.
 
 
 
1279 */
1280 static const char *tagMatchExpression(
1281 MatchStyle matchStyle, /* Match style code */
1282 const char *zTag, /* Tag name, match pattern, or pattern list */
1283 const char **zDesc /* Output expression description string */
 
1284 ){
1285 Blob expr = BLOB_INITIALIZER; /* SQL expression string assembly buffer */
1286 Blob desc = BLOB_INITIALIZER; /* English description of match patterns */
 
1287 const char *zStart; /* Text at start of expression */
1288 const char *zDelimiter; /* Text between expression terms */
1289 const char *zEnd; /* Text at end of expression */
1290 const char *zPrefix; /* Text before each match pattern */
1291 const char *zSuffix; /* Text after each match pattern */
1292 const char *zIntro; /* Text introducing pattern description */
1293 const char *zPattern = 0; /* Previous quoted pattern */
 
1294 const char *zOr = " or "; /* Text before final quoted pattern */
1295 char cDel; /* Input delimiter character */
1296 int i; /* Input match pattern length counter */
1297
1298 /* Optimize exact matches by looking up the ID in advance to create a simple
@@ -1328,10 +1334,11 @@
1328 }
1329
1330 /* Convert the list of matches into an SQL expression and text description. */
1331 blob_zero(&expr);
1332 blob_zero(&desc);
 
1333 while( 1 ){
1334 /* Skip leading delimiters. */
1335 for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag );
1336
1337 /* Next non-delimiter character determines quoting. */
@@ -1358,30 +1365,50 @@
1358 if( matchStyle==MS_REGEXP && zTag[i]=='\\' && zTag[i+1] ){
1359 ++i;
1360 }
1361 }
1362
1363 /* Incorporate the match word into the output expression. The %q format is
1364 * used to protect against SQL injection attacks by replacing ' with ''. */
1365 blob_appendf(&expr, "%s%s%#q%s", blob_size(&expr) ? zDelimiter : zStart,
1366 zPrefix, i, zTag, zSuffix);
1367
1368 /* Build up the description string. */
1369 if( !blob_size(&desc) ){
1370 /* First tag: start with intro followed by first quoted tag. */
1371 blob_append(&desc, zIntro, -1);
1372 blob_append(&desc, tagQuote(i, zTag), -1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1373 }else{
1374 if( zPattern ){
1375 /* Third and subsequent tags: append comma then previous tag. */
1376 blob_append(&desc, ", ", 2);
1377 blob_append(&desc, zPattern, -1);
1378 zOr = ", or ";
1379 }
1380
1381 /* Second and subsequent tags: store quoted tag for next iteration. */
1382 zPattern = tagQuote(i, zTag);
1383 }
1384
1385 /* Advance past all consumed input characters. */
1386 zTag += i;
1387 if( cDel!=',' && *zTag==cDel ){
@@ -1394,10 +1421,13 @@
1394 blob_append(&desc, zOr, -1);
1395 blob_append(&desc, zPattern, -1);
1396 }
1397 *zDesc = blob_str(&desc);
1398
 
 
 
1399 /* Finalize and extract the SQL expression. */
1400 if( blob_size(&expr) ){
1401 blob_append(&expr, zEnd, -1);
1402 return blob_str(&expr);
1403 }
@@ -1443,10 +1473,12 @@
1443 ** ym=YYYY-MM Show only events for the given year/month.
1444 ** yw=YYYY-WW Show only events for the given week of the given year
1445 ** ymd=YYYY-MM-DD Show only events on the given day
1446 ** datefmt=N Override the date format
1447 ** bisect Show the check-ins that are in the current bisect
 
 
1448 **
1449 ** p= and d= can appear individually or together. If either p= or d=
1450 ** appear, then u=, y=, a=, and b= are ignored.
1451 **
1452 ** If both a= and b= appear then both upper and lower bounds are honored.
@@ -1469,10 +1501,11 @@
1469 const char *zBrName = P("r"); /* Equivalent to t=TAG&rel */
1470 int related = PB("rel"); /* Show events related to zTagName */
1471 const char *zMatchStyle = P("ms"); /* Tag/branch match style string */
1472 MatchStyle matchStyle = MS_EXACT; /* Match style code */
1473 const char *zMatchDesc = 0; /* Tag match expression description text */
 
1474 const char *zTagSql = 0; /* Tag/branch match SQL expression */
1475 const char *zSearch = P("s"); /* Search string */
1476 const char *zUses = P("uf"); /* Only show check-ins hold this file */
1477 const char *zYearMonth = P("ym"); /* Show check-ins for the given YYYY-MM */
1478 const char *zYearWeek = P("yw"); /* Check-ins for YYYY-WW (week-of-year) */
@@ -1564,11 +1597,11 @@
1564
1565 /* Display a checkbox to enable/disable display of related check-ins. */
1566 style_submenu_checkbox("rel", "Related", 0);
1567
1568 /* Construct the tag match expression. */
1569 zTagSql = tagMatchExpression(matchStyle, zTagName, &zMatchDesc);
1570 }
1571
1572 if( zMark && zMark[0]==0 ){
1573 if( zAfter ) zMark = zAfter;
1574 if( zBefore ) zMark = zBefore;
@@ -1976,11 +2009,11 @@
1976 }
1977 if( zUser ){
1978 blob_appendf(&desc, " by user %h", zUser);
1979 tmFlags |= TIMELINE_DISJOINT;
1980 }
1981 if( zTagName ){
1982 if( matchStyle==MS_EXACT ){
1983 if( related ){
1984 blob_appendf(&desc, " related to %h", zMatchDesc);
1985 }else{
1986 blob_appendf(&desc, " tagged with %h", zMatchDesc);
@@ -2070,10 +2103,16 @@
2070 }
2071 blob_zero(&sql);
2072 db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
2073 @ <h2>%b(&desc)</h2>
2074 blob_reset(&desc);
 
 
 
 
 
 
2075 www_print_timeline(&q, tmFlags, zThisUser, zThisTag, selectedRid, 0);
2076 db_finalize(&q);
2077 if( zOlderButton ){
2078 @ %z(xhref("class='button'","%z",zOlderButton))Older</a>
2079 }
2080
--- src/timeline.c
+++ src/timeline.c
@@ -1274,25 +1274,31 @@
1274 ** The backslashes are not removed from the regular expression.
1275 **
1276 ** In addition to assembling and returning an SQL expression, this function
1277 ** makes an English-language description of the patterns being matched, suitable
1278 ** for display in the web interface.
1279 **
1280 ** If any errors arise during processing, *zError is set to an error message.
1281 ** Otherwise it is set to NULL.
1282 */
1283 static const char *tagMatchExpression(
1284 MatchStyle matchStyle, /* Match style code */
1285 const char *zTag, /* Tag name, match pattern, or pattern list */
1286 const char **zDesc, /* Output expression description string */
1287 const char **zError /* Output error string */
1288 ){
1289 Blob expr = BLOB_INITIALIZER; /* SQL expression string assembly buffer */
1290 Blob desc = BLOB_INITIALIZER; /* English description of match patterns */
1291 Blob err = BLOB_INITIALIZER; /* Error text assembly buffer */
1292 const char *zStart; /* Text at start of expression */
1293 const char *zDelimiter; /* Text between expression terms */
1294 const char *zEnd; /* Text at end of expression */
1295 const char *zPrefix; /* Text before each match pattern */
1296 const char *zSuffix; /* Text after each match pattern */
1297 const char *zIntro; /* Text introducing pattern description */
1298 const char *zPattern = 0; /* Previous quoted pattern */
1299 const char *zFail = 0; /* Current failure message or NULL if okay */
1300 const char *zOr = " or "; /* Text before final quoted pattern */
1301 char cDel; /* Input delimiter character */
1302 int i; /* Input match pattern length counter */
1303
1304 /* Optimize exact matches by looking up the ID in advance to create a simple
@@ -1328,10 +1334,11 @@
1334 }
1335
1336 /* Convert the list of matches into an SQL expression and text description. */
1337 blob_zero(&expr);
1338 blob_zero(&desc);
1339 blob_zero(&err);
1340 while( 1 ){
1341 /* Skip leading delimiters. */
1342 for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag );
1343
1344 /* Next non-delimiter character determines quoting. */
@@ -1358,30 +1365,50 @@
1365 if( matchStyle==MS_REGEXP && zTag[i]=='\\' && zTag[i+1] ){
1366 ++i;
1367 }
1368 }
1369
1370 /* Check for regular expression syntax errors. */
1371 if( matchStyle==MS_REGEXP ){
1372 ReCompiled *regexp;
1373 char *zTagDup = fossil_strndup(zTag, i);
1374 zFail = re_compile(&regexp, zTagDup, 0);
1375 re_free(regexp);
1376 fossil_free(zTagDup);
1377 }
1378
1379 /* Process success and error results. */
1380 if( !zFail ){
1381 /* Incorporate the match word into the output expression. %q is used to
1382 * protect against SQL injection attacks by replacing ' with ''. */
1383 blob_appendf(&expr, "%s%s%#q%s", blob_size(&expr) ? zDelimiter : zStart,
1384 zPrefix, i, zTag, zSuffix);
1385
1386 /* Build up the description string. */
1387 if( !blob_size(&desc) ){
1388 /* First tag: start with intro followed by first quoted tag. */
1389 blob_append(&desc, zIntro, -1);
1390 blob_append(&desc, tagQuote(i, zTag), -1);
1391 }else{
1392 if( zPattern ){
1393 /* Third and subsequent tags: append comma then previous tag. */
1394 blob_append(&desc, ", ", 2);
1395 blob_append(&desc, zPattern, -1);
1396 zOr = ", or ";
1397 }
1398
1399 /* Second and subsequent tags: store quoted tag for next iteration. */
1400 zPattern = tagQuote(i, zTag);
1401 }
1402 }else{
1403 /* On error, skip the match word and build up the error message buffer. */
1404 if( !blob_size(&err) ){
1405 blob_append(&err, "Error: ", 7);
1406 }else{
1407 blob_append(&err, ", ", 2);
1408 }
1409 blob_appendf(&err, "(%s%s: %s)", zIntro, tagQuote(i, zTag), zFail);
 
 
1410 }
1411
1412 /* Advance past all consumed input characters. */
1413 zTag += i;
1414 if( cDel!=',' && *zTag==cDel ){
@@ -1394,10 +1421,13 @@
1421 blob_append(&desc, zOr, -1);
1422 blob_append(&desc, zPattern, -1);
1423 }
1424 *zDesc = blob_str(&desc);
1425
1426 /* Finalize and extract the error text. */
1427 *zError = blob_size(&err) ? blob_str(&err) : 0;
1428
1429 /* Finalize and extract the SQL expression. */
1430 if( blob_size(&expr) ){
1431 blob_append(&expr, zEnd, -1);
1432 return blob_str(&expr);
1433 }
@@ -1443,10 +1473,12 @@
1473 ** ym=YYYY-MM Show only events for the given year/month.
1474 ** yw=YYYY-WW Show only events for the given week of the given year
1475 ** ymd=YYYY-MM-DD Show only events on the given day
1476 ** datefmt=N Override the date format
1477 ** bisect Show the check-ins that are in the current bisect
1478 ** showid Show RIDs
1479 ** showsql Show the SQL text
1480 **
1481 ** p= and d= can appear individually or together. If either p= or d=
1482 ** appear, then u=, y=, a=, and b= are ignored.
1483 **
1484 ** If both a= and b= appear then both upper and lower bounds are honored.
@@ -1469,10 +1501,11 @@
1501 const char *zBrName = P("r"); /* Equivalent to t=TAG&rel */
1502 int related = PB("rel"); /* Show events related to zTagName */
1503 const char *zMatchStyle = P("ms"); /* Tag/branch match style string */
1504 MatchStyle matchStyle = MS_EXACT; /* Match style code */
1505 const char *zMatchDesc = 0; /* Tag match expression description text */
1506 const char *zError = 0; /* Tag match error string */
1507 const char *zTagSql = 0; /* Tag/branch match SQL expression */
1508 const char *zSearch = P("s"); /* Search string */
1509 const char *zUses = P("uf"); /* Only show check-ins hold this file */
1510 const char *zYearMonth = P("ym"); /* Show check-ins for the given YYYY-MM */
1511 const char *zYearWeek = P("yw"); /* Check-ins for YYYY-WW (week-of-year) */
@@ -1564,11 +1597,11 @@
1597
1598 /* Display a checkbox to enable/disable display of related check-ins. */
1599 style_submenu_checkbox("rel", "Related", 0);
1600
1601 /* Construct the tag match expression. */
1602 zTagSql = tagMatchExpression(matchStyle, zTagName, &zMatchDesc, &zError);
1603 }
1604
1605 if( zMark && zMark[0]==0 ){
1606 if( zAfter ) zMark = zAfter;
1607 if( zBefore ) zMark = zBefore;
@@ -1976,11 +2009,11 @@
2009 }
2010 if( zUser ){
2011 blob_appendf(&desc, " by user %h", zUser);
2012 tmFlags |= TIMELINE_DISJOINT;
2013 }
2014 if( zTagSql ){
2015 if( matchStyle==MS_EXACT ){
2016 if( related ){
2017 blob_appendf(&desc, " related to %h", zMatchDesc);
2018 }else{
2019 blob_appendf(&desc, " tagged with %h", zMatchDesc);
@@ -2070,10 +2103,16 @@
2103 }
2104 blob_zero(&sql);
2105 db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
2106 @ <h2>%b(&desc)</h2>
2107 blob_reset(&desc);
2108
2109 /* Report any errors. */
2110 if( zError ){
2111 @ <p class="generalError">%h(zError)</p>
2112 }
2113
2114 www_print_timeline(&q, tmFlags, zThisUser, zThisTag, selectedRid, 0);
2115 db_finalize(&q);
2116 if( zOlderButton ){
2117 @ %z(xhref("class='button'","%z",zOlderButton))Older</a>
2118 }
2119

Keyboard Shortcuts

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