Fossil SCM
Enhance the verify-comments subroutine in "fossil commit" so that if the comment text contains any of the special charaters "`*_\<&[" then the formatted text is previewed to the operator who must then confirm before continuing (unless --allow-suspect-comment is used or the verify-comments setting is off).
Commit
4ee3362aac8832aafa5414d5395679720b0f6709e815ab86cb9fa7672b23d087
Parent
43ee0921ec3f3a9…
2 files changed
+28
-3
+20
-5
+28
-3
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -2305,10 +2305,11 @@ | ||
| 2305 | 2305 | char *z; |
| 2306 | 2306 | char *zEnd, *zEnd2; |
| 2307 | 2307 | char *zSep; |
| 2308 | 2308 | char cSave1; |
| 2309 | 2309 | int nIssue = 0; |
| 2310 | + static const char zSpecial[] = "\\&<*_`["; | |
| 2310 | 2311 | |
| 2311 | 2312 | if( !db_get_boolean("verify-comments",1) ) return 0; |
| 2312 | 2313 | z = zStart; |
| 2313 | 2314 | blob_init(pSus, 0, 0); |
| 2314 | 2315 | while( (z = strchr(z,'['))!=0 ){ |
| @@ -2325,26 +2326,50 @@ | ||
| 2325 | 2326 | z = zEnd2; |
| 2326 | 2327 | continue; |
| 2327 | 2328 | } |
| 2328 | 2329 | zSep = strchr(z+1,'|'); |
| 2329 | 2330 | if( zSep==0 || zSep>zEnd ) zSep = zEnd; |
| 2330 | - cSave1 = zEnd[0]; | |
| 2331 | - zEnd[0] = 0; | |
| 2331 | + while( zSep>z && fossil_isspace(zSep[-1]) ) zSep--; | |
| 2332 | + cSave1 = zSep[0]; | |
| 2333 | + zSep[0] = 0; | |
| 2332 | 2334 | if( !wiki_valid_link_target(z+1) ){ |
| 2333 | 2335 | blob_appendf(pSus,"\n (%d) ", ++nIssue); |
| 2334 | 2336 | blob_appendf(pSus, "Broken hyperlink: [%s]", z+1); |
| 2335 | 2337 | } |
| 2336 | - zEnd[0] = cSave1; | |
| 2338 | + zSep[0] = cSave1; | |
| 2337 | 2339 | z = zEnd; |
| 2338 | 2340 | } |
| 2339 | 2341 | if( nIssue ){ |
| 2340 | 2342 | Blob tmp = *pSus; |
| 2341 | 2343 | blob_init(pSus, 0, 0); |
| 2342 | 2344 | blob_appendf(pSus, |
| 2343 | 2345 | "Possible comment formatting error%s:%b\n", |
| 2344 | 2346 | nIssue>1 ? "s" : "", &tmp); |
| 2345 | 2347 | blob_reset(&tmp); |
| 2348 | + return 1; | |
| 2349 | + }else if( strcspn(zStart,zSpecial)<strlen(zStart) ){ | |
| 2350 | + Blob in, html, txt; | |
| 2351 | + char zGot[16]; | |
| 2352 | + int nGot = 0; | |
| 2353 | + int i; | |
| 2354 | + for(i=0; zSpecial[i]; i++){ | |
| 2355 | + if( strchr(zStart,zSpecial[i]) ) zGot[nGot++] = zSpecial[i]; | |
| 2356 | + } | |
| 2357 | + zGot[nGot] = 0; | |
| 2358 | + blob_init(&in, blob_str(pComment), -1); | |
| 2359 | + blob_init(&html, 0, 0); | |
| 2360 | + wiki_convert(&in, &html, WIKI_INLINE); | |
| 2361 | + blob_reset(&in); | |
| 2362 | + blob_init(&txt, 0, 0); | |
| 2363 | + html_to_plaintext(blob_str(&html), &txt); | |
| 2364 | + blob_reset(&html); | |
| 2365 | + fossil_print("The comment uses special character%s \"%s\". " | |
| 2366 | + "Does it render as you expect?\n\n ", | |
| 2367 | + nGot>1 ? "s" : "", zGot); | |
| 2368 | + comment_print(blob_str(&txt), 0, 3, -1, get_comment_format()); | |
| 2369 | + blob_init(pSus, 0, 0); | |
| 2370 | + blob_appendf(pSus, "\n"); | |
| 2346 | 2371 | return 1; |
| 2347 | 2372 | }else{ |
| 2348 | 2373 | return 0; |
| 2349 | 2374 | } |
| 2350 | 2375 | } |
| 2351 | 2376 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -2305,10 +2305,11 @@ | |
| 2305 | char *z; |
| 2306 | char *zEnd, *zEnd2; |
| 2307 | char *zSep; |
| 2308 | char cSave1; |
| 2309 | int nIssue = 0; |
| 2310 | |
| 2311 | if( !db_get_boolean("verify-comments",1) ) return 0; |
| 2312 | z = zStart; |
| 2313 | blob_init(pSus, 0, 0); |
| 2314 | while( (z = strchr(z,'['))!=0 ){ |
| @@ -2325,26 +2326,50 @@ | |
| 2325 | z = zEnd2; |
| 2326 | continue; |
| 2327 | } |
| 2328 | zSep = strchr(z+1,'|'); |
| 2329 | if( zSep==0 || zSep>zEnd ) zSep = zEnd; |
| 2330 | cSave1 = zEnd[0]; |
| 2331 | zEnd[0] = 0; |
| 2332 | if( !wiki_valid_link_target(z+1) ){ |
| 2333 | blob_appendf(pSus,"\n (%d) ", ++nIssue); |
| 2334 | blob_appendf(pSus, "Broken hyperlink: [%s]", z+1); |
| 2335 | } |
| 2336 | zEnd[0] = cSave1; |
| 2337 | z = zEnd; |
| 2338 | } |
| 2339 | if( nIssue ){ |
| 2340 | Blob tmp = *pSus; |
| 2341 | blob_init(pSus, 0, 0); |
| 2342 | blob_appendf(pSus, |
| 2343 | "Possible comment formatting error%s:%b\n", |
| 2344 | nIssue>1 ? "s" : "", &tmp); |
| 2345 | blob_reset(&tmp); |
| 2346 | return 1; |
| 2347 | }else{ |
| 2348 | return 0; |
| 2349 | } |
| 2350 | } |
| 2351 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -2305,10 +2305,11 @@ | |
| 2305 | char *z; |
| 2306 | char *zEnd, *zEnd2; |
| 2307 | char *zSep; |
| 2308 | char cSave1; |
| 2309 | int nIssue = 0; |
| 2310 | static const char zSpecial[] = "\\&<*_`["; |
| 2311 | |
| 2312 | if( !db_get_boolean("verify-comments",1) ) return 0; |
| 2313 | z = zStart; |
| 2314 | blob_init(pSus, 0, 0); |
| 2315 | while( (z = strchr(z,'['))!=0 ){ |
| @@ -2325,26 +2326,50 @@ | |
| 2326 | z = zEnd2; |
| 2327 | continue; |
| 2328 | } |
| 2329 | zSep = strchr(z+1,'|'); |
| 2330 | if( zSep==0 || zSep>zEnd ) zSep = zEnd; |
| 2331 | while( zSep>z && fossil_isspace(zSep[-1]) ) zSep--; |
| 2332 | cSave1 = zSep[0]; |
| 2333 | zSep[0] = 0; |
| 2334 | if( !wiki_valid_link_target(z+1) ){ |
| 2335 | blob_appendf(pSus,"\n (%d) ", ++nIssue); |
| 2336 | blob_appendf(pSus, "Broken hyperlink: [%s]", z+1); |
| 2337 | } |
| 2338 | zSep[0] = cSave1; |
| 2339 | z = zEnd; |
| 2340 | } |
| 2341 | if( nIssue ){ |
| 2342 | Blob tmp = *pSus; |
| 2343 | blob_init(pSus, 0, 0); |
| 2344 | blob_appendf(pSus, |
| 2345 | "Possible comment formatting error%s:%b\n", |
| 2346 | nIssue>1 ? "s" : "", &tmp); |
| 2347 | blob_reset(&tmp); |
| 2348 | return 1; |
| 2349 | }else if( strcspn(zStart,zSpecial)<strlen(zStart) ){ |
| 2350 | Blob in, html, txt; |
| 2351 | char zGot[16]; |
| 2352 | int nGot = 0; |
| 2353 | int i; |
| 2354 | for(i=0; zSpecial[i]; i++){ |
| 2355 | if( strchr(zStart,zSpecial[i]) ) zGot[nGot++] = zSpecial[i]; |
| 2356 | } |
| 2357 | zGot[nGot] = 0; |
| 2358 | blob_init(&in, blob_str(pComment), -1); |
| 2359 | blob_init(&html, 0, 0); |
| 2360 | wiki_convert(&in, &html, WIKI_INLINE); |
| 2361 | blob_reset(&in); |
| 2362 | blob_init(&txt, 0, 0); |
| 2363 | html_to_plaintext(blob_str(&html), &txt); |
| 2364 | blob_reset(&html); |
| 2365 | fossil_print("The comment uses special character%s \"%s\". " |
| 2366 | "Does it render as you expect?\n\n ", |
| 2367 | nGot>1 ? "s" : "", zGot); |
| 2368 | comment_print(blob_str(&txt), 0, 3, -1, get_comment_format()); |
| 2369 | blob_init(pSus, 0, 0); |
| 2370 | blob_appendf(pSus, "\n"); |
| 2371 | return 1; |
| 2372 | }else{ |
| 2373 | return 0; |
| 2374 | } |
| 2375 | } |
| 2376 |
+20
-5
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -2489,20 +2489,21 @@ | ||
| 2489 | 2489 | blob_append_char(pOut, nNL ? '\n' : ' '); |
| 2490 | 2490 | nWS = 1; |
| 2491 | 2491 | } |
| 2492 | 2492 | } |
| 2493 | 2493 | }else if( zIn[0]=='&' ){ |
| 2494 | - char c = '?'; | |
| 2494 | + u32 c = '?'; | |
| 2495 | 2495 | if( zIn[1]=='#' ){ |
| 2496 | - int x = atoi(&zIn[1]); | |
| 2497 | - if( x>0 && x<=127 ) c = x; | |
| 2496 | + c = atoi(&zIn[2]); | |
| 2497 | + if( c==0 ) c = '?'; | |
| 2498 | 2498 | }else{ |
| 2499 | - static const struct { int n; char c; char *z; } aEntity[] = { | |
| 2499 | + static const struct { int n; u32 c; char *z; } aEntity[] = { | |
| 2500 | 2500 | { 5, '&', "&" }, |
| 2501 | 2501 | { 4, '<', "<" }, |
| 2502 | 2502 | { 4, '>', ">" }, |
| 2503 | 2503 | { 6, ' ', " " }, |
| 2504 | + { 6, '"', """ }, | |
| 2504 | 2505 | }; |
| 2505 | 2506 | int jj; |
| 2506 | 2507 | for(jj=0; jj<count(aEntity); jj++){ |
| 2507 | 2508 | if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){ |
| 2508 | 2509 | c = aEntity[jj].c; |
| @@ -2516,11 +2517,25 @@ | ||
| 2516 | 2517 | nNL = c=='\n'; |
| 2517 | 2518 | }else{ |
| 2518 | 2519 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2519 | 2520 | seenText = 1; |
| 2520 | 2521 | nNL = nWS = 0; |
| 2521 | - blob_append_char(pOut, c); | |
| 2522 | + if( c<0x00080 ){ | |
| 2523 | + blob_append_char(pOut, c & 0xff); | |
| 2524 | + }else if( c<0x00800 ){ | |
| 2525 | + blob_append_char(pOut, 0xc0 + (u8)((c>>6)&0x1f)); | |
| 2526 | + blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); | |
| 2527 | + }else if( c<0x10000 ){ | |
| 2528 | + blob_append_char(pOut, 0xe0 + (u8)((c>>12)&0x0f)); | |
| 2529 | + blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f)); | |
| 2530 | + blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); | |
| 2531 | + }else{ | |
| 2532 | + blob_append_char(pOut, 0xf0 + (u8)((c>>18)&0x07)); | |
| 2533 | + blob_append_char(pOut, 0x80 + (u8)((c>>12)&0x3f)); | |
| 2534 | + blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f)); | |
| 2535 | + blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); | |
| 2536 | + } | |
| 2522 | 2537 | } |
| 2523 | 2538 | }else{ |
| 2524 | 2539 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2525 | 2540 | seenText = 1; |
| 2526 | 2541 | nNL = nWS = 0; |
| 2527 | 2542 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -2489,20 +2489,21 @@ | |
| 2489 | blob_append_char(pOut, nNL ? '\n' : ' '); |
| 2490 | nWS = 1; |
| 2491 | } |
| 2492 | } |
| 2493 | }else if( zIn[0]=='&' ){ |
| 2494 | char c = '?'; |
| 2495 | if( zIn[1]=='#' ){ |
| 2496 | int x = atoi(&zIn[1]); |
| 2497 | if( x>0 && x<=127 ) c = x; |
| 2498 | }else{ |
| 2499 | static const struct { int n; char c; char *z; } aEntity[] = { |
| 2500 | { 5, '&', "&" }, |
| 2501 | { 4, '<', "<" }, |
| 2502 | { 4, '>', ">" }, |
| 2503 | { 6, ' ', " " }, |
| 2504 | }; |
| 2505 | int jj; |
| 2506 | for(jj=0; jj<count(aEntity); jj++){ |
| 2507 | if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){ |
| 2508 | c = aEntity[jj].c; |
| @@ -2516,11 +2517,25 @@ | |
| 2516 | nNL = c=='\n'; |
| 2517 | }else{ |
| 2518 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2519 | seenText = 1; |
| 2520 | nNL = nWS = 0; |
| 2521 | blob_append_char(pOut, c); |
| 2522 | } |
| 2523 | }else{ |
| 2524 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2525 | seenText = 1; |
| 2526 | nNL = nWS = 0; |
| 2527 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -2489,20 +2489,21 @@ | |
| 2489 | blob_append_char(pOut, nNL ? '\n' : ' '); |
| 2490 | nWS = 1; |
| 2491 | } |
| 2492 | } |
| 2493 | }else if( zIn[0]=='&' ){ |
| 2494 | u32 c = '?'; |
| 2495 | if( zIn[1]=='#' ){ |
| 2496 | c = atoi(&zIn[2]); |
| 2497 | if( c==0 ) c = '?'; |
| 2498 | }else{ |
| 2499 | static const struct { int n; u32 c; char *z; } aEntity[] = { |
| 2500 | { 5, '&', "&" }, |
| 2501 | { 4, '<', "<" }, |
| 2502 | { 4, '>', ">" }, |
| 2503 | { 6, ' ', " " }, |
| 2504 | { 6, '"', """ }, |
| 2505 | }; |
| 2506 | int jj; |
| 2507 | for(jj=0; jj<count(aEntity); jj++){ |
| 2508 | if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){ |
| 2509 | c = aEntity[jj].c; |
| @@ -2516,11 +2517,25 @@ | |
| 2517 | nNL = c=='\n'; |
| 2518 | }else{ |
| 2519 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2520 | seenText = 1; |
| 2521 | nNL = nWS = 0; |
| 2522 | if( c<0x00080 ){ |
| 2523 | blob_append_char(pOut, c & 0xff); |
| 2524 | }else if( c<0x00800 ){ |
| 2525 | blob_append_char(pOut, 0xc0 + (u8)((c>>6)&0x1f)); |
| 2526 | blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); |
| 2527 | }else if( c<0x10000 ){ |
| 2528 | blob_append_char(pOut, 0xe0 + (u8)((c>>12)&0x0f)); |
| 2529 | blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f)); |
| 2530 | blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); |
| 2531 | }else{ |
| 2532 | blob_append_char(pOut, 0xf0 + (u8)((c>>18)&0x07)); |
| 2533 | blob_append_char(pOut, 0x80 + (u8)((c>>12)&0x3f)); |
| 2534 | blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f)); |
| 2535 | blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); |
| 2536 | } |
| 2537 | } |
| 2538 | }else{ |
| 2539 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2540 | seenText = 1; |
| 2541 | nNL = nWS = 0; |
| 2542 |