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.
Commit
5703ccb2e078ff0695f541bae31bd50a57966819
Parent
b98776679e613d1…
1 file changed
+61
-22
+61
-22
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1274,25 +1274,31 @@ | ||
| 1274 | 1274 | ** The backslashes are not removed from the regular expression. |
| 1275 | 1275 | ** |
| 1276 | 1276 | ** In addition to assembling and returning an SQL expression, this function |
| 1277 | 1277 | ** makes an English-language description of the patterns being matched, suitable |
| 1278 | 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. | |
| 1279 | 1282 | */ |
| 1280 | 1283 | static const char *tagMatchExpression( |
| 1281 | 1284 | MatchStyle matchStyle, /* Match style code */ |
| 1282 | 1285 | 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 */ | |
| 1284 | 1288 | ){ |
| 1285 | 1289 | Blob expr = BLOB_INITIALIZER; /* SQL expression string assembly buffer */ |
| 1286 | 1290 | Blob desc = BLOB_INITIALIZER; /* English description of match patterns */ |
| 1291 | + Blob err = BLOB_INITIALIZER; /* Error text assembly buffer */ | |
| 1287 | 1292 | const char *zStart; /* Text at start of expression */ |
| 1288 | 1293 | const char *zDelimiter; /* Text between expression terms */ |
| 1289 | 1294 | const char *zEnd; /* Text at end of expression */ |
| 1290 | 1295 | const char *zPrefix; /* Text before each match pattern */ |
| 1291 | 1296 | const char *zSuffix; /* Text after each match pattern */ |
| 1292 | 1297 | const char *zIntro; /* Text introducing pattern description */ |
| 1293 | 1298 | const char *zPattern = 0; /* Previous quoted pattern */ |
| 1299 | + const char *zFail = 0; /* Current failure message or NULL if okay */ | |
| 1294 | 1300 | const char *zOr = " or "; /* Text before final quoted pattern */ |
| 1295 | 1301 | char cDel; /* Input delimiter character */ |
| 1296 | 1302 | int i; /* Input match pattern length counter */ |
| 1297 | 1303 | |
| 1298 | 1304 | /* Optimize exact matches by looking up the ID in advance to create a simple |
| @@ -1328,10 +1334,11 @@ | ||
| 1328 | 1334 | } |
| 1329 | 1335 | |
| 1330 | 1336 | /* Convert the list of matches into an SQL expression and text description. */ |
| 1331 | 1337 | blob_zero(&expr); |
| 1332 | 1338 | blob_zero(&desc); |
| 1339 | + blob_zero(&err); | |
| 1333 | 1340 | while( 1 ){ |
| 1334 | 1341 | /* Skip leading delimiters. */ |
| 1335 | 1342 | for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag ); |
| 1336 | 1343 | |
| 1337 | 1344 | /* Next non-delimiter character determines quoting. */ |
| @@ -1358,30 +1365,50 @@ | ||
| 1358 | 1365 | if( matchStyle==MS_REGEXP && zTag[i]=='\\' && zTag[i+1] ){ |
| 1359 | 1366 | ++i; |
| 1360 | 1367 | } |
| 1361 | 1368 | } |
| 1362 | 1369 | |
| 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(®exp, 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 | + } | |
| 1373 | 1402 | }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); | |
| 1383 | 1410 | } |
| 1384 | 1411 | |
| 1385 | 1412 | /* Advance past all consumed input characters. */ |
| 1386 | 1413 | zTag += i; |
| 1387 | 1414 | if( cDel!=',' && *zTag==cDel ){ |
| @@ -1394,10 +1421,13 @@ | ||
| 1394 | 1421 | blob_append(&desc, zOr, -1); |
| 1395 | 1422 | blob_append(&desc, zPattern, -1); |
| 1396 | 1423 | } |
| 1397 | 1424 | *zDesc = blob_str(&desc); |
| 1398 | 1425 | |
| 1426 | + /* Finalize and extract the error text. */ | |
| 1427 | + *zError = blob_size(&err) ? blob_str(&err) : 0; | |
| 1428 | + | |
| 1399 | 1429 | /* Finalize and extract the SQL expression. */ |
| 1400 | 1430 | if( blob_size(&expr) ){ |
| 1401 | 1431 | blob_append(&expr, zEnd, -1); |
| 1402 | 1432 | return blob_str(&expr); |
| 1403 | 1433 | } |
| @@ -1443,10 +1473,12 @@ | ||
| 1443 | 1473 | ** ym=YYYY-MM Show only events for the given year/month. |
| 1444 | 1474 | ** yw=YYYY-WW Show only events for the given week of the given year |
| 1445 | 1475 | ** ymd=YYYY-MM-DD Show only events on the given day |
| 1446 | 1476 | ** datefmt=N Override the date format |
| 1447 | 1477 | ** bisect Show the check-ins that are in the current bisect |
| 1478 | +** showid Show RIDs | |
| 1479 | +** showsql Show the SQL text | |
| 1448 | 1480 | ** |
| 1449 | 1481 | ** p= and d= can appear individually or together. If either p= or d= |
| 1450 | 1482 | ** appear, then u=, y=, a=, and b= are ignored. |
| 1451 | 1483 | ** |
| 1452 | 1484 | ** If both a= and b= appear then both upper and lower bounds are honored. |
| @@ -1469,10 +1501,11 @@ | ||
| 1469 | 1501 | const char *zBrName = P("r"); /* Equivalent to t=TAG&rel */ |
| 1470 | 1502 | int related = PB("rel"); /* Show events related to zTagName */ |
| 1471 | 1503 | const char *zMatchStyle = P("ms"); /* Tag/branch match style string */ |
| 1472 | 1504 | MatchStyle matchStyle = MS_EXACT; /* Match style code */ |
| 1473 | 1505 | const char *zMatchDesc = 0; /* Tag match expression description text */ |
| 1506 | + const char *zError = 0; /* Tag match error string */ | |
| 1474 | 1507 | const char *zTagSql = 0; /* Tag/branch match SQL expression */ |
| 1475 | 1508 | const char *zSearch = P("s"); /* Search string */ |
| 1476 | 1509 | const char *zUses = P("uf"); /* Only show check-ins hold this file */ |
| 1477 | 1510 | const char *zYearMonth = P("ym"); /* Show check-ins for the given YYYY-MM */ |
| 1478 | 1511 | const char *zYearWeek = P("yw"); /* Check-ins for YYYY-WW (week-of-year) */ |
| @@ -1564,11 +1597,11 @@ | ||
| 1564 | 1597 | |
| 1565 | 1598 | /* Display a checkbox to enable/disable display of related check-ins. */ |
| 1566 | 1599 | style_submenu_checkbox("rel", "Related", 0); |
| 1567 | 1600 | |
| 1568 | 1601 | /* Construct the tag match expression. */ |
| 1569 | - zTagSql = tagMatchExpression(matchStyle, zTagName, &zMatchDesc); | |
| 1602 | + zTagSql = tagMatchExpression(matchStyle, zTagName, &zMatchDesc, &zError); | |
| 1570 | 1603 | } |
| 1571 | 1604 | |
| 1572 | 1605 | if( zMark && zMark[0]==0 ){ |
| 1573 | 1606 | if( zAfter ) zMark = zAfter; |
| 1574 | 1607 | if( zBefore ) zMark = zBefore; |
| @@ -1976,11 +2009,11 @@ | ||
| 1976 | 2009 | } |
| 1977 | 2010 | if( zUser ){ |
| 1978 | 2011 | blob_appendf(&desc, " by user %h", zUser); |
| 1979 | 2012 | tmFlags |= TIMELINE_DISJOINT; |
| 1980 | 2013 | } |
| 1981 | - if( zTagName ){ | |
| 2014 | + if( zTagSql ){ | |
| 1982 | 2015 | if( matchStyle==MS_EXACT ){ |
| 1983 | 2016 | if( related ){ |
| 1984 | 2017 | blob_appendf(&desc, " related to %h", zMatchDesc); |
| 1985 | 2018 | }else{ |
| 1986 | 2019 | blob_appendf(&desc, " tagged with %h", zMatchDesc); |
| @@ -2070,10 +2103,16 @@ | ||
| 2070 | 2103 | } |
| 2071 | 2104 | blob_zero(&sql); |
| 2072 | 2105 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); |
| 2073 | 2106 | @ <h2>%b(&desc)</h2> |
| 2074 | 2107 | blob_reset(&desc); |
| 2108 | + | |
| 2109 | + /* Report any errors. */ | |
| 2110 | + if( zError ){ | |
| 2111 | + @ <p class="generalError">%h(zError)</p> | |
| 2112 | + } | |
| 2113 | + | |
| 2075 | 2114 | www_print_timeline(&q, tmFlags, zThisUser, zThisTag, selectedRid, 0); |
| 2076 | 2115 | db_finalize(&q); |
| 2077 | 2116 | if( zOlderButton ){ |
| 2078 | 2117 | @ %z(xhref("class='button'","%z",zOlderButton))Older</a> |
| 2079 | 2118 | } |
| 2080 | 2119 |
| --- 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(®exp, 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 |