Fossil SCM

Improve quoting of match tags and patterns. Ignore empty match tags and patterns.

andygoth 2016-11-04 21:12 andygoth-timeline-ms
Commit 98cc8782e11f94d1dbd19958a2d068c547e94f65
1 file changed +94 -44
+94 -44
--- src/timeline.c
+++ src/timeline.c
@@ -1227,10 +1227,38 @@
12271227
MS_EXACT, /* Matches a single tag by exact string comparison. */
12281228
MS_GLOB, /* Matches tags against a list of GLOB patterns. */
12291229
MS_LIKE, /* Matches tags against a list of LIKE patterns. */
12301230
MS_REGEXP /* Matches tags against a list of regular expressions. */
12311231
} MatchStyle;
1232
+
1233
+/*
1234
+** Quote a tag string by surrounding it with double quotes and preceding
1235
+** internal double quotes and backslashes with backslashes.
1236
+*/
1237
+static const char *tagQuote(
1238
+ int len, /* Maximum length of zTag, or negative for unlimited */
1239
+ const char *zTag /* Tag string */
1240
+){
1241
+ Blob blob = BLOB_INITIALIZER;
1242
+ int i, j;
1243
+ blob_zero(&blob);
1244
+ blob_append(&blob, "\"", 1);
1245
+ for( i=j=0; zTag[j] && (len<0 || j<len); ++j ){
1246
+ if( zTag[j]=='\"' || zTag[j]=='\\' ){
1247
+ if( j>i ){
1248
+ blob_append(&blob, zTag+i, j-i);
1249
+ }
1250
+ blob_append(&blob, "\\", 1);
1251
+ i = j;
1252
+ }
1253
+ }
1254
+ if( j>i ){
1255
+ blob_append(&blob, zTag+i, j-i);
1256
+ }
1257
+ blob_append(&blob, "\"", 1);
1258
+ return blob_str(&blob);
1259
+}
12321260
12331261
/*
12341262
** Construct the tag match SQL expression.
12351263
**
12361264
** This function is adapted from glob_expr() to support the MS_EXACT, MS_GLOB,
@@ -1241,29 +1269,38 @@
12411269
** the tag table to access the "tagname" column.
12421270
**
12431271
** Each pattern is adjusted to to start with "sym-" and be anchored at end.
12441272
**
12451273
** In MS_REGEXP mode, backslash can be used to protect delimiter characters.
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.
12461279
*/
12471280
static const char *tagMatchExpression(
1248
- MatchStyle matchStyle, /* Match style code */
1249
- const char *zTag, /* Tag name, match pattern, or list of patterns */
1250
- int *pCount /* Pointer to match pattern count variable */
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 */
12511284
){
1252
- Blob blob = BLOB_INITIALIZER; /* SQL expression string assembly buffer */
1253
- const char *zStart; /* Text at start of expression */
1254
- const char *zDelimiter; /* Text between expression terms */
1255
- const char *zEnd; /* Text at end of expression */
1256
- const char *zPrefix; /* Text before each match pattern */
1257
- const char *zSuffix; /* Text after each match pattern */
1258
- char cDel; /* Input delimiter character */
1259
- int i; /* Input match pattern length counter */
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 */
12601297
12611298
/* Optimize exact matches by looking up the ID in advance to create a simple
12621299
* numeric comparison. Bypass the remainder of this function. */
12631300
if( matchStyle==MS_EXACT ){
1264
- *pCount = 1;
1301
+ *zDesc = tagQuote(-1, zTag);
12651302
return mprintf("(tagid=%d)", db_int(-1,
12661303
"SELECT tagid FROM tag WHERE tagname='sym-%q'", zTag));
12671304
}
12681305
12691306
/* Decide pattern prefix and suffix strings according to match style. */
@@ -1271,27 +1308,30 @@
12711308
zStart = "(";
12721309
zDelimiter = " OR ";
12731310
zEnd = ")";
12741311
zPrefix = "tagname GLOB 'sym-";
12751312
zSuffix = "'";
1313
+ zIntro = "glob pattern ";
12761314
}else if( matchStyle==MS_LIKE ){
12771315
zStart = "(";
12781316
zDelimiter = " OR ";
12791317
zEnd = ")";
12801318
zPrefix = "tagname LIKE 'sym-";
12811319
zSuffix = "'";
1320
+ zIntro = "SQL LIKE pattern ";
12821321
}else/* if( matchStyle==MS_REGEXP )*/{
12831322
zStart = "(tagname REGEXP '^sym-(";
12841323
zDelimiter = "|";
12851324
zEnd = ")$')";
12861325
zPrefix = "";
12871326
zSuffix = "";
1327
+ zIntro = "regular expression ";
12881328
}
12891329
1290
- /* Convert the list of matches into an SQL expression. */
1291
- *pCount = 0;
1292
- blob_zero(&blob);
1330
+ /* Convert the list of matches into an SQL expression and text description. */
1331
+ blob_zero(&expr);
1332
+ blob_zero(&desc);
12931333
while( 1 ){
12941334
/* Skip leading delimiters. */
12951335
for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag );
12961336
12971337
/* Next non-delimiter character determines quoting. */
@@ -1320,27 +1360,48 @@
13201360
}
13211361
}
13221362
13231363
/* Incorporate the match word into the output expression. The %q format is
13241364
* used to protect against SQL injection attacks by replacing ' with ''. */
1325
- blob_appendf(&blob, "%s%s%#q%s", *pCount ? zDelimiter : zStart,
1365
+ blob_appendf(&expr, "%s%s%#q%s", blob_size(&expr) ? zDelimiter : zStart,
13261366
zPrefix, i, zTag, zSuffix);
13271367
1328
- /* Keep track of the number of match expressions. */
1329
- ++*pCount;
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
+ }
13301384
13311385
/* Advance past all consumed input characters. */
13321386
zTag += i;
13331387
if( cDel!=',' && *zTag==cDel ){
13341388
++zTag;
13351389
}
13361390
}
1391
+
1392
+ /* Finalize and extract the pattern description. */
1393
+ if( zPattern ){
1394
+ blob_append(&desc, zOr, -1);
1395
+ blob_append(&desc, zPattern, -1);
1396
+ }
1397
+ *zDesc = blob_str(&desc);
13371398
13381399
/* Finalize and extract the SQL expression. */
1339
- if( *pCount ){
1340
- blob_append(&blob, zEnd, -1);
1341
- return blob_str(&blob);
1400
+ if( blob_size(&expr) ){
1401
+ blob_append(&expr, zEnd, -1);
1402
+ return blob_str(&expr);
13421403
}
13431404
13441405
/* If execution reaches this point, the pattern was empty. Return NULL. */
13451406
return 0;
13461407
}
@@ -1404,12 +1465,12 @@
14041465
const char *zMark = P("m"); /* Mark this event or an event this time */
14051466
const char *zTagName = P("t"); /* Show events with this tag */
14061467
const char *zBrName = P("r"); /* Show events related to this tag */
14071468
const char *zMatchStyle = P("ms"); /* Tag/branch match style string */
14081469
MatchStyle matchStyle = MS_EXACT; /* Match style code */
1470
+ const char *zMatchDesc = 0; /* Tag match expression description text */
14091471
const char *zTagSql = 0; /* Tag/branch match SQL expression */
1410
- int tagMatchCount = 0; /* Number of tag match patterns */
14111472
const char *zSearch = P("s"); /* Search string */
14121473
const char *zUses = P("uf"); /* Only show check-ins hold this file */
14131474
const char *zYearMonth = P("ym"); /* Show check-ins for the given YYYY-MM */
14141475
const char *zYearWeek = P("yw"); /* Check-ins for YYYY-WW (week-of-year) */
14151476
const char *zDay = P("ymd"); /* Check-ins for the day YYYY-MM-DD */
@@ -1469,13 +1530,13 @@
14691530
}
14701531
url_initialize(&url, "timeline");
14711532
cgi_query_parameters_to_url(&url);
14721533
14731534
/* Identify the tag or branch name or match pattern. */
1474
- if( zTagName ){
1535
+ if( zTagName && *zTagName ){
14751536
zThisTag = zTagName;
1476
- }else if( zBrName ){
1537
+ }else if( zBrName && *zBrName ){
14771538
zThisTag = zBrName;
14781539
}
14791540
14801541
/* Interpet the tag style string. */
14811542
if( zThisTag ){
@@ -1488,11 +1549,11 @@
14881549
}
14891550
}
14901551
14911552
/* Construct the tag match expression. */
14921553
if( zThisTag ){
1493
- zTagSql = tagMatchExpression(matchStyle, zThisTag, &tagMatchCount);
1554
+ zTagSql = tagMatchExpression(matchStyle, zThisTag, &zMatchDesc);
14941555
}
14951556
14961557
if( zTagName && g.perm.Read ){
14971558
style_submenu_element("Related", "Related", "%s",
14981559
url_render(&url, "r", zTagName, "t", 0));
@@ -1915,29 +1976,18 @@
19151976
tmFlags |= TIMELINE_DISJOINT;
19161977
}
19171978
if( zThisTag ){
19181979
if( matchStyle!=MS_EXACT ){
19191980
if( zTagName ){
1920
- blob_append(&desc, " with tags matching ", -1);
1921
- }else{
1922
- blob_append(&desc, " related to tags matching ", -1);
1923
- }
1924
- if( matchStyle==MS_GLOB ){
1925
- blob_append(&desc, " glob pattern", -1);
1926
- }else if( matchStyle==MS_LIKE ){
1927
- blob_append(&desc, " SQL LIKE pattern", -1);
1928
- }else/* if( matchStyle==MS_REGEXP )*/{
1929
- blob_append(&desc, " regular expression", -1);
1930
- }
1931
- if( tagMatchCount!=1 ){
1932
- blob_append(&desc, "s", 1);
1933
- }
1934
- blob_appendf(&desc, " (%h)", zThisTag);
1935
- }else if( zTagName ){
1936
- blob_appendf(&desc, " tagged with \"%h\"", zTagName);
1937
- }else{
1938
- blob_appendf(&desc, " related to \"%h\"", zBrName);
1981
+ blob_appendf(&desc, " with tags matching %h", zMatchDesc);
1982
+ }else{
1983
+ blob_appendf(&desc, " related to tags matching %h", zMatchDesc);
1984
+ }
1985
+ }else if( zTagName ){
1986
+ blob_appendf(&desc, " tagged with %h", zMatchDesc);
1987
+ }else{
1988
+ blob_appendf(&desc, " related to %h", zMatchDesc);
19391989
}
19401990
tmFlags |= TIMELINE_DISJOINT;
19411991
}
19421992
addFileGlobDescription(zChng, &desc);
19431993
if( rAfter>0.0 ){
19441994
--- src/timeline.c
+++ src/timeline.c
@@ -1227,10 +1227,38 @@
1227 MS_EXACT, /* Matches a single tag by exact string comparison. */
1228 MS_GLOB, /* Matches tags against a list of GLOB patterns. */
1229 MS_LIKE, /* Matches tags against a list of LIKE patterns. */
1230 MS_REGEXP /* Matches tags against a list of regular expressions. */
1231 } MatchStyle;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1232
1233 /*
1234 ** Construct the tag match SQL expression.
1235 **
1236 ** This function is adapted from glob_expr() to support the MS_EXACT, MS_GLOB,
@@ -1241,29 +1269,38 @@
1241 ** the tag table to access the "tagname" column.
1242 **
1243 ** Each pattern is adjusted to to start with "sym-" and be anchored at end.
1244 **
1245 ** In MS_REGEXP mode, backslash can be used to protect delimiter characters.
 
 
 
 
 
1246 */
1247 static const char *tagMatchExpression(
1248 MatchStyle matchStyle, /* Match style code */
1249 const char *zTag, /* Tag name, match pattern, or list of patterns */
1250 int *pCount /* Pointer to match pattern count variable */
1251 ){
1252 Blob blob = BLOB_INITIALIZER; /* SQL expression string assembly buffer */
1253 const char *zStart; /* Text at start of expression */
1254 const char *zDelimiter; /* Text between expression terms */
1255 const char *zEnd; /* Text at end of expression */
1256 const char *zPrefix; /* Text before each match pattern */
1257 const char *zSuffix; /* Text after each match pattern */
1258 char cDel; /* Input delimiter character */
1259 int i; /* Input match pattern length counter */
 
 
 
 
1260
1261 /* Optimize exact matches by looking up the ID in advance to create a simple
1262 * numeric comparison. Bypass the remainder of this function. */
1263 if( matchStyle==MS_EXACT ){
1264 *pCount = 1;
1265 return mprintf("(tagid=%d)", db_int(-1,
1266 "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTag));
1267 }
1268
1269 /* Decide pattern prefix and suffix strings according to match style. */
@@ -1271,27 +1308,30 @@
1271 zStart = "(";
1272 zDelimiter = " OR ";
1273 zEnd = ")";
1274 zPrefix = "tagname GLOB 'sym-";
1275 zSuffix = "'";
 
1276 }else if( matchStyle==MS_LIKE ){
1277 zStart = "(";
1278 zDelimiter = " OR ";
1279 zEnd = ")";
1280 zPrefix = "tagname LIKE 'sym-";
1281 zSuffix = "'";
 
1282 }else/* if( matchStyle==MS_REGEXP )*/{
1283 zStart = "(tagname REGEXP '^sym-(";
1284 zDelimiter = "|";
1285 zEnd = ")$')";
1286 zPrefix = "";
1287 zSuffix = "";
 
1288 }
1289
1290 /* Convert the list of matches into an SQL expression. */
1291 *pCount = 0;
1292 blob_zero(&blob);
1293 while( 1 ){
1294 /* Skip leading delimiters. */
1295 for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag );
1296
1297 /* Next non-delimiter character determines quoting. */
@@ -1320,27 +1360,48 @@
1320 }
1321 }
1322
1323 /* Incorporate the match word into the output expression. The %q format is
1324 * used to protect against SQL injection attacks by replacing ' with ''. */
1325 blob_appendf(&blob, "%s%s%#q%s", *pCount ? zDelimiter : zStart,
1326 zPrefix, i, zTag, zSuffix);
1327
1328 /* Keep track of the number of match expressions. */
1329 ++*pCount;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1330
1331 /* Advance past all consumed input characters. */
1332 zTag += i;
1333 if( cDel!=',' && *zTag==cDel ){
1334 ++zTag;
1335 }
1336 }
 
 
 
 
 
 
 
1337
1338 /* Finalize and extract the SQL expression. */
1339 if( *pCount ){
1340 blob_append(&blob, zEnd, -1);
1341 return blob_str(&blob);
1342 }
1343
1344 /* If execution reaches this point, the pattern was empty. Return NULL. */
1345 return 0;
1346 }
@@ -1404,12 +1465,12 @@
1404 const char *zMark = P("m"); /* Mark this event or an event this time */
1405 const char *zTagName = P("t"); /* Show events with this tag */
1406 const char *zBrName = P("r"); /* Show events related to this tag */
1407 const char *zMatchStyle = P("ms"); /* Tag/branch match style string */
1408 MatchStyle matchStyle = MS_EXACT; /* Match style code */
 
1409 const char *zTagSql = 0; /* Tag/branch match SQL expression */
1410 int tagMatchCount = 0; /* Number of tag match patterns */
1411 const char *zSearch = P("s"); /* Search string */
1412 const char *zUses = P("uf"); /* Only show check-ins hold this file */
1413 const char *zYearMonth = P("ym"); /* Show check-ins for the given YYYY-MM */
1414 const char *zYearWeek = P("yw"); /* Check-ins for YYYY-WW (week-of-year) */
1415 const char *zDay = P("ymd"); /* Check-ins for the day YYYY-MM-DD */
@@ -1469,13 +1530,13 @@
1469 }
1470 url_initialize(&url, "timeline");
1471 cgi_query_parameters_to_url(&url);
1472
1473 /* Identify the tag or branch name or match pattern. */
1474 if( zTagName ){
1475 zThisTag = zTagName;
1476 }else if( zBrName ){
1477 zThisTag = zBrName;
1478 }
1479
1480 /* Interpet the tag style string. */
1481 if( zThisTag ){
@@ -1488,11 +1549,11 @@
1488 }
1489 }
1490
1491 /* Construct the tag match expression. */
1492 if( zThisTag ){
1493 zTagSql = tagMatchExpression(matchStyle, zThisTag, &tagMatchCount);
1494 }
1495
1496 if( zTagName && g.perm.Read ){
1497 style_submenu_element("Related", "Related", "%s",
1498 url_render(&url, "r", zTagName, "t", 0));
@@ -1915,29 +1976,18 @@
1915 tmFlags |= TIMELINE_DISJOINT;
1916 }
1917 if( zThisTag ){
1918 if( matchStyle!=MS_EXACT ){
1919 if( zTagName ){
1920 blob_append(&desc, " with tags matching ", -1);
1921 }else{
1922 blob_append(&desc, " related to tags matching ", -1);
1923 }
1924 if( matchStyle==MS_GLOB ){
1925 blob_append(&desc, " glob pattern", -1);
1926 }else if( matchStyle==MS_LIKE ){
1927 blob_append(&desc, " SQL LIKE pattern", -1);
1928 }else/* if( matchStyle==MS_REGEXP )*/{
1929 blob_append(&desc, " regular expression", -1);
1930 }
1931 if( tagMatchCount!=1 ){
1932 blob_append(&desc, "s", 1);
1933 }
1934 blob_appendf(&desc, " (%h)", zThisTag);
1935 }else if( zTagName ){
1936 blob_appendf(&desc, " tagged with \"%h\"", zTagName);
1937 }else{
1938 blob_appendf(&desc, " related to \"%h\"", zBrName);
1939 }
1940 tmFlags |= TIMELINE_DISJOINT;
1941 }
1942 addFileGlobDescription(zChng, &desc);
1943 if( rAfter>0.0 ){
1944
--- src/timeline.c
+++ src/timeline.c
@@ -1227,10 +1227,38 @@
1227 MS_EXACT, /* Matches a single tag by exact string comparison. */
1228 MS_GLOB, /* Matches tags against a list of GLOB patterns. */
1229 MS_LIKE, /* Matches tags against a list of LIKE patterns. */
1230 MS_REGEXP /* Matches tags against a list of regular expressions. */
1231 } MatchStyle;
1232
1233 /*
1234 ** Quote a tag string by surrounding it with double quotes and preceding
1235 ** internal double quotes and backslashes with backslashes.
1236 */
1237 static const char *tagQuote(
1238 int len, /* Maximum length of zTag, or negative for unlimited */
1239 const char *zTag /* Tag string */
1240 ){
1241 Blob blob = BLOB_INITIALIZER;
1242 int i, j;
1243 blob_zero(&blob);
1244 blob_append(&blob, "\"", 1);
1245 for( i=j=0; zTag[j] && (len<0 || j<len); ++j ){
1246 if( zTag[j]=='\"' || zTag[j]=='\\' ){
1247 if( j>i ){
1248 blob_append(&blob, zTag+i, j-i);
1249 }
1250 blob_append(&blob, "\\", 1);
1251 i = j;
1252 }
1253 }
1254 if( j>i ){
1255 blob_append(&blob, zTag+i, j-i);
1256 }
1257 blob_append(&blob, "\"", 1);
1258 return blob_str(&blob);
1259 }
1260
1261 /*
1262 ** Construct the tag match SQL expression.
1263 **
1264 ** This function is adapted from glob_expr() to support the MS_EXACT, MS_GLOB,
@@ -1241,29 +1269,38 @@
1269 ** the tag table to access the "tagname" column.
1270 **
1271 ** Each pattern is adjusted to to start with "sym-" and be anchored at end.
1272 **
1273 ** In MS_REGEXP mode, backslash can be used to protect delimiter characters.
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
1299 * numeric comparison. Bypass the remainder of this function. */
1300 if( matchStyle==MS_EXACT ){
1301 *zDesc = tagQuote(-1, zTag);
1302 return mprintf("(tagid=%d)", db_int(-1,
1303 "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTag));
1304 }
1305
1306 /* Decide pattern prefix and suffix strings according to match style. */
@@ -1271,27 +1308,30 @@
1308 zStart = "(";
1309 zDelimiter = " OR ";
1310 zEnd = ")";
1311 zPrefix = "tagname GLOB 'sym-";
1312 zSuffix = "'";
1313 zIntro = "glob pattern ";
1314 }else if( matchStyle==MS_LIKE ){
1315 zStart = "(";
1316 zDelimiter = " OR ";
1317 zEnd = ")";
1318 zPrefix = "tagname LIKE 'sym-";
1319 zSuffix = "'";
1320 zIntro = "SQL LIKE pattern ";
1321 }else/* if( matchStyle==MS_REGEXP )*/{
1322 zStart = "(tagname REGEXP '^sym-(";
1323 zDelimiter = "|";
1324 zEnd = ")$')";
1325 zPrefix = "";
1326 zSuffix = "";
1327 zIntro = "regular expression ";
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. */
@@ -1320,27 +1360,48 @@
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 ){
1388 ++zTag;
1389 }
1390 }
1391
1392 /* Finalize and extract the pattern description. */
1393 if( zPattern ){
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 }
1404
1405 /* If execution reaches this point, the pattern was empty. Return NULL. */
1406 return 0;
1407 }
@@ -1404,12 +1465,12 @@
1465 const char *zMark = P("m"); /* Mark this event or an event this time */
1466 const char *zTagName = P("t"); /* Show events with this tag */
1467 const char *zBrName = P("r"); /* Show events related to this tag */
1468 const char *zMatchStyle = P("ms"); /* Tag/branch match style string */
1469 MatchStyle matchStyle = MS_EXACT; /* Match style code */
1470 const char *zMatchDesc = 0; /* Tag match expression description text */
1471 const char *zTagSql = 0; /* Tag/branch match SQL expression */
 
1472 const char *zSearch = P("s"); /* Search string */
1473 const char *zUses = P("uf"); /* Only show check-ins hold this file */
1474 const char *zYearMonth = P("ym"); /* Show check-ins for the given YYYY-MM */
1475 const char *zYearWeek = P("yw"); /* Check-ins for YYYY-WW (week-of-year) */
1476 const char *zDay = P("ymd"); /* Check-ins for the day YYYY-MM-DD */
@@ -1469,13 +1530,13 @@
1530 }
1531 url_initialize(&url, "timeline");
1532 cgi_query_parameters_to_url(&url);
1533
1534 /* Identify the tag or branch name or match pattern. */
1535 if( zTagName && *zTagName ){
1536 zThisTag = zTagName;
1537 }else if( zBrName && *zBrName ){
1538 zThisTag = zBrName;
1539 }
1540
1541 /* Interpet the tag style string. */
1542 if( zThisTag ){
@@ -1488,11 +1549,11 @@
1549 }
1550 }
1551
1552 /* Construct the tag match expression. */
1553 if( zThisTag ){
1554 zTagSql = tagMatchExpression(matchStyle, zThisTag, &zMatchDesc);
1555 }
1556
1557 if( zTagName && g.perm.Read ){
1558 style_submenu_element("Related", "Related", "%s",
1559 url_render(&url, "r", zTagName, "t", 0));
@@ -1915,29 +1976,18 @@
1976 tmFlags |= TIMELINE_DISJOINT;
1977 }
1978 if( zThisTag ){
1979 if( matchStyle!=MS_EXACT ){
1980 if( zTagName ){
1981 blob_appendf(&desc, " with tags matching %h", zMatchDesc);
1982 }else{
1983 blob_appendf(&desc, " related to tags matching %h", zMatchDesc);
1984 }
1985 }else if( zTagName ){
1986 blob_appendf(&desc, " tagged with %h", zMatchDesc);
1987 }else{
1988 blob_appendf(&desc, " related to %h", zMatchDesc);
 
 
 
 
 
 
 
 
 
 
 
1989 }
1990 tmFlags |= TIMELINE_DISJOINT;
1991 }
1992 addFileGlobDescription(zChng, &desc);
1993 if( rAfter>0.0 ){
1994

Keyboard Shortcuts

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