Fossil SCM
merged in trunk [272e304d3f].
Commit
34359c3ad1324acc56df349be331e221640d1da3
Parent
14ed7070674729a…
37 files changed
+2
-2
+14
-2
+14
-2
+1
+2
-2
+3
-3
+1
-1
+1
-1
+16
-2
+16
-2
+2
-2
+13
-2
-2
+45
-27
+45
-27
+22
-7
+3
-2
+29
-7
+329
-260
+329
-260
+9
-5
+1
-1
+1
-1
+98
-64
+1
-1
+56
-26
+1
-1
+1
-1
+1
-1
+1
-1
+1
-1
+1
-1
+1
-1
+1
-1
+2
+1
-1
+5
-4
~
src/add.c
~
src/blob.c
~
src/blob.c
~
src/browse.c
~
src/checkin.c
~
src/checkout.c
~
src/configure.c
~
src/content.c
~
src/db.c
~
src/db.c
~
src/descendants.c
~
src/doc.c
~
src/info.c
~
src/main.c
~
src/main.c
~
src/manifest.c
~
src/md5.c
~
src/mkindex.c
~
src/name.c
~
src/name.c
~
src/rebuild.c
~
src/search.c
~
src/sha1.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/stash.c
~
src/tar.c
~
src/tkt.c
~
src/tkt.c
~
src/undo.c
~
src/user.c
~
src/wiki.c
~
src/wiki.c
~
src/winhttp.c
~
src/xfer.c
~
src/zip.c
~
www/checkin_names.wiki
+2
-2
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -260,11 +260,11 @@ | ||
| 260 | 260 | db_end_transaction(0); |
| 261 | 261 | } |
| 262 | 262 | |
| 263 | 263 | /* |
| 264 | 264 | ** COMMAND: rm |
| 265 | -** COMMAND: delete | |
| 265 | +** COMMAND: delete* | |
| 266 | 266 | ** |
| 267 | 267 | ** Usage: %fossil rm FILE1 ?FILE2 ...? |
| 268 | 268 | ** or: %fossil delete FILE1 ?FILE2 ...? |
| 269 | 269 | ** |
| 270 | 270 | ** Remove one or more files or directories from the repository. |
| @@ -473,11 +473,11 @@ | ||
| 473 | 473 | ); |
| 474 | 474 | } |
| 475 | 475 | |
| 476 | 476 | /* |
| 477 | 477 | ** COMMAND: mv |
| 478 | -** COMMAND: rename | |
| 478 | +** COMMAND: rename* | |
| 479 | 479 | ** |
| 480 | 480 | ** Usage: %fossil mv|rename OLDNAME NEWNAME |
| 481 | 481 | ** or: %fossil mv|rename OLDNAME... DIR |
| 482 | 482 | ** |
| 483 | 483 | ** Move or rename one or more files or directories within the repository tree. |
| 484 | 484 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -260,11 +260,11 @@ | |
| 260 | db_end_transaction(0); |
| 261 | } |
| 262 | |
| 263 | /* |
| 264 | ** COMMAND: rm |
| 265 | ** COMMAND: delete |
| 266 | ** |
| 267 | ** Usage: %fossil rm FILE1 ?FILE2 ...? |
| 268 | ** or: %fossil delete FILE1 ?FILE2 ...? |
| 269 | ** |
| 270 | ** Remove one or more files or directories from the repository. |
| @@ -473,11 +473,11 @@ | |
| 473 | ); |
| 474 | } |
| 475 | |
| 476 | /* |
| 477 | ** COMMAND: mv |
| 478 | ** COMMAND: rename |
| 479 | ** |
| 480 | ** Usage: %fossil mv|rename OLDNAME NEWNAME |
| 481 | ** or: %fossil mv|rename OLDNAME... DIR |
| 482 | ** |
| 483 | ** Move or rename one or more files or directories within the repository tree. |
| 484 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -260,11 +260,11 @@ | |
| 260 | db_end_transaction(0); |
| 261 | } |
| 262 | |
| 263 | /* |
| 264 | ** COMMAND: rm |
| 265 | ** COMMAND: delete* |
| 266 | ** |
| 267 | ** Usage: %fossil rm FILE1 ?FILE2 ...? |
| 268 | ** or: %fossil delete FILE1 ?FILE2 ...? |
| 269 | ** |
| 270 | ** Remove one or more files or directories from the repository. |
| @@ -473,11 +473,11 @@ | |
| 473 | ); |
| 474 | } |
| 475 | |
| 476 | /* |
| 477 | ** COMMAND: mv |
| 478 | ** COMMAND: rename* |
| 479 | ** |
| 480 | ** Usage: %fossil mv|rename OLDNAME NEWNAME |
| 481 | ** or: %fossil mv|rename OLDNAME... DIR |
| 482 | ** |
| 483 | ** Move or rename one or more files or directories within the repository tree. |
| 484 |
+14
-2
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -766,12 +766,24 @@ | ||
| 766 | 766 | int blob_write_to_file(Blob *pBlob, const char *zFilename){ |
| 767 | 767 | FILE *out; |
| 768 | 768 | int wrote; |
| 769 | 769 | |
| 770 | 770 | if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ |
| 771 | - fossil_puts(blob_str(pBlob), 0); | |
| 772 | - return blob_size(pBlob); | |
| 771 | + int n; | |
| 772 | +#if defined(_WIN32) | |
| 773 | + if( _isatty(fileno(stdout)) ){ | |
| 774 | + char *z; | |
| 775 | + z = fossil_utf8_to_console(blob_str(pBlob)); | |
| 776 | + n = strlen(z); | |
| 777 | + fwrite(z, 1, n, stdout); | |
| 778 | + free(z); | |
| 779 | + return n; | |
| 780 | + } | |
| 781 | +#endif | |
| 782 | + n = blob_size(pBlob); | |
| 783 | + fwrite(blob_buffer(pBlob), 1, n, stdout); | |
| 784 | + return n; | |
| 773 | 785 | }else{ |
| 774 | 786 | int i, nName; |
| 775 | 787 | char *zName, zBuf[1000]; |
| 776 | 788 | |
| 777 | 789 | nName = strlen(zFilename); |
| 778 | 790 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -766,12 +766,24 @@ | |
| 766 | int blob_write_to_file(Blob *pBlob, const char *zFilename){ |
| 767 | FILE *out; |
| 768 | int wrote; |
| 769 | |
| 770 | if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ |
| 771 | fossil_puts(blob_str(pBlob), 0); |
| 772 | return blob_size(pBlob); |
| 773 | }else{ |
| 774 | int i, nName; |
| 775 | char *zName, zBuf[1000]; |
| 776 | |
| 777 | nName = strlen(zFilename); |
| 778 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -766,12 +766,24 @@ | |
| 766 | int blob_write_to_file(Blob *pBlob, const char *zFilename){ |
| 767 | FILE *out; |
| 768 | int wrote; |
| 769 | |
| 770 | if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ |
| 771 | int n; |
| 772 | #if defined(_WIN32) |
| 773 | if( _isatty(fileno(stdout)) ){ |
| 774 | char *z; |
| 775 | z = fossil_utf8_to_console(blob_str(pBlob)); |
| 776 | n = strlen(z); |
| 777 | fwrite(z, 1, n, stdout); |
| 778 | free(z); |
| 779 | return n; |
| 780 | } |
| 781 | #endif |
| 782 | n = blob_size(pBlob); |
| 783 | fwrite(blob_buffer(pBlob), 1, n, stdout); |
| 784 | return n; |
| 785 | }else{ |
| 786 | int i, nName; |
| 787 | char *zName, zBuf[1000]; |
| 788 | |
| 789 | nName = strlen(zFilename); |
| 790 |
+14
-2
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -766,12 +766,24 @@ | ||
| 766 | 766 | int blob_write_to_file(Blob *pBlob, const char *zFilename){ |
| 767 | 767 | FILE *out; |
| 768 | 768 | int wrote; |
| 769 | 769 | |
| 770 | 770 | if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ |
| 771 | - fossil_puts(blob_str(pBlob), 0); | |
| 772 | - return blob_size(pBlob); | |
| 771 | + int n; | |
| 772 | +#if defined(_WIN32) | |
| 773 | + if( _isatty(fileno(stdout)) ){ | |
| 774 | + char *z; | |
| 775 | + z = fossil_utf8_to_console(blob_str(pBlob)); | |
| 776 | + n = strlen(z); | |
| 777 | + fwrite(z, 1, n, stdout); | |
| 778 | + free(z); | |
| 779 | + return n; | |
| 780 | + } | |
| 781 | +#endif | |
| 782 | + n = blob_size(pBlob); | |
| 783 | + fwrite(blob_buffer(pBlob), 1, n, stdout); | |
| 784 | + return n; | |
| 773 | 785 | }else{ |
| 774 | 786 | int i, nName; |
| 775 | 787 | char *zName, zBuf[1000]; |
| 776 | 788 | |
| 777 | 789 | nName = strlen(zFilename); |
| 778 | 790 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -766,12 +766,24 @@ | |
| 766 | int blob_write_to_file(Blob *pBlob, const char *zFilename){ |
| 767 | FILE *out; |
| 768 | int wrote; |
| 769 | |
| 770 | if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ |
| 771 | fossil_puts(blob_str(pBlob), 0); |
| 772 | return blob_size(pBlob); |
| 773 | }else{ |
| 774 | int i, nName; |
| 775 | char *zName, zBuf[1000]; |
| 776 | |
| 777 | nName = strlen(zFilename); |
| 778 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -766,12 +766,24 @@ | |
| 766 | int blob_write_to_file(Blob *pBlob, const char *zFilename){ |
| 767 | FILE *out; |
| 768 | int wrote; |
| 769 | |
| 770 | if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ |
| 771 | int n; |
| 772 | #if defined(_WIN32) |
| 773 | if( _isatty(fileno(stdout)) ){ |
| 774 | char *z; |
| 775 | z = fossil_utf8_to_console(blob_str(pBlob)); |
| 776 | n = strlen(z); |
| 777 | fwrite(z, 1, n, stdout); |
| 778 | free(z); |
| 779 | return n; |
| 780 | } |
| 781 | #endif |
| 782 | n = blob_size(pBlob); |
| 783 | fwrite(blob_buffer(pBlob), 1, n, stdout); |
| 784 | return n; |
| 785 | }else{ |
| 786 | int i, nName; |
| 787 | char *zName, zBuf[1000]; |
| 788 | |
| 789 | nName = strlen(zFilename); |
| 790 |
+1
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -249,10 +249,11 @@ | ||
| 249 | 249 | /* Generate a multi-column table listing the contents of zD[] |
| 250 | 250 | ** directory. |
| 251 | 251 | */ |
| 252 | 252 | mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); |
| 253 | 253 | cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/"); |
| 254 | + if( mxLen<12 ) mxLen = 12; | |
| 254 | 255 | nCol = 100/mxLen; |
| 255 | 256 | if( nCol<1 ) nCol = 1; |
| 256 | 257 | if( nCol>5 ) nCol = 5; |
| 257 | 258 | nRow = (cnt+nCol-1)/nCol; |
| 258 | 259 | db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/"); |
| 259 | 260 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -249,10 +249,11 @@ | |
| 249 | /* Generate a multi-column table listing the contents of zD[] |
| 250 | ** directory. |
| 251 | */ |
| 252 | mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); |
| 253 | cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/"); |
| 254 | nCol = 100/mxLen; |
| 255 | if( nCol<1 ) nCol = 1; |
| 256 | if( nCol>5 ) nCol = 5; |
| 257 | nRow = (cnt+nCol-1)/nCol; |
| 258 | db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/"); |
| 259 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -249,10 +249,11 @@ | |
| 249 | /* Generate a multi-column table listing the contents of zD[] |
| 250 | ** directory. |
| 251 | */ |
| 252 | mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); |
| 253 | cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/"); |
| 254 | if( mxLen<12 ) mxLen = 12; |
| 255 | nCol = 100/mxLen; |
| 256 | if( nCol<1 ) nCol = 1; |
| 257 | if( nCol>5 ) nCol = 5; |
| 258 | nRow = (cnt+nCol-1)/nCol; |
| 259 | db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/"); |
| 260 |
+2
-2
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -182,15 +182,15 @@ | ||
| 182 | 182 | int vid; |
| 183 | 183 | db_must_be_within_tree(); |
| 184 | 184 | /* 012345678901234 */ |
| 185 | 185 | fossil_print("repository: %s\n", db_lget("repository","")); |
| 186 | 186 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| 187 | - fossil_print("server-code: %s\n", db_get("server-code", "")); | |
| 188 | 187 | vid = db_lget_int("checkout", 0); |
| 189 | 188 | if( vid ){ |
| 190 | 189 | show_common_info(vid, "checkout:", 1, 1); |
| 191 | 190 | } |
| 191 | + db_record_repository_filename(0); | |
| 192 | 192 | changes_cmd(); |
| 193 | 193 | } |
| 194 | 194 | |
| 195 | 195 | /* |
| 196 | 196 | ** COMMAND: ls |
| @@ -825,11 +825,11 @@ | ||
| 825 | 825 | blob_reset(&fname); |
| 826 | 826 | } |
| 827 | 827 | } |
| 828 | 828 | |
| 829 | 829 | /* |
| 830 | -** COMMAND: ci | |
| 830 | +** COMMAND: ci* | |
| 831 | 831 | ** COMMAND: commit |
| 832 | 832 | ** |
| 833 | 833 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 834 | 834 | ** |
| 835 | 835 | ** Create a new version containing all of the changes in the current |
| 836 | 836 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -182,15 +182,15 @@ | |
| 182 | int vid; |
| 183 | db_must_be_within_tree(); |
| 184 | /* 012345678901234 */ |
| 185 | fossil_print("repository: %s\n", db_lget("repository","")); |
| 186 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| 187 | fossil_print("server-code: %s\n", db_get("server-code", "")); |
| 188 | vid = db_lget_int("checkout", 0); |
| 189 | if( vid ){ |
| 190 | show_common_info(vid, "checkout:", 1, 1); |
| 191 | } |
| 192 | changes_cmd(); |
| 193 | } |
| 194 | |
| 195 | /* |
| 196 | ** COMMAND: ls |
| @@ -825,11 +825,11 @@ | |
| 825 | blob_reset(&fname); |
| 826 | } |
| 827 | } |
| 828 | |
| 829 | /* |
| 830 | ** COMMAND: ci |
| 831 | ** COMMAND: commit |
| 832 | ** |
| 833 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 834 | ** |
| 835 | ** Create a new version containing all of the changes in the current |
| 836 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -182,15 +182,15 @@ | |
| 182 | int vid; |
| 183 | db_must_be_within_tree(); |
| 184 | /* 012345678901234 */ |
| 185 | fossil_print("repository: %s\n", db_lget("repository","")); |
| 186 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| 187 | vid = db_lget_int("checkout", 0); |
| 188 | if( vid ){ |
| 189 | show_common_info(vid, "checkout:", 1, 1); |
| 190 | } |
| 191 | db_record_repository_filename(0); |
| 192 | changes_cmd(); |
| 193 | } |
| 194 | |
| 195 | /* |
| 196 | ** COMMAND: ls |
| @@ -825,11 +825,11 @@ | |
| 825 | blob_reset(&fname); |
| 826 | } |
| 827 | } |
| 828 | |
| 829 | /* |
| 830 | ** COMMAND: ci* |
| 831 | ** COMMAND: commit |
| 832 | ** |
| 833 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 834 | ** |
| 835 | ** Create a new version containing all of the changes in the current |
| 836 |
+3
-3
| --- src/checkout.c | ||
| +++ src/checkout.c | ||
| @@ -161,12 +161,12 @@ | ||
| 161 | 161 | } |
| 162 | 162 | |
| 163 | 163 | } |
| 164 | 164 | |
| 165 | 165 | /* |
| 166 | -** COMMAND: checkout | |
| 167 | -** COMMAND: co | |
| 166 | +** COMMAND: checkout* | |
| 167 | +** COMMAND: co* | |
| 168 | 168 | ** |
| 169 | 169 | ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS? |
| 170 | 170 | ** or: %fossil co ?VERSION | --latest? ?OPTIONS? |
| 171 | 171 | ** |
| 172 | 172 | ** Check out a version specified on the command-line. This command |
| @@ -272,11 +272,11 @@ | ||
| 272 | 272 | } |
| 273 | 273 | } |
| 274 | 274 | } |
| 275 | 275 | |
| 276 | 276 | /* |
| 277 | -** COMMAND: close | |
| 277 | +** COMMAND: close* | |
| 278 | 278 | ** |
| 279 | 279 | ** Usage: %fossil close ?OPTIONS? |
| 280 | 280 | ** |
| 281 | 281 | ** The opposite of "open". Close the current database connection. |
| 282 | 282 | ** Require a -f or --force flag if there are unsaved changed in the |
| 283 | 283 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -161,12 +161,12 @@ | |
| 161 | } |
| 162 | |
| 163 | } |
| 164 | |
| 165 | /* |
| 166 | ** COMMAND: checkout |
| 167 | ** COMMAND: co |
| 168 | ** |
| 169 | ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS? |
| 170 | ** or: %fossil co ?VERSION | --latest? ?OPTIONS? |
| 171 | ** |
| 172 | ** Check out a version specified on the command-line. This command |
| @@ -272,11 +272,11 @@ | |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | /* |
| 277 | ** COMMAND: close |
| 278 | ** |
| 279 | ** Usage: %fossil close ?OPTIONS? |
| 280 | ** |
| 281 | ** The opposite of "open". Close the current database connection. |
| 282 | ** Require a -f or --force flag if there are unsaved changed in the |
| 283 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -161,12 +161,12 @@ | |
| 161 | } |
| 162 | |
| 163 | } |
| 164 | |
| 165 | /* |
| 166 | ** COMMAND: checkout* |
| 167 | ** COMMAND: co* |
| 168 | ** |
| 169 | ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS? |
| 170 | ** or: %fossil co ?VERSION | --latest? ?OPTIONS? |
| 171 | ** |
| 172 | ** Check out a version specified on the command-line. This command |
| @@ -272,11 +272,11 @@ | |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | /* |
| 277 | ** COMMAND: close* |
| 278 | ** |
| 279 | ** Usage: %fossil close ?OPTIONS? |
| 280 | ** |
| 281 | ** The opposite of "open". Close the current database connection. |
| 282 | ** Require a -f or --force flag if there are unsaved changed in the |
| 283 |
+1
-1
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -724,11 +724,11 @@ | ||
| 724 | 724 | blob_reset(&out); |
| 725 | 725 | } |
| 726 | 726 | |
| 727 | 727 | |
| 728 | 728 | /* |
| 729 | -** COMMAND: configuration | |
| 729 | +** COMMAND: configuration* | |
| 730 | 730 | ** |
| 731 | 731 | ** Usage: %fossil configuration METHOD ... ?OPTIONS? |
| 732 | 732 | ** |
| 733 | 733 | ** Where METHOD is one of: export import merge pull push reset. All methods |
| 734 | 734 | ** accept the -R or --repository option to specific a repository. |
| 735 | 735 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -724,11 +724,11 @@ | |
| 724 | blob_reset(&out); |
| 725 | } |
| 726 | |
| 727 | |
| 728 | /* |
| 729 | ** COMMAND: configuration |
| 730 | ** |
| 731 | ** Usage: %fossil configuration METHOD ... ?OPTIONS? |
| 732 | ** |
| 733 | ** Where METHOD is one of: export import merge pull push reset. All methods |
| 734 | ** accept the -R or --repository option to specific a repository. |
| 735 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -724,11 +724,11 @@ | |
| 724 | blob_reset(&out); |
| 725 | } |
| 726 | |
| 727 | |
| 728 | /* |
| 729 | ** COMMAND: configuration* |
| 730 | ** |
| 731 | ** Usage: %fossil configuration METHOD ... ?OPTIONS? |
| 732 | ** |
| 733 | ** Where METHOD is one of: export import merge pull push reset. All methods |
| 734 | ** accept the -R or --repository option to specific a repository. |
| 735 |
+1
-1
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -303,11 +303,11 @@ | ||
| 303 | 303 | } |
| 304 | 304 | return rc; |
| 305 | 305 | } |
| 306 | 306 | |
| 307 | 307 | /* |
| 308 | -** COMMAND: artifact | |
| 308 | +** COMMAND: artifact* | |
| 309 | 309 | ** |
| 310 | 310 | ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS? |
| 311 | 311 | ** |
| 312 | 312 | ** Extract an artifact by its SHA1 hash and write the results on |
| 313 | 313 | ** standard output, or if the optional 4th argument is given, in |
| 314 | 314 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -303,11 +303,11 @@ | |
| 303 | } |
| 304 | return rc; |
| 305 | } |
| 306 | |
| 307 | /* |
| 308 | ** COMMAND: artifact |
| 309 | ** |
| 310 | ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS? |
| 311 | ** |
| 312 | ** Extract an artifact by its SHA1 hash and write the results on |
| 313 | ** standard output, or if the optional 4th argument is given, in |
| 314 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -303,11 +303,11 @@ | |
| 303 | } |
| 304 | return rc; |
| 305 | } |
| 306 | |
| 307 | /* |
| 308 | ** COMMAND: artifact* |
| 309 | ** |
| 310 | ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS? |
| 311 | ** |
| 312 | ** Extract an artifact by its SHA1 hash and write the results on |
| 313 | ** standard output, or if the optional 4th argument is given, in |
| 314 |
M
src/db.c
+16
-2
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1186,11 +1186,11 @@ | ||
| 1186 | 1186 | manifest_crosslink(rid, &manifest); |
| 1187 | 1187 | } |
| 1188 | 1188 | } |
| 1189 | 1189 | |
| 1190 | 1190 | /* |
| 1191 | -** COMMAND: new | |
| 1191 | +** COMMAND: new* | |
| 1192 | 1192 | ** COMMAND: init |
| 1193 | 1193 | ** |
| 1194 | 1194 | ** Usage: %fossil new ?OPTIONS? FILENAME |
| 1195 | 1195 | ** Or: %fossil init ?OPTIONS? FILENAME |
| 1196 | 1196 | ** |
| @@ -1643,10 +1643,17 @@ | ||
| 1643 | 1643 | ** of the following form: |
| 1644 | 1644 | ** |
| 1645 | 1645 | ** repo:%s |
| 1646 | 1646 | ** |
| 1647 | 1647 | ** The value field is set to 1. |
| 1648 | +** | |
| 1649 | +** If running from a local checkout, also record the root of the checkout | |
| 1650 | +** as follows: | |
| 1651 | +** | |
| 1652 | +** ckout:%s | |
| 1653 | +** | |
| 1654 | +** Where %s is the checkout root. The value is the repository file. | |
| 1648 | 1655 | */ |
| 1649 | 1656 | void db_record_repository_filename(const char *zName){ |
| 1650 | 1657 | Blob full; |
| 1651 | 1658 | if( zName==0 ){ |
| 1652 | 1659 | if( !g.localOpen ) return; |
| @@ -1657,10 +1664,17 @@ | ||
| 1657 | 1664 | db_multi_exec( |
| 1658 | 1665 | "INSERT OR IGNORE INTO global_config(name,value)" |
| 1659 | 1666 | "VALUES('repo:%q',1)", |
| 1660 | 1667 | blob_str(&full) |
| 1661 | 1668 | ); |
| 1669 | + if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){ | |
| 1670 | + db_multi_exec( | |
| 1671 | + "REPLACE INTO global_config(name, value)" | |
| 1672 | + "VALUES('ckout:%q','%q');", | |
| 1673 | + g.zLocalRoot, blob_str(&full) | |
| 1674 | + ); | |
| 1675 | + } | |
| 1662 | 1676 | db_swap_connections(); |
| 1663 | 1677 | blob_reset(&full); |
| 1664 | 1678 | } |
| 1665 | 1679 | |
| 1666 | 1680 | /* |
| @@ -1821,11 +1835,11 @@ | ||
| 1821 | 1835 | { 0,0,0,0,0 } |
| 1822 | 1836 | }; |
| 1823 | 1837 | |
| 1824 | 1838 | /* |
| 1825 | 1839 | ** COMMAND: settings |
| 1826 | -** COMMAND: unset | |
| 1840 | +** COMMAND: unset* | |
| 1827 | 1841 | ** |
| 1828 | 1842 | ** %fossil settings ?PROPERTY? ?VALUE? ?-global? |
| 1829 | 1843 | ** %fossil unset PROPERTY ?-global? |
| 1830 | 1844 | ** |
| 1831 | 1845 | ** The "settings" command with no arguments lists all properties and their |
| 1832 | 1846 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1186,11 +1186,11 @@ | |
| 1186 | manifest_crosslink(rid, &manifest); |
| 1187 | } |
| 1188 | } |
| 1189 | |
| 1190 | /* |
| 1191 | ** COMMAND: new |
| 1192 | ** COMMAND: init |
| 1193 | ** |
| 1194 | ** Usage: %fossil new ?OPTIONS? FILENAME |
| 1195 | ** Or: %fossil init ?OPTIONS? FILENAME |
| 1196 | ** |
| @@ -1643,10 +1643,17 @@ | |
| 1643 | ** of the following form: |
| 1644 | ** |
| 1645 | ** repo:%s |
| 1646 | ** |
| 1647 | ** The value field is set to 1. |
| 1648 | */ |
| 1649 | void db_record_repository_filename(const char *zName){ |
| 1650 | Blob full; |
| 1651 | if( zName==0 ){ |
| 1652 | if( !g.localOpen ) return; |
| @@ -1657,10 +1664,17 @@ | |
| 1657 | db_multi_exec( |
| 1658 | "INSERT OR IGNORE INTO global_config(name,value)" |
| 1659 | "VALUES('repo:%q',1)", |
| 1660 | blob_str(&full) |
| 1661 | ); |
| 1662 | db_swap_connections(); |
| 1663 | blob_reset(&full); |
| 1664 | } |
| 1665 | |
| 1666 | /* |
| @@ -1821,11 +1835,11 @@ | |
| 1821 | { 0,0,0,0,0 } |
| 1822 | }; |
| 1823 | |
| 1824 | /* |
| 1825 | ** COMMAND: settings |
| 1826 | ** COMMAND: unset |
| 1827 | ** |
| 1828 | ** %fossil settings ?PROPERTY? ?VALUE? ?-global? |
| 1829 | ** %fossil unset PROPERTY ?-global? |
| 1830 | ** |
| 1831 | ** The "settings" command with no arguments lists all properties and their |
| 1832 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1186,11 +1186,11 @@ | |
| 1186 | manifest_crosslink(rid, &manifest); |
| 1187 | } |
| 1188 | } |
| 1189 | |
| 1190 | /* |
| 1191 | ** COMMAND: new* |
| 1192 | ** COMMAND: init |
| 1193 | ** |
| 1194 | ** Usage: %fossil new ?OPTIONS? FILENAME |
| 1195 | ** Or: %fossil init ?OPTIONS? FILENAME |
| 1196 | ** |
| @@ -1643,10 +1643,17 @@ | |
| 1643 | ** of the following form: |
| 1644 | ** |
| 1645 | ** repo:%s |
| 1646 | ** |
| 1647 | ** The value field is set to 1. |
| 1648 | ** |
| 1649 | ** If running from a local checkout, also record the root of the checkout |
| 1650 | ** as follows: |
| 1651 | ** |
| 1652 | ** ckout:%s |
| 1653 | ** |
| 1654 | ** Where %s is the checkout root. The value is the repository file. |
| 1655 | */ |
| 1656 | void db_record_repository_filename(const char *zName){ |
| 1657 | Blob full; |
| 1658 | if( zName==0 ){ |
| 1659 | if( !g.localOpen ) return; |
| @@ -1657,10 +1664,17 @@ | |
| 1664 | db_multi_exec( |
| 1665 | "INSERT OR IGNORE INTO global_config(name,value)" |
| 1666 | "VALUES('repo:%q',1)", |
| 1667 | blob_str(&full) |
| 1668 | ); |
| 1669 | if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){ |
| 1670 | db_multi_exec( |
| 1671 | "REPLACE INTO global_config(name, value)" |
| 1672 | "VALUES('ckout:%q','%q');", |
| 1673 | g.zLocalRoot, blob_str(&full) |
| 1674 | ); |
| 1675 | } |
| 1676 | db_swap_connections(); |
| 1677 | blob_reset(&full); |
| 1678 | } |
| 1679 | |
| 1680 | /* |
| @@ -1821,11 +1835,11 @@ | |
| 1835 | { 0,0,0,0,0 } |
| 1836 | }; |
| 1837 | |
| 1838 | /* |
| 1839 | ** COMMAND: settings |
| 1840 | ** COMMAND: unset* |
| 1841 | ** |
| 1842 | ** %fossil settings ?PROPERTY? ?VALUE? ?-global? |
| 1843 | ** %fossil unset PROPERTY ?-global? |
| 1844 | ** |
| 1845 | ** The "settings" command with no arguments lists all properties and their |
| 1846 |
M
src/db.c
+16
-2
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1186,11 +1186,11 @@ | ||
| 1186 | 1186 | manifest_crosslink(rid, &manifest); |
| 1187 | 1187 | } |
| 1188 | 1188 | } |
| 1189 | 1189 | |
| 1190 | 1190 | /* |
| 1191 | -** COMMAND: new | |
| 1191 | +** COMMAND: new* | |
| 1192 | 1192 | ** COMMAND: init |
| 1193 | 1193 | ** |
| 1194 | 1194 | ** Usage: %fossil new ?OPTIONS? FILENAME |
| 1195 | 1195 | ** Or: %fossil init ?OPTIONS? FILENAME |
| 1196 | 1196 | ** |
| @@ -1643,10 +1643,17 @@ | ||
| 1643 | 1643 | ** of the following form: |
| 1644 | 1644 | ** |
| 1645 | 1645 | ** repo:%s |
| 1646 | 1646 | ** |
| 1647 | 1647 | ** The value field is set to 1. |
| 1648 | +** | |
| 1649 | +** If running from a local checkout, also record the root of the checkout | |
| 1650 | +** as follows: | |
| 1651 | +** | |
| 1652 | +** ckout:%s | |
| 1653 | +** | |
| 1654 | +** Where %s is the checkout root. The value is the repository file. | |
| 1648 | 1655 | */ |
| 1649 | 1656 | void db_record_repository_filename(const char *zName){ |
| 1650 | 1657 | Blob full; |
| 1651 | 1658 | if( zName==0 ){ |
| 1652 | 1659 | if( !g.localOpen ) return; |
| @@ -1657,10 +1664,17 @@ | ||
| 1657 | 1664 | db_multi_exec( |
| 1658 | 1665 | "INSERT OR IGNORE INTO global_config(name,value)" |
| 1659 | 1666 | "VALUES('repo:%q',1)", |
| 1660 | 1667 | blob_str(&full) |
| 1661 | 1668 | ); |
| 1669 | + if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){ | |
| 1670 | + db_multi_exec( | |
| 1671 | + "REPLACE INTO global_config(name, value)" | |
| 1672 | + "VALUES('ckout:%q','%q');", | |
| 1673 | + g.zLocalRoot, blob_str(&full) | |
| 1674 | + ); | |
| 1675 | + } | |
| 1662 | 1676 | db_swap_connections(); |
| 1663 | 1677 | blob_reset(&full); |
| 1664 | 1678 | } |
| 1665 | 1679 | |
| 1666 | 1680 | /* |
| @@ -1821,11 +1835,11 @@ | ||
| 1821 | 1835 | { 0,0,0,0,0 } |
| 1822 | 1836 | }; |
| 1823 | 1837 | |
| 1824 | 1838 | /* |
| 1825 | 1839 | ** COMMAND: settings |
| 1826 | -** COMMAND: unset | |
| 1840 | +** COMMAND: unset* | |
| 1827 | 1841 | ** |
| 1828 | 1842 | ** %fossil settings ?PROPERTY? ?VALUE? ?-global? |
| 1829 | 1843 | ** %fossil unset PROPERTY ?-global? |
| 1830 | 1844 | ** |
| 1831 | 1845 | ** The "settings" command with no arguments lists all properties and their |
| 1832 | 1846 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1186,11 +1186,11 @@ | |
| 1186 | manifest_crosslink(rid, &manifest); |
| 1187 | } |
| 1188 | } |
| 1189 | |
| 1190 | /* |
| 1191 | ** COMMAND: new |
| 1192 | ** COMMAND: init |
| 1193 | ** |
| 1194 | ** Usage: %fossil new ?OPTIONS? FILENAME |
| 1195 | ** Or: %fossil init ?OPTIONS? FILENAME |
| 1196 | ** |
| @@ -1643,10 +1643,17 @@ | |
| 1643 | ** of the following form: |
| 1644 | ** |
| 1645 | ** repo:%s |
| 1646 | ** |
| 1647 | ** The value field is set to 1. |
| 1648 | */ |
| 1649 | void db_record_repository_filename(const char *zName){ |
| 1650 | Blob full; |
| 1651 | if( zName==0 ){ |
| 1652 | if( !g.localOpen ) return; |
| @@ -1657,10 +1664,17 @@ | |
| 1657 | db_multi_exec( |
| 1658 | "INSERT OR IGNORE INTO global_config(name,value)" |
| 1659 | "VALUES('repo:%q',1)", |
| 1660 | blob_str(&full) |
| 1661 | ); |
| 1662 | db_swap_connections(); |
| 1663 | blob_reset(&full); |
| 1664 | } |
| 1665 | |
| 1666 | /* |
| @@ -1821,11 +1835,11 @@ | |
| 1821 | { 0,0,0,0,0 } |
| 1822 | }; |
| 1823 | |
| 1824 | /* |
| 1825 | ** COMMAND: settings |
| 1826 | ** COMMAND: unset |
| 1827 | ** |
| 1828 | ** %fossil settings ?PROPERTY? ?VALUE? ?-global? |
| 1829 | ** %fossil unset PROPERTY ?-global? |
| 1830 | ** |
| 1831 | ** The "settings" command with no arguments lists all properties and their |
| 1832 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1186,11 +1186,11 @@ | |
| 1186 | manifest_crosslink(rid, &manifest); |
| 1187 | } |
| 1188 | } |
| 1189 | |
| 1190 | /* |
| 1191 | ** COMMAND: new* |
| 1192 | ** COMMAND: init |
| 1193 | ** |
| 1194 | ** Usage: %fossil new ?OPTIONS? FILENAME |
| 1195 | ** Or: %fossil init ?OPTIONS? FILENAME |
| 1196 | ** |
| @@ -1643,10 +1643,17 @@ | |
| 1643 | ** of the following form: |
| 1644 | ** |
| 1645 | ** repo:%s |
| 1646 | ** |
| 1647 | ** The value field is set to 1. |
| 1648 | ** |
| 1649 | ** If running from a local checkout, also record the root of the checkout |
| 1650 | ** as follows: |
| 1651 | ** |
| 1652 | ** ckout:%s |
| 1653 | ** |
| 1654 | ** Where %s is the checkout root. The value is the repository file. |
| 1655 | */ |
| 1656 | void db_record_repository_filename(const char *zName){ |
| 1657 | Blob full; |
| 1658 | if( zName==0 ){ |
| 1659 | if( !g.localOpen ) return; |
| @@ -1657,10 +1664,17 @@ | |
| 1664 | db_multi_exec( |
| 1665 | "INSERT OR IGNORE INTO global_config(name,value)" |
| 1666 | "VALUES('repo:%q',1)", |
| 1667 | blob_str(&full) |
| 1668 | ); |
| 1669 | if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){ |
| 1670 | db_multi_exec( |
| 1671 | "REPLACE INTO global_config(name, value)" |
| 1672 | "VALUES('ckout:%q','%q');", |
| 1673 | g.zLocalRoot, blob_str(&full) |
| 1674 | ); |
| 1675 | } |
| 1676 | db_swap_connections(); |
| 1677 | blob_reset(&full); |
| 1678 | } |
| 1679 | |
| 1680 | /* |
| @@ -1821,11 +1835,11 @@ | |
| 1835 | { 0,0,0,0,0 } |
| 1836 | }; |
| 1837 | |
| 1838 | /* |
| 1839 | ** COMMAND: settings |
| 1840 | ** COMMAND: unset* |
| 1841 | ** |
| 1842 | ** %fossil settings ?PROPERTY? ?VALUE? ?-global? |
| 1843 | ** %fossil unset PROPERTY ?-global? |
| 1844 | ** |
| 1845 | ** The "settings" command with no arguments lists all properties and their |
| 1846 |
+2
-2
| --- src/descendants.c | ||
| +++ src/descendants.c | ||
| @@ -263,11 +263,11 @@ | ||
| 263 | 263 | db_finalize(&ins); |
| 264 | 264 | db_finalize(&q); |
| 265 | 265 | } |
| 266 | 266 | |
| 267 | 267 | /* |
| 268 | -** COMMAND: descendants | |
| 268 | +** COMMAND: descendants* | |
| 269 | 269 | ** |
| 270 | 270 | ** Usage: %fossil descendants ?BASELINE-ID? ?OPTIONS? |
| 271 | 271 | ** |
| 272 | 272 | ** Find all leaf descendants of the baseline specified or if the argument |
| 273 | 273 | ** is omitted, of the baseline currently checked out. |
| @@ -298,11 +298,11 @@ | ||
| 298 | 298 | print_timeline(&q, 20, 0); |
| 299 | 299 | db_finalize(&q); |
| 300 | 300 | } |
| 301 | 301 | |
| 302 | 302 | /* |
| 303 | -** COMMAND: leaves | |
| 303 | +** COMMAND: leaves* | |
| 304 | 304 | ** |
| 305 | 305 | ** Usage: %fossil leaves ?OPTIONS? |
| 306 | 306 | ** |
| 307 | 307 | ** Find leaves of all branches. By default show only open leaves. |
| 308 | 308 | ** The --all flag causes all leaves (closed and open) to be shown. |
| 309 | 309 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -263,11 +263,11 @@ | |
| 263 | db_finalize(&ins); |
| 264 | db_finalize(&q); |
| 265 | } |
| 266 | |
| 267 | /* |
| 268 | ** COMMAND: descendants |
| 269 | ** |
| 270 | ** Usage: %fossil descendants ?BASELINE-ID? ?OPTIONS? |
| 271 | ** |
| 272 | ** Find all leaf descendants of the baseline specified or if the argument |
| 273 | ** is omitted, of the baseline currently checked out. |
| @@ -298,11 +298,11 @@ | |
| 298 | print_timeline(&q, 20, 0); |
| 299 | db_finalize(&q); |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | ** COMMAND: leaves |
| 304 | ** |
| 305 | ** Usage: %fossil leaves ?OPTIONS? |
| 306 | ** |
| 307 | ** Find leaves of all branches. By default show only open leaves. |
| 308 | ** The --all flag causes all leaves (closed and open) to be shown. |
| 309 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -263,11 +263,11 @@ | |
| 263 | db_finalize(&ins); |
| 264 | db_finalize(&q); |
| 265 | } |
| 266 | |
| 267 | /* |
| 268 | ** COMMAND: descendants* |
| 269 | ** |
| 270 | ** Usage: %fossil descendants ?BASELINE-ID? ?OPTIONS? |
| 271 | ** |
| 272 | ** Find all leaf descendants of the baseline specified or if the argument |
| 273 | ** is omitted, of the baseline currently checked out. |
| @@ -298,11 +298,11 @@ | |
| 298 | print_timeline(&q, 20, 0); |
| 299 | db_finalize(&q); |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | ** COMMAND: leaves* |
| 304 | ** |
| 305 | ** Usage: %fossil leaves ?OPTIONS? |
| 306 | ** |
| 307 | ** Find leaves of all branches. By default show only open leaves. |
| 308 | ** The --all flag causes all leaves (closed and open) to be shown. |
| 309 |
+13
-2
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -367,18 +367,27 @@ | ||
| 367 | 367 | login_check_credentials(); |
| 368 | 368 | if( !g.perm.Read ){ login_needed(); return; } |
| 369 | 369 | zName = PD("name", "tip/index.wiki"); |
| 370 | 370 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 371 | 371 | if( zName[i]==0 || i>UUID_SIZE ){ |
| 372 | + zName = "index.html"; | |
| 372 | 373 | goto doc_not_found; |
| 373 | 374 | } |
| 374 | 375 | memcpy(zBaseline, zName, i); |
| 375 | 376 | zBaseline[i] = 0; |
| 376 | 377 | zName += i; |
| 377 | 378 | while( zName[0]=='/' ){ zName++; } |
| 378 | 379 | if( !file_is_simple_pathname(zName) ){ |
| 379 | - goto doc_not_found; | |
| 380 | + int n = strlen(zName); | |
| 381 | + if( n>0 && zName[n-1]=='/' ){ | |
| 382 | + zName = mprintf("%sindex.html", zName); | |
| 383 | + if( !file_is_simple_pathname(zName) ){ | |
| 384 | + goto doc_not_found; | |
| 385 | + } | |
| 386 | + }else{ | |
| 387 | + goto doc_not_found; | |
| 388 | + } | |
| 380 | 389 | } |
| 381 | 390 | if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){ |
| 382 | 391 | sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip"); |
| 383 | 392 | } |
| 384 | 393 | if( fossil_strcmp(zBaseline,"ckout")==0 ){ |
| @@ -408,10 +417,12 @@ | ||
| 408 | 417 | " fname TEXT, -- filename\n" |
| 409 | 418 | " rid INTEGER, -- artifact ID\n" |
| 410 | 419 | " UNIQUE(vid,fname,rid)\n" |
| 411 | 420 | ")" |
| 412 | 421 | ); |
| 422 | + | |
| 423 | + | |
| 413 | 424 | |
| 414 | 425 | /* Check to see if the documentation file artifact ID is contained |
| 415 | 426 | ** in the baseline cache */ |
| 416 | 427 | rid = db_int(0, "SELECT rid FROM vcache" |
| 417 | 428 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| @@ -499,11 +510,11 @@ | ||
| 499 | 510 | |
| 500 | 511 | doc_not_found: |
| 501 | 512 | /* Jump here when unable to locate the document */ |
| 502 | 513 | db_end_transaction(0); |
| 503 | 514 | style_header("Document Not Found"); |
| 504 | - @ <p>No such document: %h(PD("name","tip/index.wiki"))</p> | |
| 515 | + @ <p>No such document: %h(zName)</p> | |
| 505 | 516 | style_footer(); |
| 506 | 517 | return; |
| 507 | 518 | } |
| 508 | 519 | |
| 509 | 520 | /* |
| 510 | 521 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -367,18 +367,27 @@ | |
| 367 | login_check_credentials(); |
| 368 | if( !g.perm.Read ){ login_needed(); return; } |
| 369 | zName = PD("name", "tip/index.wiki"); |
| 370 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 371 | if( zName[i]==0 || i>UUID_SIZE ){ |
| 372 | goto doc_not_found; |
| 373 | } |
| 374 | memcpy(zBaseline, zName, i); |
| 375 | zBaseline[i] = 0; |
| 376 | zName += i; |
| 377 | while( zName[0]=='/' ){ zName++; } |
| 378 | if( !file_is_simple_pathname(zName) ){ |
| 379 | goto doc_not_found; |
| 380 | } |
| 381 | if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){ |
| 382 | sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip"); |
| 383 | } |
| 384 | if( fossil_strcmp(zBaseline,"ckout")==0 ){ |
| @@ -408,10 +417,12 @@ | |
| 408 | " fname TEXT, -- filename\n" |
| 409 | " rid INTEGER, -- artifact ID\n" |
| 410 | " UNIQUE(vid,fname,rid)\n" |
| 411 | ")" |
| 412 | ); |
| 413 | |
| 414 | /* Check to see if the documentation file artifact ID is contained |
| 415 | ** in the baseline cache */ |
| 416 | rid = db_int(0, "SELECT rid FROM vcache" |
| 417 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| @@ -499,11 +510,11 @@ | |
| 499 | |
| 500 | doc_not_found: |
| 501 | /* Jump here when unable to locate the document */ |
| 502 | db_end_transaction(0); |
| 503 | style_header("Document Not Found"); |
| 504 | @ <p>No such document: %h(PD("name","tip/index.wiki"))</p> |
| 505 | style_footer(); |
| 506 | return; |
| 507 | } |
| 508 | |
| 509 | /* |
| 510 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -367,18 +367,27 @@ | |
| 367 | login_check_credentials(); |
| 368 | if( !g.perm.Read ){ login_needed(); return; } |
| 369 | zName = PD("name", "tip/index.wiki"); |
| 370 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 371 | if( zName[i]==0 || i>UUID_SIZE ){ |
| 372 | zName = "index.html"; |
| 373 | goto doc_not_found; |
| 374 | } |
| 375 | memcpy(zBaseline, zName, i); |
| 376 | zBaseline[i] = 0; |
| 377 | zName += i; |
| 378 | while( zName[0]=='/' ){ zName++; } |
| 379 | if( !file_is_simple_pathname(zName) ){ |
| 380 | int n = strlen(zName); |
| 381 | if( n>0 && zName[n-1]=='/' ){ |
| 382 | zName = mprintf("%sindex.html", zName); |
| 383 | if( !file_is_simple_pathname(zName) ){ |
| 384 | goto doc_not_found; |
| 385 | } |
| 386 | }else{ |
| 387 | goto doc_not_found; |
| 388 | } |
| 389 | } |
| 390 | if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){ |
| 391 | sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip"); |
| 392 | } |
| 393 | if( fossil_strcmp(zBaseline,"ckout")==0 ){ |
| @@ -408,10 +417,12 @@ | |
| 417 | " fname TEXT, -- filename\n" |
| 418 | " rid INTEGER, -- artifact ID\n" |
| 419 | " UNIQUE(vid,fname,rid)\n" |
| 420 | ")" |
| 421 | ); |
| 422 | |
| 423 | |
| 424 | |
| 425 | /* Check to see if the documentation file artifact ID is contained |
| 426 | ** in the baseline cache */ |
| 427 | rid = db_int(0, "SELECT rid FROM vcache" |
| 428 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| @@ -499,11 +510,11 @@ | |
| 510 | |
| 511 | doc_not_found: |
| 512 | /* Jump here when unable to locate the document */ |
| 513 | db_end_transaction(0); |
| 514 | style_header("Document Not Found"); |
| 515 | @ <p>No such document: %h(zName)</p> |
| 516 | style_footer(); |
| 517 | return; |
| 518 | } |
| 519 | |
| 520 | /* |
| 521 |
-2
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -148,11 +148,10 @@ | ||
| 148 | 148 | db_open_config(0); |
| 149 | 149 | db_record_repository_filename(g.argv[2]); |
| 150 | 150 | db_open_repository(g.argv[2]); |
| 151 | 151 | fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); |
| 152 | 152 | fossil_print("project-code: %s\n", db_get("project-code", "<none>")); |
| 153 | - fossil_print("server-code: %s\n", db_get("server-code", "<none>")); | |
| 154 | 153 | return; |
| 155 | 154 | } |
| 156 | 155 | db_find_and_open_repository(0,0); |
| 157 | 156 | if( g.argc==2 ){ |
| 158 | 157 | int vid; |
| @@ -167,11 +166,10 @@ | ||
| 167 | 166 | if( g.zHome ){ |
| 168 | 167 | fossil_print("user-home: %s\n", g.zHome); |
| 169 | 168 | } |
| 170 | 169 | #endif |
| 171 | 170 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 172 | - fossil_print("server-code: %s\n", db_get("server-code", "")); | |
| 173 | 171 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 174 | 172 | if( vid ){ |
| 175 | 173 | show_common_info(vid, "checkout:", 1, 1); |
| 176 | 174 | } |
| 177 | 175 | }else{ |
| 178 | 176 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -148,11 +148,10 @@ | |
| 148 | db_open_config(0); |
| 149 | db_record_repository_filename(g.argv[2]); |
| 150 | db_open_repository(g.argv[2]); |
| 151 | fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); |
| 152 | fossil_print("project-code: %s\n", db_get("project-code", "<none>")); |
| 153 | fossil_print("server-code: %s\n", db_get("server-code", "<none>")); |
| 154 | return; |
| 155 | } |
| 156 | db_find_and_open_repository(0,0); |
| 157 | if( g.argc==2 ){ |
| 158 | int vid; |
| @@ -167,11 +166,10 @@ | |
| 167 | if( g.zHome ){ |
| 168 | fossil_print("user-home: %s\n", g.zHome); |
| 169 | } |
| 170 | #endif |
| 171 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 172 | fossil_print("server-code: %s\n", db_get("server-code", "")); |
| 173 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 174 | if( vid ){ |
| 175 | show_common_info(vid, "checkout:", 1, 1); |
| 176 | } |
| 177 | }else{ |
| 178 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -148,11 +148,10 @@ | |
| 148 | db_open_config(0); |
| 149 | db_record_repository_filename(g.argv[2]); |
| 150 | db_open_repository(g.argv[2]); |
| 151 | fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); |
| 152 | fossil_print("project-code: %s\n", db_get("project-code", "<none>")); |
| 153 | return; |
| 154 | } |
| 155 | db_find_and_open_repository(0,0); |
| 156 | if( g.argc==2 ){ |
| 157 | int vid; |
| @@ -167,11 +166,10 @@ | |
| 166 | if( g.zHome ){ |
| 167 | fossil_print("user-home: %s\n", g.zHome); |
| 168 | } |
| 169 | #endif |
| 170 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 171 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 172 | if( vid ){ |
| 173 | show_common_info(vid, "checkout:", 1, 1); |
| 174 | } |
| 175 | }else{ |
| 176 |
+45
-27
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -409,14 +409,17 @@ | ||
| 409 | 409 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 410 | 410 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 411 | 411 | zCmdName = "cgi"; |
| 412 | 412 | g.isHTTP = 1; |
| 413 | 413 | }else if( argc<2 ){ |
| 414 | - fossil_fatal("Usage: %s COMMAND ...\n" | |
| 415 | - "\"%s help\" for a list of available commands\n" | |
| 416 | - "\"%s help COMMAND\" for specific details\n", | |
| 417 | - argv[0], argv[0], argv[0]); | |
| 414 | + fossil_print( | |
| 415 | + "Usage: %s COMMAND ...\n" | |
| 416 | + " or: %s help -- for a list of common commands\n" | |
| 417 | + " or: %s help COMMMAND -- for help with the named command\n" | |
| 418 | + " or: %s commands -- for a list of all commands\n", | |
| 419 | + argv[0], argv[0], argv[0], argv[0]); | |
| 420 | + fossil_exit(1); | |
| 418 | 421 | }else{ |
| 419 | 422 | g.isHTTP = 0; |
| 420 | 423 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 421 | 424 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 422 | 425 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| @@ -453,14 +456,15 @@ | ||
| 453 | 456 | for(i=0; i<count(aCommand); i++){ |
| 454 | 457 | if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){ |
| 455 | 458 | blob_appendf(&couldbe, " %s", aCommand[i].zName); |
| 456 | 459 | } |
| 457 | 460 | } |
| 458 | - fossil_fatal("%s: ambiguous command prefix: %s\n" | |
| 461 | + fossil_print("%s: ambiguous command prefix: %s\n" | |
| 459 | 462 | "%s: could be any of:%s\n" |
| 460 | 463 | "%s: use \"help\" for more information\n", |
| 461 | 464 | argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]); |
| 465 | + fossil_exit(1); | |
| 462 | 466 | } |
| 463 | 467 | if(rc){ |
| 464 | 468 | fossil_fatal("%s: unrecoverable error while initializing JSON CGI bits: " |
| 465 | 469 | "cson error code #%d (%s)\n", |
| 466 | 470 | argv[0], rc, cson_rc_string(rc)); |
| @@ -810,38 +814,45 @@ | ||
| 810 | 814 | } |
| 811 | 815 | |
| 812 | 816 | /* |
| 813 | 817 | ** List of commands starting with zPrefix, or all commands if zPrefix is NULL. |
| 814 | 818 | */ |
| 815 | -static void cmd_cmd_list(const char *zPrefix){ | |
| 819 | +static void command_list(const char *zPrefix, int cmdMask){ | |
| 816 | 820 | int i, nCmd; |
| 817 | 821 | int nPrefix = zPrefix ? strlen(zPrefix) : 0; |
| 818 | 822 | const char *aCmd[count(aCommand)]; |
| 819 | 823 | for(i=nCmd=0; i<count(aCommand); i++){ |
| 820 | 824 | const char *z = aCommand[i].zName; |
| 821 | - if( memcmp(z,"test",4)==0 ) continue; | |
| 825 | + if( (aCommand[i].cmdFlags & cmdMask)==0 ) continue; | |
| 822 | 826 | if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue; |
| 823 | 827 | aCmd[nCmd++] = aCommand[i].zName; |
| 824 | 828 | } |
| 825 | 829 | multi_column_list(aCmd, nCmd); |
| 826 | 830 | } |
| 827 | 831 | |
| 828 | 832 | /* |
| 829 | -** COMMAND: test-commands | |
| 833 | +** COMMAND: commands | |
| 830 | 834 | ** |
| 831 | -** Usage: %fossil test-commands | |
| 835 | +** Usage: %fossil commands ?--test? ?--all? ?--aux? | |
| 832 | 836 | ** |
| 833 | -** List all commands used for testing and debugging. | |
| 837 | +** List available commands. If the --test option is used list only unsupported | |
| 838 | +** commands intended for testing and debugging. With --all, show all commands | |
| 839 | +** including test debugging commands. With --aux, show only the auxiliary | |
| 840 | +** and less often used commands, and/or command aliases. | |
| 834 | 841 | */ |
| 835 | -void cmd_test_cmd_list(void){ | |
| 836 | - int i, nCmd; | |
| 837 | - const char *aCmd[count(aCommand)]; | |
| 838 | - for(i=nCmd=0; i<count(aCommand); i++){ | |
| 839 | - if( strncmp(aCommand[i].zName,"test",4)!=0 ) continue; | |
| 840 | - aCmd[nCmd++] = aCommand[i].zName; | |
| 841 | - } | |
| 842 | - multi_column_list(aCmd, nCmd); | |
| 842 | +void cmd_command_list(void){ | |
| 843 | + int cmdFlags = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER; | |
| 844 | + if( find_option("all",0,0)!=0 ){ | |
| 845 | + cmdFlags |= CMDFLAG_TEST; | |
| 846 | + }else if( find_option("test",0,0)!=0 ){ | |
| 847 | + cmdFlags = CMDFLAG_TEST; | |
| 848 | + }else if( find_option("aux",0,0)!=0 ){ | |
| 849 | + cmdFlags = CMDFLAG_2ND_TIER; | |
| 850 | + }else{ | |
| 851 | + fossil_print("Use --test or --all to see test and debug commands.\n"); | |
| 852 | + } | |
| 853 | + command_list(0, cmdFlags); | |
| 843 | 854 | } |
| 844 | 855 | |
| 845 | 856 | |
| 846 | 857 | /* |
| 847 | 858 | ** COMMAND: test-list-webpage |
| @@ -873,32 +884,39 @@ | ||
| 873 | 884 | |
| 874 | 885 | /* |
| 875 | 886 | ** COMMAND: help |
| 876 | 887 | ** |
| 877 | 888 | ** Usage: %fossil help COMMAND |
| 889 | +** or: %fossil COMMAND -help | |
| 890 | +** | |
| 891 | +** Display information on how to use COMMAND. To display a list of | |
| 892 | +** available commands use: | |
| 878 | 893 | ** |
| 879 | -** Display information on how to use COMMAND | |
| 894 | +** %fossil commands | |
| 880 | 895 | */ |
| 881 | 896 | void help_cmd(void){ |
| 882 | 897 | int rc, idx; |
| 883 | 898 | const char *z; |
| 884 | 899 | if( g.argc<3 ){ |
| 885 | - fossil_print("Usage: %s help COMMAND.\nAvailable COMMANDs:\n", | |
| 886 | - fossil_nameofexe()); | |
| 887 | - cmd_cmd_list(0); | |
| 900 | + z = fossil_nameofexe(); | |
| 901 | + fossil_print( | |
| 902 | + "Usage: %s help COMMAND\n" | |
| 903 | + "Common COMMANDs: (use \"%s commands\" for a complete list)\n", | |
| 904 | + z, z); | |
| 905 | + command_list(0, CMDFLAG_1ST_TIER); | |
| 888 | 906 | version_cmd(); |
| 889 | 907 | return; |
| 890 | 908 | } |
| 891 | 909 | rc = name_search(g.argv[2], aCommand, count(aCommand), &idx); |
| 892 | 910 | if( rc==1 ){ |
| 893 | 911 | fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]); |
| 894 | - cmd_cmd_list(0); | |
| 912 | + command_list(0, 0xff); | |
| 895 | 913 | fossil_exit(1); |
| 896 | 914 | }else if( rc==2 ){ |
| 897 | 915 | fossil_print("ambiguous command prefix: %s\nMatching commands:\n", |
| 898 | 916 | g.argv[2]); |
| 899 | - cmd_cmd_list(g.argv[2]); | |
| 917 | + command_list(g.argv[2], 0xff); | |
| 900 | 918 | fossil_exit(1); |
| 901 | 919 | } |
| 902 | 920 | z = aCmdHelp[idx]; |
| 903 | 921 | if( z==0 ){ |
| 904 | 922 | fossil_fatal("no help available for the %s command", |
| @@ -1285,11 +1303,11 @@ | ||
| 1285 | 1303 | */ |
| 1286 | 1304 | cgi_reply(); |
| 1287 | 1305 | } |
| 1288 | 1306 | |
| 1289 | 1307 | /* |
| 1290 | -** COMMAND: cgi | |
| 1308 | +** COMMAND: cgi* | |
| 1291 | 1309 | ** |
| 1292 | 1310 | ** Usage: %fossil ?cgi? SCRIPT |
| 1293 | 1311 | ** |
| 1294 | 1312 | ** The SCRIPT argument is the name of a file that is the CGI script |
| 1295 | 1313 | ** that is being run. The command name, "cgi", may be omitted if |
| @@ -1472,11 +1490,11 @@ | ||
| 1472 | 1490 | ** |
| 1473 | 1491 | ** fossil http REPOSITORY INFILE OUTFILE IPADDR |
| 1474 | 1492 | ** |
| 1475 | 1493 | ** The argv==6 form is used by the win32 server only. |
| 1476 | 1494 | ** |
| 1477 | -** COMMAND: http | |
| 1495 | +** COMMAND: http* | |
| 1478 | 1496 | ** |
| 1479 | 1497 | ** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https] |
| 1480 | 1498 | ** |
| 1481 | 1499 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 1482 | 1500 | ** is delivered on stdout. This method is used to launch an HTTP request |
| @@ -1573,11 +1591,11 @@ | ||
| 1573 | 1591 | } |
| 1574 | 1592 | #endif |
| 1575 | 1593 | #endif |
| 1576 | 1594 | |
| 1577 | 1595 | /* |
| 1578 | -** COMMAND: server | |
| 1596 | +** COMMAND: server* | |
| 1579 | 1597 | ** COMMAND: ui |
| 1580 | 1598 | ** |
| 1581 | 1599 | ** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY? |
| 1582 | 1600 | ** Or: %fossil ui ?-P|--port TCPPORT? ?REPOSITORY? |
| 1583 | 1601 | ** |
| 1584 | 1602 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -409,14 +409,17 @@ | |
| 409 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 410 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 411 | zCmdName = "cgi"; |
| 412 | g.isHTTP = 1; |
| 413 | }else if( argc<2 ){ |
| 414 | fossil_fatal("Usage: %s COMMAND ...\n" |
| 415 | "\"%s help\" for a list of available commands\n" |
| 416 | "\"%s help COMMAND\" for specific details\n", |
| 417 | argv[0], argv[0], argv[0]); |
| 418 | }else{ |
| 419 | g.isHTTP = 0; |
| 420 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 421 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 422 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| @@ -453,14 +456,15 @@ | |
| 453 | for(i=0; i<count(aCommand); i++){ |
| 454 | if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){ |
| 455 | blob_appendf(&couldbe, " %s", aCommand[i].zName); |
| 456 | } |
| 457 | } |
| 458 | fossil_fatal("%s: ambiguous command prefix: %s\n" |
| 459 | "%s: could be any of:%s\n" |
| 460 | "%s: use \"help\" for more information\n", |
| 461 | argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]); |
| 462 | } |
| 463 | if(rc){ |
| 464 | fossil_fatal("%s: unrecoverable error while initializing JSON CGI bits: " |
| 465 | "cson error code #%d (%s)\n", |
| 466 | argv[0], rc, cson_rc_string(rc)); |
| @@ -810,38 +814,45 @@ | |
| 810 | } |
| 811 | |
| 812 | /* |
| 813 | ** List of commands starting with zPrefix, or all commands if zPrefix is NULL. |
| 814 | */ |
| 815 | static void cmd_cmd_list(const char *zPrefix){ |
| 816 | int i, nCmd; |
| 817 | int nPrefix = zPrefix ? strlen(zPrefix) : 0; |
| 818 | const char *aCmd[count(aCommand)]; |
| 819 | for(i=nCmd=0; i<count(aCommand); i++){ |
| 820 | const char *z = aCommand[i].zName; |
| 821 | if( memcmp(z,"test",4)==0 ) continue; |
| 822 | if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue; |
| 823 | aCmd[nCmd++] = aCommand[i].zName; |
| 824 | } |
| 825 | multi_column_list(aCmd, nCmd); |
| 826 | } |
| 827 | |
| 828 | /* |
| 829 | ** COMMAND: test-commands |
| 830 | ** |
| 831 | ** Usage: %fossil test-commands |
| 832 | ** |
| 833 | ** List all commands used for testing and debugging. |
| 834 | */ |
| 835 | void cmd_test_cmd_list(void){ |
| 836 | int i, nCmd; |
| 837 | const char *aCmd[count(aCommand)]; |
| 838 | for(i=nCmd=0; i<count(aCommand); i++){ |
| 839 | if( strncmp(aCommand[i].zName,"test",4)!=0 ) continue; |
| 840 | aCmd[nCmd++] = aCommand[i].zName; |
| 841 | } |
| 842 | multi_column_list(aCmd, nCmd); |
| 843 | } |
| 844 | |
| 845 | |
| 846 | /* |
| 847 | ** COMMAND: test-list-webpage |
| @@ -873,32 +884,39 @@ | |
| 873 | |
| 874 | /* |
| 875 | ** COMMAND: help |
| 876 | ** |
| 877 | ** Usage: %fossil help COMMAND |
| 878 | ** |
| 879 | ** Display information on how to use COMMAND |
| 880 | */ |
| 881 | void help_cmd(void){ |
| 882 | int rc, idx; |
| 883 | const char *z; |
| 884 | if( g.argc<3 ){ |
| 885 | fossil_print("Usage: %s help COMMAND.\nAvailable COMMANDs:\n", |
| 886 | fossil_nameofexe()); |
| 887 | cmd_cmd_list(0); |
| 888 | version_cmd(); |
| 889 | return; |
| 890 | } |
| 891 | rc = name_search(g.argv[2], aCommand, count(aCommand), &idx); |
| 892 | if( rc==1 ){ |
| 893 | fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]); |
| 894 | cmd_cmd_list(0); |
| 895 | fossil_exit(1); |
| 896 | }else if( rc==2 ){ |
| 897 | fossil_print("ambiguous command prefix: %s\nMatching commands:\n", |
| 898 | g.argv[2]); |
| 899 | cmd_cmd_list(g.argv[2]); |
| 900 | fossil_exit(1); |
| 901 | } |
| 902 | z = aCmdHelp[idx]; |
| 903 | if( z==0 ){ |
| 904 | fossil_fatal("no help available for the %s command", |
| @@ -1285,11 +1303,11 @@ | |
| 1285 | */ |
| 1286 | cgi_reply(); |
| 1287 | } |
| 1288 | |
| 1289 | /* |
| 1290 | ** COMMAND: cgi |
| 1291 | ** |
| 1292 | ** Usage: %fossil ?cgi? SCRIPT |
| 1293 | ** |
| 1294 | ** The SCRIPT argument is the name of a file that is the CGI script |
| 1295 | ** that is being run. The command name, "cgi", may be omitted if |
| @@ -1472,11 +1490,11 @@ | |
| 1472 | ** |
| 1473 | ** fossil http REPOSITORY INFILE OUTFILE IPADDR |
| 1474 | ** |
| 1475 | ** The argv==6 form is used by the win32 server only. |
| 1476 | ** |
| 1477 | ** COMMAND: http |
| 1478 | ** |
| 1479 | ** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https] |
| 1480 | ** |
| 1481 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 1482 | ** is delivered on stdout. This method is used to launch an HTTP request |
| @@ -1573,11 +1591,11 @@ | |
| 1573 | } |
| 1574 | #endif |
| 1575 | #endif |
| 1576 | |
| 1577 | /* |
| 1578 | ** COMMAND: server |
| 1579 | ** COMMAND: ui |
| 1580 | ** |
| 1581 | ** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY? |
| 1582 | ** Or: %fossil ui ?-P|--port TCPPORT? ?REPOSITORY? |
| 1583 | ** |
| 1584 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -409,14 +409,17 @@ | |
| 409 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 410 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 411 | zCmdName = "cgi"; |
| 412 | g.isHTTP = 1; |
| 413 | }else if( argc<2 ){ |
| 414 | fossil_print( |
| 415 | "Usage: %s COMMAND ...\n" |
| 416 | " or: %s help -- for a list of common commands\n" |
| 417 | " or: %s help COMMMAND -- for help with the named command\n" |
| 418 | " or: %s commands -- for a list of all commands\n", |
| 419 | argv[0], argv[0], argv[0], argv[0]); |
| 420 | fossil_exit(1); |
| 421 | }else{ |
| 422 | g.isHTTP = 0; |
| 423 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 424 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 425 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| @@ -453,14 +456,15 @@ | |
| 456 | for(i=0; i<count(aCommand); i++){ |
| 457 | if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){ |
| 458 | blob_appendf(&couldbe, " %s", aCommand[i].zName); |
| 459 | } |
| 460 | } |
| 461 | fossil_print("%s: ambiguous command prefix: %s\n" |
| 462 | "%s: could be any of:%s\n" |
| 463 | "%s: use \"help\" for more information\n", |
| 464 | argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]); |
| 465 | fossil_exit(1); |
| 466 | } |
| 467 | if(rc){ |
| 468 | fossil_fatal("%s: unrecoverable error while initializing JSON CGI bits: " |
| 469 | "cson error code #%d (%s)\n", |
| 470 | argv[0], rc, cson_rc_string(rc)); |
| @@ -810,38 +814,45 @@ | |
| 814 | } |
| 815 | |
| 816 | /* |
| 817 | ** List of commands starting with zPrefix, or all commands if zPrefix is NULL. |
| 818 | */ |
| 819 | static void command_list(const char *zPrefix, int cmdMask){ |
| 820 | int i, nCmd; |
| 821 | int nPrefix = zPrefix ? strlen(zPrefix) : 0; |
| 822 | const char *aCmd[count(aCommand)]; |
| 823 | for(i=nCmd=0; i<count(aCommand); i++){ |
| 824 | const char *z = aCommand[i].zName; |
| 825 | if( (aCommand[i].cmdFlags & cmdMask)==0 ) continue; |
| 826 | if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue; |
| 827 | aCmd[nCmd++] = aCommand[i].zName; |
| 828 | } |
| 829 | multi_column_list(aCmd, nCmd); |
| 830 | } |
| 831 | |
| 832 | /* |
| 833 | ** COMMAND: commands |
| 834 | ** |
| 835 | ** Usage: %fossil commands ?--test? ?--all? ?--aux? |
| 836 | ** |
| 837 | ** List available commands. If the --test option is used list only unsupported |
| 838 | ** commands intended for testing and debugging. With --all, show all commands |
| 839 | ** including test debugging commands. With --aux, show only the auxiliary |
| 840 | ** and less often used commands, and/or command aliases. |
| 841 | */ |
| 842 | void cmd_command_list(void){ |
| 843 | int cmdFlags = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER; |
| 844 | if( find_option("all",0,0)!=0 ){ |
| 845 | cmdFlags |= CMDFLAG_TEST; |
| 846 | }else if( find_option("test",0,0)!=0 ){ |
| 847 | cmdFlags = CMDFLAG_TEST; |
| 848 | }else if( find_option("aux",0,0)!=0 ){ |
| 849 | cmdFlags = CMDFLAG_2ND_TIER; |
| 850 | }else{ |
| 851 | fossil_print("Use --test or --all to see test and debug commands.\n"); |
| 852 | } |
| 853 | command_list(0, cmdFlags); |
| 854 | } |
| 855 | |
| 856 | |
| 857 | /* |
| 858 | ** COMMAND: test-list-webpage |
| @@ -873,32 +884,39 @@ | |
| 884 | |
| 885 | /* |
| 886 | ** COMMAND: help |
| 887 | ** |
| 888 | ** Usage: %fossil help COMMAND |
| 889 | ** or: %fossil COMMAND -help |
| 890 | ** |
| 891 | ** Display information on how to use COMMAND. To display a list of |
| 892 | ** available commands use: |
| 893 | ** |
| 894 | ** %fossil commands |
| 895 | */ |
| 896 | void help_cmd(void){ |
| 897 | int rc, idx; |
| 898 | const char *z; |
| 899 | if( g.argc<3 ){ |
| 900 | z = fossil_nameofexe(); |
| 901 | fossil_print( |
| 902 | "Usage: %s help COMMAND\n" |
| 903 | "Common COMMANDs: (use \"%s commands\" for a complete list)\n", |
| 904 | z, z); |
| 905 | command_list(0, CMDFLAG_1ST_TIER); |
| 906 | version_cmd(); |
| 907 | return; |
| 908 | } |
| 909 | rc = name_search(g.argv[2], aCommand, count(aCommand), &idx); |
| 910 | if( rc==1 ){ |
| 911 | fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]); |
| 912 | command_list(0, 0xff); |
| 913 | fossil_exit(1); |
| 914 | }else if( rc==2 ){ |
| 915 | fossil_print("ambiguous command prefix: %s\nMatching commands:\n", |
| 916 | g.argv[2]); |
| 917 | command_list(g.argv[2], 0xff); |
| 918 | fossil_exit(1); |
| 919 | } |
| 920 | z = aCmdHelp[idx]; |
| 921 | if( z==0 ){ |
| 922 | fossil_fatal("no help available for the %s command", |
| @@ -1285,11 +1303,11 @@ | |
| 1303 | */ |
| 1304 | cgi_reply(); |
| 1305 | } |
| 1306 | |
| 1307 | /* |
| 1308 | ** COMMAND: cgi* |
| 1309 | ** |
| 1310 | ** Usage: %fossil ?cgi? SCRIPT |
| 1311 | ** |
| 1312 | ** The SCRIPT argument is the name of a file that is the CGI script |
| 1313 | ** that is being run. The command name, "cgi", may be omitted if |
| @@ -1472,11 +1490,11 @@ | |
| 1490 | ** |
| 1491 | ** fossil http REPOSITORY INFILE OUTFILE IPADDR |
| 1492 | ** |
| 1493 | ** The argv==6 form is used by the win32 server only. |
| 1494 | ** |
| 1495 | ** COMMAND: http* |
| 1496 | ** |
| 1497 | ** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https] |
| 1498 | ** |
| 1499 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 1500 | ** is delivered on stdout. This method is used to launch an HTTP request |
| @@ -1573,11 +1591,11 @@ | |
| 1591 | } |
| 1592 | #endif |
| 1593 | #endif |
| 1594 | |
| 1595 | /* |
| 1596 | ** COMMAND: server* |
| 1597 | ** COMMAND: ui |
| 1598 | ** |
| 1599 | ** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY? |
| 1600 | ** Or: %fossil ui ?-P|--port TCPPORT? ?REPOSITORY? |
| 1601 | ** |
| 1602 |
+45
-27
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -409,14 +409,17 @@ | ||
| 409 | 409 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 410 | 410 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 411 | 411 | zCmdName = "cgi"; |
| 412 | 412 | g.isHTTP = 1; |
| 413 | 413 | }else if( argc<2 ){ |
| 414 | - fossil_fatal("Usage: %s COMMAND ...\n" | |
| 415 | - "\"%s help\" for a list of available commands\n" | |
| 416 | - "\"%s help COMMAND\" for specific details\n", | |
| 417 | - argv[0], argv[0], argv[0]); | |
| 414 | + fossil_print( | |
| 415 | + "Usage: %s COMMAND ...\n" | |
| 416 | + " or: %s help -- for a list of common commands\n" | |
| 417 | + " or: %s help COMMMAND -- for help with the named command\n" | |
| 418 | + " or: %s commands -- for a list of all commands\n", | |
| 419 | + argv[0], argv[0], argv[0], argv[0]); | |
| 420 | + fossil_exit(1); | |
| 418 | 421 | }else{ |
| 419 | 422 | g.isHTTP = 0; |
| 420 | 423 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 421 | 424 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 422 | 425 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| @@ -453,14 +456,15 @@ | ||
| 453 | 456 | for(i=0; i<count(aCommand); i++){ |
| 454 | 457 | if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){ |
| 455 | 458 | blob_appendf(&couldbe, " %s", aCommand[i].zName); |
| 456 | 459 | } |
| 457 | 460 | } |
| 458 | - fossil_fatal("%s: ambiguous command prefix: %s\n" | |
| 461 | + fossil_print("%s: ambiguous command prefix: %s\n" | |
| 459 | 462 | "%s: could be any of:%s\n" |
| 460 | 463 | "%s: use \"help\" for more information\n", |
| 461 | 464 | argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]); |
| 465 | + fossil_exit(1); | |
| 462 | 466 | } |
| 463 | 467 | if(rc){ |
| 464 | 468 | fossil_fatal("%s: unrecoverable error while initializing JSON CGI bits: " |
| 465 | 469 | "cson error code #%d (%s)\n", |
| 466 | 470 | argv[0], rc, cson_rc_string(rc)); |
| @@ -810,38 +814,45 @@ | ||
| 810 | 814 | } |
| 811 | 815 | |
| 812 | 816 | /* |
| 813 | 817 | ** List of commands starting with zPrefix, or all commands if zPrefix is NULL. |
| 814 | 818 | */ |
| 815 | -static void cmd_cmd_list(const char *zPrefix){ | |
| 819 | +static void command_list(const char *zPrefix, int cmdMask){ | |
| 816 | 820 | int i, nCmd; |
| 817 | 821 | int nPrefix = zPrefix ? strlen(zPrefix) : 0; |
| 818 | 822 | const char *aCmd[count(aCommand)]; |
| 819 | 823 | for(i=nCmd=0; i<count(aCommand); i++){ |
| 820 | 824 | const char *z = aCommand[i].zName; |
| 821 | - if( memcmp(z,"test",4)==0 ) continue; | |
| 825 | + if( (aCommand[i].cmdFlags & cmdMask)==0 ) continue; | |
| 822 | 826 | if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue; |
| 823 | 827 | aCmd[nCmd++] = aCommand[i].zName; |
| 824 | 828 | } |
| 825 | 829 | multi_column_list(aCmd, nCmd); |
| 826 | 830 | } |
| 827 | 831 | |
| 828 | 832 | /* |
| 829 | -** COMMAND: test-commands | |
| 833 | +** COMMAND: commands | |
| 830 | 834 | ** |
| 831 | -** Usage: %fossil test-commands | |
| 835 | +** Usage: %fossil commands ?--test? ?--all? ?--aux? | |
| 832 | 836 | ** |
| 833 | -** List all commands used for testing and debugging. | |
| 837 | +** List available commands. If the --test option is used list only unsupported | |
| 838 | +** commands intended for testing and debugging. With --all, show all commands | |
| 839 | +** including test debugging commands. With --aux, show only the auxiliary | |
| 840 | +** and less often used commands, and/or command aliases. | |
| 834 | 841 | */ |
| 835 | -void cmd_test_cmd_list(void){ | |
| 836 | - int i, nCmd; | |
| 837 | - const char *aCmd[count(aCommand)]; | |
| 838 | - for(i=nCmd=0; i<count(aCommand); i++){ | |
| 839 | - if( strncmp(aCommand[i].zName,"test",4)!=0 ) continue; | |
| 840 | - aCmd[nCmd++] = aCommand[i].zName; | |
| 841 | - } | |
| 842 | - multi_column_list(aCmd, nCmd); | |
| 842 | +void cmd_command_list(void){ | |
| 843 | + int cmdFlags = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER; | |
| 844 | + if( find_option("all",0,0)!=0 ){ | |
| 845 | + cmdFlags |= CMDFLAG_TEST; | |
| 846 | + }else if( find_option("test",0,0)!=0 ){ | |
| 847 | + cmdFlags = CMDFLAG_TEST; | |
| 848 | + }else if( find_option("aux",0,0)!=0 ){ | |
| 849 | + cmdFlags = CMDFLAG_2ND_TIER; | |
| 850 | + }else{ | |
| 851 | + fossil_print("Use --test or --all to see test and debug commands.\n"); | |
| 852 | + } | |
| 853 | + command_list(0, cmdFlags); | |
| 843 | 854 | } |
| 844 | 855 | |
| 845 | 856 | |
| 846 | 857 | /* |
| 847 | 858 | ** COMMAND: test-list-webpage |
| @@ -873,32 +884,39 @@ | ||
| 873 | 884 | |
| 874 | 885 | /* |
| 875 | 886 | ** COMMAND: help |
| 876 | 887 | ** |
| 877 | 888 | ** Usage: %fossil help COMMAND |
| 889 | +** or: %fossil COMMAND -help | |
| 890 | +** | |
| 891 | +** Display information on how to use COMMAND. To display a list of | |
| 892 | +** available commands use: | |
| 878 | 893 | ** |
| 879 | -** Display information on how to use COMMAND | |
| 894 | +** %fossil commands | |
| 880 | 895 | */ |
| 881 | 896 | void help_cmd(void){ |
| 882 | 897 | int rc, idx; |
| 883 | 898 | const char *z; |
| 884 | 899 | if( g.argc<3 ){ |
| 885 | - fossil_print("Usage: %s help COMMAND.\nAvailable COMMANDs:\n", | |
| 886 | - fossil_nameofexe()); | |
| 887 | - cmd_cmd_list(0); | |
| 900 | + z = fossil_nameofexe(); | |
| 901 | + fossil_print( | |
| 902 | + "Usage: %s help COMMAND\n" | |
| 903 | + "Common COMMANDs: (use \"%s commands\" for a complete list)\n", | |
| 904 | + z, z); | |
| 905 | + command_list(0, CMDFLAG_1ST_TIER); | |
| 888 | 906 | version_cmd(); |
| 889 | 907 | return; |
| 890 | 908 | } |
| 891 | 909 | rc = name_search(g.argv[2], aCommand, count(aCommand), &idx); |
| 892 | 910 | if( rc==1 ){ |
| 893 | 911 | fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]); |
| 894 | - cmd_cmd_list(0); | |
| 912 | + command_list(0, 0xff); | |
| 895 | 913 | fossil_exit(1); |
| 896 | 914 | }else if( rc==2 ){ |
| 897 | 915 | fossil_print("ambiguous command prefix: %s\nMatching commands:\n", |
| 898 | 916 | g.argv[2]); |
| 899 | - cmd_cmd_list(g.argv[2]); | |
| 917 | + command_list(g.argv[2], 0xff); | |
| 900 | 918 | fossil_exit(1); |
| 901 | 919 | } |
| 902 | 920 | z = aCmdHelp[idx]; |
| 903 | 921 | if( z==0 ){ |
| 904 | 922 | fossil_fatal("no help available for the %s command", |
| @@ -1285,11 +1303,11 @@ | ||
| 1285 | 1303 | */ |
| 1286 | 1304 | cgi_reply(); |
| 1287 | 1305 | } |
| 1288 | 1306 | |
| 1289 | 1307 | /* |
| 1290 | -** COMMAND: cgi | |
| 1308 | +** COMMAND: cgi* | |
| 1291 | 1309 | ** |
| 1292 | 1310 | ** Usage: %fossil ?cgi? SCRIPT |
| 1293 | 1311 | ** |
| 1294 | 1312 | ** The SCRIPT argument is the name of a file that is the CGI script |
| 1295 | 1313 | ** that is being run. The command name, "cgi", may be omitted if |
| @@ -1472,11 +1490,11 @@ | ||
| 1472 | 1490 | ** |
| 1473 | 1491 | ** fossil http REPOSITORY INFILE OUTFILE IPADDR |
| 1474 | 1492 | ** |
| 1475 | 1493 | ** The argv==6 form is used by the win32 server only. |
| 1476 | 1494 | ** |
| 1477 | -** COMMAND: http | |
| 1495 | +** COMMAND: http* | |
| 1478 | 1496 | ** |
| 1479 | 1497 | ** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https] |
| 1480 | 1498 | ** |
| 1481 | 1499 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 1482 | 1500 | ** is delivered on stdout. This method is used to launch an HTTP request |
| @@ -1573,11 +1591,11 @@ | ||
| 1573 | 1591 | } |
| 1574 | 1592 | #endif |
| 1575 | 1593 | #endif |
| 1576 | 1594 | |
| 1577 | 1595 | /* |
| 1578 | -** COMMAND: server | |
| 1596 | +** COMMAND: server* | |
| 1579 | 1597 | ** COMMAND: ui |
| 1580 | 1598 | ** |
| 1581 | 1599 | ** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY? |
| 1582 | 1600 | ** Or: %fossil ui ?-P|--port TCPPORT? ?REPOSITORY? |
| 1583 | 1601 | ** |
| 1584 | 1602 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -409,14 +409,17 @@ | |
| 409 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 410 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 411 | zCmdName = "cgi"; |
| 412 | g.isHTTP = 1; |
| 413 | }else if( argc<2 ){ |
| 414 | fossil_fatal("Usage: %s COMMAND ...\n" |
| 415 | "\"%s help\" for a list of available commands\n" |
| 416 | "\"%s help COMMAND\" for specific details\n", |
| 417 | argv[0], argv[0], argv[0]); |
| 418 | }else{ |
| 419 | g.isHTTP = 0; |
| 420 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 421 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 422 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| @@ -453,14 +456,15 @@ | |
| 453 | for(i=0; i<count(aCommand); i++){ |
| 454 | if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){ |
| 455 | blob_appendf(&couldbe, " %s", aCommand[i].zName); |
| 456 | } |
| 457 | } |
| 458 | fossil_fatal("%s: ambiguous command prefix: %s\n" |
| 459 | "%s: could be any of:%s\n" |
| 460 | "%s: use \"help\" for more information\n", |
| 461 | argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]); |
| 462 | } |
| 463 | if(rc){ |
| 464 | fossil_fatal("%s: unrecoverable error while initializing JSON CGI bits: " |
| 465 | "cson error code #%d (%s)\n", |
| 466 | argv[0], rc, cson_rc_string(rc)); |
| @@ -810,38 +814,45 @@ | |
| 810 | } |
| 811 | |
| 812 | /* |
| 813 | ** List of commands starting with zPrefix, or all commands if zPrefix is NULL. |
| 814 | */ |
| 815 | static void cmd_cmd_list(const char *zPrefix){ |
| 816 | int i, nCmd; |
| 817 | int nPrefix = zPrefix ? strlen(zPrefix) : 0; |
| 818 | const char *aCmd[count(aCommand)]; |
| 819 | for(i=nCmd=0; i<count(aCommand); i++){ |
| 820 | const char *z = aCommand[i].zName; |
| 821 | if( memcmp(z,"test",4)==0 ) continue; |
| 822 | if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue; |
| 823 | aCmd[nCmd++] = aCommand[i].zName; |
| 824 | } |
| 825 | multi_column_list(aCmd, nCmd); |
| 826 | } |
| 827 | |
| 828 | /* |
| 829 | ** COMMAND: test-commands |
| 830 | ** |
| 831 | ** Usage: %fossil test-commands |
| 832 | ** |
| 833 | ** List all commands used for testing and debugging. |
| 834 | */ |
| 835 | void cmd_test_cmd_list(void){ |
| 836 | int i, nCmd; |
| 837 | const char *aCmd[count(aCommand)]; |
| 838 | for(i=nCmd=0; i<count(aCommand); i++){ |
| 839 | if( strncmp(aCommand[i].zName,"test",4)!=0 ) continue; |
| 840 | aCmd[nCmd++] = aCommand[i].zName; |
| 841 | } |
| 842 | multi_column_list(aCmd, nCmd); |
| 843 | } |
| 844 | |
| 845 | |
| 846 | /* |
| 847 | ** COMMAND: test-list-webpage |
| @@ -873,32 +884,39 @@ | |
| 873 | |
| 874 | /* |
| 875 | ** COMMAND: help |
| 876 | ** |
| 877 | ** Usage: %fossil help COMMAND |
| 878 | ** |
| 879 | ** Display information on how to use COMMAND |
| 880 | */ |
| 881 | void help_cmd(void){ |
| 882 | int rc, idx; |
| 883 | const char *z; |
| 884 | if( g.argc<3 ){ |
| 885 | fossil_print("Usage: %s help COMMAND.\nAvailable COMMANDs:\n", |
| 886 | fossil_nameofexe()); |
| 887 | cmd_cmd_list(0); |
| 888 | version_cmd(); |
| 889 | return; |
| 890 | } |
| 891 | rc = name_search(g.argv[2], aCommand, count(aCommand), &idx); |
| 892 | if( rc==1 ){ |
| 893 | fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]); |
| 894 | cmd_cmd_list(0); |
| 895 | fossil_exit(1); |
| 896 | }else if( rc==2 ){ |
| 897 | fossil_print("ambiguous command prefix: %s\nMatching commands:\n", |
| 898 | g.argv[2]); |
| 899 | cmd_cmd_list(g.argv[2]); |
| 900 | fossil_exit(1); |
| 901 | } |
| 902 | z = aCmdHelp[idx]; |
| 903 | if( z==0 ){ |
| 904 | fossil_fatal("no help available for the %s command", |
| @@ -1285,11 +1303,11 @@ | |
| 1285 | */ |
| 1286 | cgi_reply(); |
| 1287 | } |
| 1288 | |
| 1289 | /* |
| 1290 | ** COMMAND: cgi |
| 1291 | ** |
| 1292 | ** Usage: %fossil ?cgi? SCRIPT |
| 1293 | ** |
| 1294 | ** The SCRIPT argument is the name of a file that is the CGI script |
| 1295 | ** that is being run. The command name, "cgi", may be omitted if |
| @@ -1472,11 +1490,11 @@ | |
| 1472 | ** |
| 1473 | ** fossil http REPOSITORY INFILE OUTFILE IPADDR |
| 1474 | ** |
| 1475 | ** The argv==6 form is used by the win32 server only. |
| 1476 | ** |
| 1477 | ** COMMAND: http |
| 1478 | ** |
| 1479 | ** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https] |
| 1480 | ** |
| 1481 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 1482 | ** is delivered on stdout. This method is used to launch an HTTP request |
| @@ -1573,11 +1591,11 @@ | |
| 1573 | } |
| 1574 | #endif |
| 1575 | #endif |
| 1576 | |
| 1577 | /* |
| 1578 | ** COMMAND: server |
| 1579 | ** COMMAND: ui |
| 1580 | ** |
| 1581 | ** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY? |
| 1582 | ** Or: %fossil ui ?-P|--port TCPPORT? ?REPOSITORY? |
| 1583 | ** |
| 1584 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -409,14 +409,17 @@ | |
| 409 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 410 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 411 | zCmdName = "cgi"; |
| 412 | g.isHTTP = 1; |
| 413 | }else if( argc<2 ){ |
| 414 | fossil_print( |
| 415 | "Usage: %s COMMAND ...\n" |
| 416 | " or: %s help -- for a list of common commands\n" |
| 417 | " or: %s help COMMMAND -- for help with the named command\n" |
| 418 | " or: %s commands -- for a list of all commands\n", |
| 419 | argv[0], argv[0], argv[0], argv[0]); |
| 420 | fossil_exit(1); |
| 421 | }else{ |
| 422 | g.isHTTP = 0; |
| 423 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 424 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 425 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| @@ -453,14 +456,15 @@ | |
| 456 | for(i=0; i<count(aCommand); i++){ |
| 457 | if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){ |
| 458 | blob_appendf(&couldbe, " %s", aCommand[i].zName); |
| 459 | } |
| 460 | } |
| 461 | fossil_print("%s: ambiguous command prefix: %s\n" |
| 462 | "%s: could be any of:%s\n" |
| 463 | "%s: use \"help\" for more information\n", |
| 464 | argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]); |
| 465 | fossil_exit(1); |
| 466 | } |
| 467 | if(rc){ |
| 468 | fossil_fatal("%s: unrecoverable error while initializing JSON CGI bits: " |
| 469 | "cson error code #%d (%s)\n", |
| 470 | argv[0], rc, cson_rc_string(rc)); |
| @@ -810,38 +814,45 @@ | |
| 814 | } |
| 815 | |
| 816 | /* |
| 817 | ** List of commands starting with zPrefix, or all commands if zPrefix is NULL. |
| 818 | */ |
| 819 | static void command_list(const char *zPrefix, int cmdMask){ |
| 820 | int i, nCmd; |
| 821 | int nPrefix = zPrefix ? strlen(zPrefix) : 0; |
| 822 | const char *aCmd[count(aCommand)]; |
| 823 | for(i=nCmd=0; i<count(aCommand); i++){ |
| 824 | const char *z = aCommand[i].zName; |
| 825 | if( (aCommand[i].cmdFlags & cmdMask)==0 ) continue; |
| 826 | if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue; |
| 827 | aCmd[nCmd++] = aCommand[i].zName; |
| 828 | } |
| 829 | multi_column_list(aCmd, nCmd); |
| 830 | } |
| 831 | |
| 832 | /* |
| 833 | ** COMMAND: commands |
| 834 | ** |
| 835 | ** Usage: %fossil commands ?--test? ?--all? ?--aux? |
| 836 | ** |
| 837 | ** List available commands. If the --test option is used list only unsupported |
| 838 | ** commands intended for testing and debugging. With --all, show all commands |
| 839 | ** including test debugging commands. With --aux, show only the auxiliary |
| 840 | ** and less often used commands, and/or command aliases. |
| 841 | */ |
| 842 | void cmd_command_list(void){ |
| 843 | int cmdFlags = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER; |
| 844 | if( find_option("all",0,0)!=0 ){ |
| 845 | cmdFlags |= CMDFLAG_TEST; |
| 846 | }else if( find_option("test",0,0)!=0 ){ |
| 847 | cmdFlags = CMDFLAG_TEST; |
| 848 | }else if( find_option("aux",0,0)!=0 ){ |
| 849 | cmdFlags = CMDFLAG_2ND_TIER; |
| 850 | }else{ |
| 851 | fossil_print("Use --test or --all to see test and debug commands.\n"); |
| 852 | } |
| 853 | command_list(0, cmdFlags); |
| 854 | } |
| 855 | |
| 856 | |
| 857 | /* |
| 858 | ** COMMAND: test-list-webpage |
| @@ -873,32 +884,39 @@ | |
| 884 | |
| 885 | /* |
| 886 | ** COMMAND: help |
| 887 | ** |
| 888 | ** Usage: %fossil help COMMAND |
| 889 | ** or: %fossil COMMAND -help |
| 890 | ** |
| 891 | ** Display information on how to use COMMAND. To display a list of |
| 892 | ** available commands use: |
| 893 | ** |
| 894 | ** %fossil commands |
| 895 | */ |
| 896 | void help_cmd(void){ |
| 897 | int rc, idx; |
| 898 | const char *z; |
| 899 | if( g.argc<3 ){ |
| 900 | z = fossil_nameofexe(); |
| 901 | fossil_print( |
| 902 | "Usage: %s help COMMAND\n" |
| 903 | "Common COMMANDs: (use \"%s commands\" for a complete list)\n", |
| 904 | z, z); |
| 905 | command_list(0, CMDFLAG_1ST_TIER); |
| 906 | version_cmd(); |
| 907 | return; |
| 908 | } |
| 909 | rc = name_search(g.argv[2], aCommand, count(aCommand), &idx); |
| 910 | if( rc==1 ){ |
| 911 | fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]); |
| 912 | command_list(0, 0xff); |
| 913 | fossil_exit(1); |
| 914 | }else if( rc==2 ){ |
| 915 | fossil_print("ambiguous command prefix: %s\nMatching commands:\n", |
| 916 | g.argv[2]); |
| 917 | command_list(g.argv[2], 0xff); |
| 918 | fossil_exit(1); |
| 919 | } |
| 920 | z = aCmdHelp[idx]; |
| 921 | if( z==0 ){ |
| 922 | fossil_fatal("no help available for the %s command", |
| @@ -1285,11 +1303,11 @@ | |
| 1303 | */ |
| 1304 | cgi_reply(); |
| 1305 | } |
| 1306 | |
| 1307 | /* |
| 1308 | ** COMMAND: cgi* |
| 1309 | ** |
| 1310 | ** Usage: %fossil ?cgi? SCRIPT |
| 1311 | ** |
| 1312 | ** The SCRIPT argument is the name of a file that is the CGI script |
| 1313 | ** that is being run. The command name, "cgi", may be omitted if |
| @@ -1472,11 +1490,11 @@ | |
| 1490 | ** |
| 1491 | ** fossil http REPOSITORY INFILE OUTFILE IPADDR |
| 1492 | ** |
| 1493 | ** The argv==6 form is used by the win32 server only. |
| 1494 | ** |
| 1495 | ** COMMAND: http* |
| 1496 | ** |
| 1497 | ** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https] |
| 1498 | ** |
| 1499 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 1500 | ** is delivered on stdout. This method is used to launch an HTTP request |
| @@ -1573,11 +1591,11 @@ | |
| 1591 | } |
| 1592 | #endif |
| 1593 | #endif |
| 1594 | |
| 1595 | /* |
| 1596 | ** COMMAND: server* |
| 1597 | ** COMMAND: ui |
| 1598 | ** |
| 1599 | ** Usage: %fossil server ?-P|--port TCPPORT? ?REPOSITORY? |
| 1600 | ** Or: %fossil ui ?-P|--port TCPPORT? ?REPOSITORY? |
| 1601 | ** |
| 1602 |
+22
-7
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -1334,18 +1334,33 @@ | ||
| 1334 | 1334 | } |
| 1335 | 1335 | } |
| 1336 | 1336 | } |
| 1337 | 1337 | if( pParent->zBaseline && pChild->zBaseline ){ |
| 1338 | 1338 | /* Both parent and child are delta manifests. Look for files that |
| 1339 | - ** are marked as deleted in the parent but which reappear in the child | |
| 1340 | - ** and show such files as being added in the child. */ | |
| 1339 | + ** are deleted or modified in the parent but which reappear or revert | |
| 1340 | + ** to baseline in the child and show such files as being added or changed | |
| 1341 | + ** in the child. */ | |
| 1341 | 1342 | for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){ |
| 1342 | - if( pParentFile->zUuid ) continue; | |
| 1343 | - pChildFile = manifest_file_seek(pChild, pParentFile->zName); | |
| 1344 | - if( pChildFile ){ | |
| 1345 | - add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0, | |
| 1346 | - isPublic, manifest_file_mperm(pChildFile)); | |
| 1343 | + if( pParentFile->zUuid ){ | |
| 1344 | + pChildFile = manifest_file_seek_base(pChild, pParentFile->zName); | |
| 1345 | + if( pChildFile==0 ){ | |
| 1346 | + /* The child file reverts to baseline. Show this as a change */ | |
| 1347 | + pChildFile = manifest_file_seek(pChild, pParentFile->zName); | |
| 1348 | + if( pChildFile ){ | |
| 1349 | + add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, | |
| 1350 | + pChildFile->zName, 0, isPublic, | |
| 1351 | + manifest_file_mperm(pChildFile)); | |
| 1352 | + } | |
| 1353 | + } | |
| 1354 | + }else{ | |
| 1355 | + pChildFile = manifest_file_seek(pChild, pParentFile->zName); | |
| 1356 | + if( pChildFile ){ | |
| 1357 | + /* File resurrected in the child after having been deleted in | |
| 1358 | + ** the parent. Show this as an added file. */ | |
| 1359 | + add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0, | |
| 1360 | + isPublic, manifest_file_mperm(pChildFile)); | |
| 1361 | + } | |
| 1347 | 1362 | } |
| 1348 | 1363 | } |
| 1349 | 1364 | }else if( pChild->zBaseline==0 ){ |
| 1350 | 1365 | /* pChild is a baseline. Look for files that are present in pParent |
| 1351 | 1366 | ** but are missing from pChild and mark them as having been deleted. */ |
| 1352 | 1367 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -1334,18 +1334,33 @@ | |
| 1334 | } |
| 1335 | } |
| 1336 | } |
| 1337 | if( pParent->zBaseline && pChild->zBaseline ){ |
| 1338 | /* Both parent and child are delta manifests. Look for files that |
| 1339 | ** are marked as deleted in the parent but which reappear in the child |
| 1340 | ** and show such files as being added in the child. */ |
| 1341 | for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){ |
| 1342 | if( pParentFile->zUuid ) continue; |
| 1343 | pChildFile = manifest_file_seek(pChild, pParentFile->zName); |
| 1344 | if( pChildFile ){ |
| 1345 | add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0, |
| 1346 | isPublic, manifest_file_mperm(pChildFile)); |
| 1347 | } |
| 1348 | } |
| 1349 | }else if( pChild->zBaseline==0 ){ |
| 1350 | /* pChild is a baseline. Look for files that are present in pParent |
| 1351 | ** but are missing from pChild and mark them as having been deleted. */ |
| 1352 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -1334,18 +1334,33 @@ | |
| 1334 | } |
| 1335 | } |
| 1336 | } |
| 1337 | if( pParent->zBaseline && pChild->zBaseline ){ |
| 1338 | /* Both parent and child are delta manifests. Look for files that |
| 1339 | ** are deleted or modified in the parent but which reappear or revert |
| 1340 | ** to baseline in the child and show such files as being added or changed |
| 1341 | ** in the child. */ |
| 1342 | for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){ |
| 1343 | if( pParentFile->zUuid ){ |
| 1344 | pChildFile = manifest_file_seek_base(pChild, pParentFile->zName); |
| 1345 | if( pChildFile==0 ){ |
| 1346 | /* The child file reverts to baseline. Show this as a change */ |
| 1347 | pChildFile = manifest_file_seek(pChild, pParentFile->zName); |
| 1348 | if( pChildFile ){ |
| 1349 | add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, |
| 1350 | pChildFile->zName, 0, isPublic, |
| 1351 | manifest_file_mperm(pChildFile)); |
| 1352 | } |
| 1353 | } |
| 1354 | }else{ |
| 1355 | pChildFile = manifest_file_seek(pChild, pParentFile->zName); |
| 1356 | if( pChildFile ){ |
| 1357 | /* File resurrected in the child after having been deleted in |
| 1358 | ** the parent. Show this as an added file. */ |
| 1359 | add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0, |
| 1360 | isPublic, manifest_file_mperm(pChildFile)); |
| 1361 | } |
| 1362 | } |
| 1363 | } |
| 1364 | }else if( pChild->zBaseline==0 ){ |
| 1365 | /* pChild is a baseline. Look for files that are present in pParent |
| 1366 | ** but are missing from pChild and mark them as having been deleted. */ |
| 1367 |
+3
-2
| --- src/md5.c | ||
| +++ src/md5.c | ||
| @@ -421,14 +421,15 @@ | ||
| 421 | 421 | return 0; |
| 422 | 422 | } |
| 423 | 423 | |
| 424 | 424 | |
| 425 | 425 | /* |
| 426 | -** COMMAND: test-md5sum | |
| 426 | +** COMMAND: md5sum* | |
| 427 | +** Usage: %fossil md5sum FILES.... | |
| 427 | 428 | ** |
| 428 | 429 | ** Compute an MD5 checksum of all files named on the command-line. |
| 429 | -** If an file is named "-" then take its content from standard input. | |
| 430 | +** If a file is named "-" then content is read from standard input. | |
| 430 | 431 | */ |
| 431 | 432 | void md5sum_test(void){ |
| 432 | 433 | int i; |
| 433 | 434 | Blob in; |
| 434 | 435 | Blob cksum; |
| 435 | 436 |
| --- src/md5.c | |
| +++ src/md5.c | |
| @@ -421,14 +421,15 @@ | |
| 421 | return 0; |
| 422 | } |
| 423 | |
| 424 | |
| 425 | /* |
| 426 | ** COMMAND: test-md5sum |
| 427 | ** |
| 428 | ** Compute an MD5 checksum of all files named on the command-line. |
| 429 | ** If an file is named "-" then take its content from standard input. |
| 430 | */ |
| 431 | void md5sum_test(void){ |
| 432 | int i; |
| 433 | Blob in; |
| 434 | Blob cksum; |
| 435 |
| --- src/md5.c | |
| +++ src/md5.c | |
| @@ -421,14 +421,15 @@ | |
| 421 | return 0; |
| 422 | } |
| 423 | |
| 424 | |
| 425 | /* |
| 426 | ** COMMAND: md5sum* |
| 427 | ** Usage: %fossil md5sum FILES.... |
| 428 | ** |
| 429 | ** Compute an MD5 checksum of all files named on the command-line. |
| 430 | ** If a file is named "-" then content is read from standard input. |
| 431 | */ |
| 432 | void md5sum_test(void){ |
| 433 | int i; |
| 434 | Blob in; |
| 435 | Blob cksum; |
| 436 |
+29
-7
| --- src/mkindex.c | ||
| +++ src/mkindex.c | ||
| @@ -36,11 +36,13 @@ | ||
| 36 | 36 | ** We also scan for comments lines of this form: |
| 37 | 37 | ** |
| 38 | 38 | ** COMMAND: cmdname |
| 39 | 39 | ** |
| 40 | 40 | ** These entries build a constant table used to map command names into |
| 41 | -** functions. | |
| 41 | +** functions. If cmdname ends with "*" then the command is a second-tier | |
| 42 | +** command that is not displayed by the "fossil help" command. The | |
| 43 | +** final "*" is not considered to be part of the command name. | |
| 42 | 44 | ** |
| 43 | 45 | ** Comment text following COMMAND: through the end of the comment is |
| 44 | 46 | ** understood to be help text for the command specified. This help |
| 45 | 47 | ** text is accumulated and a table containing the text for each command |
| 46 | 48 | ** is generated. That table is used implement the "fossil help" command |
| @@ -224,28 +226,48 @@ | ||
| 224 | 226 | printf( |
| 225 | 227 | "typedef struct NameMap NameMap;\n" |
| 226 | 228 | "struct NameMap {\n" |
| 227 | 229 | " const char *zName;\n" |
| 228 | 230 | " void (*xFunc)(void);\n" |
| 231 | + " char cmdFlags;\n" | |
| 229 | 232 | "};\n" |
| 233 | + "#define CMDFLAG_1ST_TIER 0x01\n" | |
| 234 | + "#define CMDFLAG_2ND_TIER 0x02\n" | |
| 235 | + "#define CMDFLAG_TEST 0x04\n" | |
| 230 | 236 | "static const NameMap aWebpage[] = {\n" |
| 231 | 237 | ); |
| 232 | 238 | for(i=0; i<nFixed && aEntry[i].eType==0; i++){ |
| 233 | - printf(" { \"%s\",%*s %s },\n", | |
| 234 | - aEntry[i].zPath, (int)(25-strlen(aEntry[i].zPath)), "", | |
| 235 | - aEntry[i].zFunc | |
| 239 | + const char *z = aEntry[i].zPath; | |
| 240 | + int n = strlen(z); | |
| 241 | + printf(" { \"%s\",%*s %s,%*s 1 },\n", | |
| 242 | + z, | |
| 243 | + 25-n, "", | |
| 244 | + aEntry[i].zFunc, | |
| 245 | + (int)(35-strlen(aEntry[i].zFunc)), "" | |
| 236 | 246 | ); |
| 237 | 247 | } |
| 238 | 248 | printf("};\n"); |
| 239 | 249 | nType0 = i; |
| 240 | 250 | printf( |
| 241 | 251 | "static const NameMap aCommand[] = {\n" |
| 242 | 252 | ); |
| 243 | 253 | for(i=nType0; i<nFixed && aEntry[i].eType==1; i++){ |
| 244 | - printf(" { \"%s\",%*s %s },\n", | |
| 245 | - aEntry[i].zPath, (int)(25-strlen(aEntry[i].zPath)), "", | |
| 246 | - aEntry[i].zFunc | |
| 254 | + const char *z = aEntry[i].zPath; | |
| 255 | + int n = strlen(z); | |
| 256 | + int cmdFlags = 0x01; | |
| 257 | + if( z[n-1]=='*' ){ | |
| 258 | + n--; | |
| 259 | + cmdFlags = 0x02; | |
| 260 | + }else if( memcmp(z, "test-", 5)==0 ){ | |
| 261 | + cmdFlags = 0x04; | |
| 262 | + } | |
| 263 | + printf(" { \"%.*s\",%*s %s,%*s %d },\n", | |
| 264 | + n, z, | |
| 265 | + 25-n, "", | |
| 266 | + aEntry[i].zFunc, | |
| 267 | + (int)(35-strlen(aEntry[i].zFunc)), "", | |
| 268 | + cmdFlags | |
| 247 | 269 | ); |
| 248 | 270 | } |
| 249 | 271 | printf("};\n"); |
| 250 | 272 | for(i=nType0; i<nFixed; i++){ |
| 251 | 273 | char *z = aEntry[i].zHelp; |
| 252 | 274 |
| --- src/mkindex.c | |
| +++ src/mkindex.c | |
| @@ -36,11 +36,13 @@ | |
| 36 | ** We also scan for comments lines of this form: |
| 37 | ** |
| 38 | ** COMMAND: cmdname |
| 39 | ** |
| 40 | ** These entries build a constant table used to map command names into |
| 41 | ** functions. |
| 42 | ** |
| 43 | ** Comment text following COMMAND: through the end of the comment is |
| 44 | ** understood to be help text for the command specified. This help |
| 45 | ** text is accumulated and a table containing the text for each command |
| 46 | ** is generated. That table is used implement the "fossil help" command |
| @@ -224,28 +226,48 @@ | |
| 224 | printf( |
| 225 | "typedef struct NameMap NameMap;\n" |
| 226 | "struct NameMap {\n" |
| 227 | " const char *zName;\n" |
| 228 | " void (*xFunc)(void);\n" |
| 229 | "};\n" |
| 230 | "static const NameMap aWebpage[] = {\n" |
| 231 | ); |
| 232 | for(i=0; i<nFixed && aEntry[i].eType==0; i++){ |
| 233 | printf(" { \"%s\",%*s %s },\n", |
| 234 | aEntry[i].zPath, (int)(25-strlen(aEntry[i].zPath)), "", |
| 235 | aEntry[i].zFunc |
| 236 | ); |
| 237 | } |
| 238 | printf("};\n"); |
| 239 | nType0 = i; |
| 240 | printf( |
| 241 | "static const NameMap aCommand[] = {\n" |
| 242 | ); |
| 243 | for(i=nType0; i<nFixed && aEntry[i].eType==1; i++){ |
| 244 | printf(" { \"%s\",%*s %s },\n", |
| 245 | aEntry[i].zPath, (int)(25-strlen(aEntry[i].zPath)), "", |
| 246 | aEntry[i].zFunc |
| 247 | ); |
| 248 | } |
| 249 | printf("};\n"); |
| 250 | for(i=nType0; i<nFixed; i++){ |
| 251 | char *z = aEntry[i].zHelp; |
| 252 |
| --- src/mkindex.c | |
| +++ src/mkindex.c | |
| @@ -36,11 +36,13 @@ | |
| 36 | ** We also scan for comments lines of this form: |
| 37 | ** |
| 38 | ** COMMAND: cmdname |
| 39 | ** |
| 40 | ** These entries build a constant table used to map command names into |
| 41 | ** functions. If cmdname ends with "*" then the command is a second-tier |
| 42 | ** command that is not displayed by the "fossil help" command. The |
| 43 | ** final "*" is not considered to be part of the command name. |
| 44 | ** |
| 45 | ** Comment text following COMMAND: through the end of the comment is |
| 46 | ** understood to be help text for the command specified. This help |
| 47 | ** text is accumulated and a table containing the text for each command |
| 48 | ** is generated. That table is used implement the "fossil help" command |
| @@ -224,28 +226,48 @@ | |
| 226 | printf( |
| 227 | "typedef struct NameMap NameMap;\n" |
| 228 | "struct NameMap {\n" |
| 229 | " const char *zName;\n" |
| 230 | " void (*xFunc)(void);\n" |
| 231 | " char cmdFlags;\n" |
| 232 | "};\n" |
| 233 | "#define CMDFLAG_1ST_TIER 0x01\n" |
| 234 | "#define CMDFLAG_2ND_TIER 0x02\n" |
| 235 | "#define CMDFLAG_TEST 0x04\n" |
| 236 | "static const NameMap aWebpage[] = {\n" |
| 237 | ); |
| 238 | for(i=0; i<nFixed && aEntry[i].eType==0; i++){ |
| 239 | const char *z = aEntry[i].zPath; |
| 240 | int n = strlen(z); |
| 241 | printf(" { \"%s\",%*s %s,%*s 1 },\n", |
| 242 | z, |
| 243 | 25-n, "", |
| 244 | aEntry[i].zFunc, |
| 245 | (int)(35-strlen(aEntry[i].zFunc)), "" |
| 246 | ); |
| 247 | } |
| 248 | printf("};\n"); |
| 249 | nType0 = i; |
| 250 | printf( |
| 251 | "static const NameMap aCommand[] = {\n" |
| 252 | ); |
| 253 | for(i=nType0; i<nFixed && aEntry[i].eType==1; i++){ |
| 254 | const char *z = aEntry[i].zPath; |
| 255 | int n = strlen(z); |
| 256 | int cmdFlags = 0x01; |
| 257 | if( z[n-1]=='*' ){ |
| 258 | n--; |
| 259 | cmdFlags = 0x02; |
| 260 | }else if( memcmp(z, "test-", 5)==0 ){ |
| 261 | cmdFlags = 0x04; |
| 262 | } |
| 263 | printf(" { \"%.*s\",%*s %s,%*s %d },\n", |
| 264 | n, z, |
| 265 | 25-n, "", |
| 266 | aEntry[i].zFunc, |
| 267 | (int)(35-strlen(aEntry[i].zFunc)), "", |
| 268 | cmdFlags |
| 269 | ); |
| 270 | } |
| 271 | printf("};\n"); |
| 272 | for(i=nType0; i<nFixed; i++){ |
| 273 | char *z = aEntry[i].zHelp; |
| 274 |
+329
-260
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -22,10 +22,220 @@ | ||
| 22 | 22 | ** not necessarily in canonical form. |
| 23 | 23 | */ |
| 24 | 24 | #include "config.h" |
| 25 | 25 | #include "name.h" |
| 26 | 26 | #include <assert.h> |
| 27 | + | |
| 28 | +/* | |
| 29 | +** Return TRUE if the string begins with something that looks roughly | |
| 30 | +** like an ISO date/time string. The SQLite date/time functions will | |
| 31 | +** have the final say-so about whether or not the date/time string is | |
| 32 | +** well-formed. | |
| 33 | +*/ | |
| 34 | +static int is_date(const char *z){ | |
| 35 | + if( !fossil_isdigit(z[0]) ) return 0; | |
| 36 | + if( !fossil_isdigit(z[1]) ) return 0; | |
| 37 | + if( !fossil_isdigit(z[2]) ) return 0; | |
| 38 | + if( !fossil_isdigit(z[3]) ) return 0; | |
| 39 | + if( z[4]!='-') return 0; | |
| 40 | + if( !fossil_isdigit(z[5]) ) return 0; | |
| 41 | + if( !fossil_isdigit(z[6]) ) return 0; | |
| 42 | + if( z[7]!='-') return 0; | |
| 43 | + if( !fossil_isdigit(z[8]) ) return 0; | |
| 44 | + if( !fossil_isdigit(z[9]) ) return 0; | |
| 45 | + return 1; | |
| 46 | +} | |
| 47 | + | |
| 48 | +/* | |
| 49 | +** Convert a symbolic name into a RID. Acceptable forms: | |
| 50 | +** | |
| 51 | +** * SHA1 hash | |
| 52 | +** * SHA1 hash prefix of at least 4 characters | |
| 53 | +** * Symbolic Name | |
| 54 | +** * "tag:" + symbolic name | |
| 55 | +** * Date or date-time | |
| 56 | +** * "date:" + Date or date-time | |
| 57 | +** * symbolic-name ":" date-time | |
| 58 | +** * "tip" | |
| 59 | +** | |
| 60 | +** The following additional forms are available in local checkouts: | |
| 61 | +** | |
| 62 | +** * "current" | |
| 63 | +** * "prev" or "previous" | |
| 64 | +** * "next" | |
| 65 | +** | |
| 66 | +** Return the RID of the matching artifact. Or return 0 if the name does not | |
| 67 | +** match any known object. Or return -1 if the name is ambiguious. | |
| 68 | +** | |
| 69 | +** The zType parameter specifies the type of artifact: ci, t, w, e, g. | |
| 70 | +** If zType is NULL or "" or "*" then any type of artifact will serve. | |
| 71 | +** zType is "ci" in most use cases since we are usually searching for | |
| 72 | +** a check-in. | |
| 73 | +*/ | |
| 74 | +static int symbolic_name_to_rid(const char *zTag, const char *zType){ | |
| 75 | + int vid; | |
| 76 | + int rid = 0; | |
| 77 | + int nTag; | |
| 78 | + int i; | |
| 79 | + | |
| 80 | + if( zType==0 || zType[0]==0 ) zType = "*"; | |
| 81 | + if( zTag==0 || zTag[0]==0 ) return 0; | |
| 82 | + | |
| 83 | + /* special keyword: "tip" */ | |
| 84 | + if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){ | |
| 85 | + rid = db_int(0, | |
| 86 | + "SELECT objid" | |
| 87 | + " FROM event" | |
| 88 | + " WHERE type='ci'" | |
| 89 | + " ORDER BY event.mtime DESC" | |
| 90 | + ); | |
| 91 | + if( rid ) return rid; | |
| 92 | + } | |
| 93 | + | |
| 94 | + /* special keywords: "prev", "previous", "current", and "next" */ | |
| 95 | + if( g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ | |
| 96 | + if( fossil_strcmp(zTag, "current")==0 ){ | |
| 97 | + rid = vid; | |
| 98 | + }else if( fossil_strcmp(zTag, "prev")==0 | |
| 99 | + || fossil_strcmp(zTag, "previous")==0 ){ | |
| 100 | + rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", vid); | |
| 101 | + }else if( fossil_strcmp(zTag, "next")==0 ){ | |
| 102 | + rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d" | |
| 103 | + " ORDER BY isprim DESC, mtime DESC", vid); | |
| 104 | + } | |
| 105 | + if( rid ) return rid; | |
| 106 | + } | |
| 107 | + | |
| 108 | + /* Date and times */ | |
| 109 | + if( memcmp(zTag, "date:", 5)==0 ){ | |
| 110 | + rid = db_int(0, | |
| 111 | + "SELECT objid FROM event" | |
| 112 | + " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" | |
| 113 | + " ORDER BY mtime DESC LIMIT 1", | |
| 114 | + &zTag[5], zType); | |
| 115 | + return rid; | |
| 116 | + } | |
| 117 | + if( is_date(zTag) ){ | |
| 118 | + rid = db_int(0, | |
| 119 | + "SELECT objid FROM event" | |
| 120 | + " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" | |
| 121 | + " ORDER BY mtime DESC LIMIT 1", | |
| 122 | + zTag, zType); | |
| 123 | + if( rid) return rid; | |
| 124 | + } | |
| 125 | + | |
| 126 | + /* Deprecated date & time formats: "local:" + date-time and | |
| 127 | + ** "utc:" + date-time */ | |
| 128 | + if( memcmp(zTag, "local:", 6)==0 ){ | |
| 129 | + rid = db_int(0, | |
| 130 | + "SELECT objid FROM event" | |
| 131 | + " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" | |
| 132 | + " ORDER BY mtime DESC LIMIT 1", | |
| 133 | + &zTag[6], zType); | |
| 134 | + return rid; | |
| 135 | + } | |
| 136 | + if( memcmp(zTag, "utc:", 4)==0 ){ | |
| 137 | + rid = db_int(0, | |
| 138 | + "SELECT objid FROM event" | |
| 139 | + " WHERE mtime<=julianday('%qz') AND type GLOB '%q'" | |
| 140 | + " ORDER BY mtime DESC LIMIT 1", | |
| 141 | + &zTag[4], zType); | |
| 142 | + return rid; | |
| 143 | + } | |
| 144 | + | |
| 145 | + /* "tag:" + symbolic-name */ | |
| 146 | + if( memcmp(zTag, "tag:", 4)==0 ){ | |
| 147 | + rid = db_int(0, | |
| 148 | + "SELECT event.objid" | |
| 149 | + " FROM tag, tagxref, event" | |
| 150 | + " WHERE tag.tagname='sym-%q' " | |
| 151 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 152 | + " AND event.objid=tagxref.rid " | |
| 153 | + " AND event.type GLOB '%q'" | |
| 154 | + " ORDER BY event.mtime DESC /*sort*/", | |
| 155 | + &zTag[4], zType | |
| 156 | + ); | |
| 157 | + return rid; | |
| 158 | + } | |
| 159 | + | |
| 160 | + /* symbolic-name ":" date-time */ | |
| 161 | + nTag = strlen(zTag); | |
| 162 | + for(i=0; i<nTag-10 && zTag[i]!=':'; i++){} | |
| 163 | + if( zTag[i]==':' && is_date(&zTag[i+1]) ){ | |
| 164 | + char *zDate = mprintf("%s", &zTag[i+1]); | |
| 165 | + char *zTagBase = mprintf("%.*s", i, zTag); | |
| 166 | + int nDate = strlen(zDate); | |
| 167 | + if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ | |
| 168 | + zDate[nDate-3] = 'z'; | |
| 169 | + zDate[nDate-2] = 0; | |
| 170 | + } | |
| 171 | + rid = db_int(0, | |
| 172 | + "SELECT event.objid" | |
| 173 | + " FROM tag, tagxref, event" | |
| 174 | + " WHERE tag.tagname='sym-%q' " | |
| 175 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 176 | + " AND event.objid=tagxref.rid " | |
| 177 | + " AND event.mtime<=julianday(%Q)" | |
| 178 | + " AND event.type GLOB '%q'" | |
| 179 | + " ORDER BY event.mtime DESC /*sort*/ ", | |
| 180 | + zTagBase, zDate, zType | |
| 181 | + ); | |
| 182 | + return rid; | |
| 183 | + } | |
| 184 | + | |
| 185 | + /* SHA1 hash or prefix */ | |
| 186 | + if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){ | |
| 187 | + Stmt q; | |
| 188 | + char zUuid[UUID_SIZE+1]; | |
| 189 | + memcpy(zUuid, zTag, nTag+1); | |
| 190 | + canonical16(zUuid, nTag); | |
| 191 | + rid = 0; | |
| 192 | + if( zType[0]=='*' ){ | |
| 193 | + db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%s*'", zUuid); | |
| 194 | + }else{ | |
| 195 | + db_prepare(&q, | |
| 196 | + "SELECT blob.rid" | |
| 197 | + " FROM blob, event" | |
| 198 | + " WHERE blob.uuid GLOB '%s*'" | |
| 199 | + " AND event.objid=blob.rid" | |
| 200 | + " AND event.type GLOB '%q'", | |
| 201 | + zUuid, zType | |
| 202 | + ); | |
| 203 | + } | |
| 204 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 205 | + rid = db_column_int(&q, 0); | |
| 206 | + if( db_step(&q)==SQLITE_ROW ) rid = -1; | |
| 207 | + } | |
| 208 | + db_finalize(&q); | |
| 209 | + if( rid ) return rid; | |
| 210 | + } | |
| 211 | + | |
| 212 | + /* Symbolic name */ | |
| 213 | + rid = db_int(0, | |
| 214 | + "SELECT event.objid" | |
| 215 | + " FROM tag, tagxref, event" | |
| 216 | + " WHERE tag.tagname='sym-%q' " | |
| 217 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 218 | + " AND event.objid=tagxref.rid " | |
| 219 | + " AND event.type GLOB '%q'" | |
| 220 | + " ORDER BY event.mtime DESC /*sort*/ ", | |
| 221 | + zTag, zType | |
| 222 | + ); | |
| 223 | + if( rid>0 ) return rid; | |
| 224 | + | |
| 225 | + /* Undocumented: numeric tags get translated directly into the RID */ | |
| 226 | + for(i=0; fossil_isdigit(zTag[i]); i++){} | |
| 227 | + if( zTag[i]==0 ){ | |
| 228 | + rid = db_int(0, | |
| 229 | + "SELECT event.objid" | |
| 230 | + " FROM event" | |
| 231 | + " WHERE event.objid=%s" | |
| 232 | + " AND event.type GLOB '%q'", zTag, zType); | |
| 233 | + } | |
| 234 | + return rid; | |
| 235 | +} | |
| 236 | + | |
| 27 | 237 | |
| 28 | 238 | /* |
| 29 | 239 | ** This routine takes a user-entered UUID which might be in mixed |
| 30 | 240 | ** case and might only be a prefix of the full UUID and converts it |
| 31 | 241 | ** into the full-length UUID in canonical form. |
| @@ -41,228 +251,23 @@ | ||
| 41 | 251 | ** |
| 42 | 252 | ** Return 0 on success. Return 1 if the name cannot be resolved. |
| 43 | 253 | ** Return 2 name is ambiguous. |
| 44 | 254 | */ |
| 45 | 255 | int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){ |
| 46 | - int rc; | |
| 47 | - int sz; | |
| 48 | - sz = blob_size(pName); | |
| 49 | - if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){ | |
| 50 | - char *zUuid; | |
| 51 | - const char *zName = blob_str(pName); | |
| 52 | - if( memcmp(zName, "tag:", 4)==0 ){ | |
| 53 | - zName += 4; | |
| 54 | - zUuid = tag_to_uuid(zName, zType); | |
| 55 | - }else{ | |
| 56 | - zUuid = tag_to_uuid(zName, zType); | |
| 57 | - if( zUuid==0 ){ | |
| 58 | - zUuid = date_to_uuid(zName, zType); | |
| 59 | - } | |
| 60 | - } | |
| 61 | - if( zUuid ){ | |
| 62 | - blob_reset(pName); | |
| 63 | - blob_append(pName, zUuid, -1); | |
| 64 | - free(zUuid); | |
| 65 | - return 0; | |
| 66 | - } | |
| 67 | - fossil_error(iErrPriority, "not a valid object name: %s", zName); | |
| 68 | - return 1; | |
| 69 | - } | |
| 70 | - blob_materialize(pName); | |
| 71 | - canonical16(blob_buffer(pName), sz); | |
| 72 | - if( sz==UUID_SIZE ){ | |
| 73 | - rc = db_int(1, "SELECT 0 FROM blob WHERE uuid=%B", pName); | |
| 74 | - if( rc ){ | |
| 75 | - fossil_error(iErrPriority, "no such artifact: %b", pName); | |
| 76 | - blob_reset(pName); | |
| 77 | - } | |
| 78 | - }else if( sz<UUID_SIZE && sz>=4 ){ | |
| 79 | - Stmt q; | |
| 80 | - db_prepare(&q, "SELECT uuid FROM blob WHERE uuid GLOB '%b*'", pName); | |
| 81 | - if( db_step(&q)!=SQLITE_ROW ){ | |
| 82 | - char *zUuid; | |
| 83 | - db_finalize(&q); | |
| 84 | - zUuid = tag_to_uuid(blob_str(pName), "*"); | |
| 85 | - if( zUuid ){ | |
| 86 | - blob_reset(pName); | |
| 87 | - blob_append(pName, zUuid, -1); | |
| 88 | - free(zUuid); | |
| 89 | - return 0; | |
| 90 | - } | |
| 91 | - fossil_error(iErrPriority, "no artifacts match the prefix \"%b\"", pName); | |
| 92 | - return 1; | |
| 93 | - } | |
| 94 | - blob_reset(pName); | |
| 95 | - blob_append(pName, db_column_text(&q, 0), db_column_bytes(&q, 0)); | |
| 96 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 97 | - fossil_error(iErrPriority, | |
| 98 | - "multiple artifacts match" | |
| 99 | - ); | |
| 100 | - blob_reset(pName); | |
| 101 | - db_finalize(&q); | |
| 102 | - return 2; | |
| 103 | - } | |
| 104 | - db_finalize(&q); | |
| 105 | - rc = 0; | |
| 106 | - }else{ | |
| 107 | - rc = 0; | |
| 108 | - } | |
| 109 | - return rc; | |
| 110 | -} | |
| 111 | - | |
| 112 | -/* | |
| 113 | -** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. | |
| 114 | -*/ | |
| 115 | -static int is_date(const char *z){ | |
| 116 | - if( !fossil_isdigit(z[0]) ) return 0; | |
| 117 | - if( !fossil_isdigit(z[1]) ) return 0; | |
| 118 | - if( !fossil_isdigit(z[2]) ) return 0; | |
| 119 | - if( !fossil_isdigit(z[3]) ) return 0; | |
| 120 | - if( z[4]!='-') return 0; | |
| 121 | - if( !fossil_isdigit(z[5]) ) return 0; | |
| 122 | - if( !fossil_isdigit(z[6]) ) return 0; | |
| 123 | - if( z[7]!='-') return 0; | |
| 124 | - if( !fossil_isdigit(z[8]) ) return 0; | |
| 125 | - if( !fossil_isdigit(z[9]) ) return 0; | |
| 126 | - return 1; | |
| 127 | -} | |
| 128 | - | |
| 129 | -/* | |
| 130 | -** Convert a symbolic tag name into the UUID of a check-in that contains | |
| 131 | -** that tag. If the tag appears on multiple check-ins, return the UUID | |
| 132 | -** of the most recent check-in with the tag. | |
| 133 | -** | |
| 134 | -** If the input string is of the form: | |
| 135 | -** | |
| 136 | -** tag:date | |
| 137 | -** | |
| 138 | -** Then return the UUID of the oldest check-in with that tag that is | |
| 139 | -** not older than 'date'. | |
| 140 | -** | |
| 141 | -** An input of "tip" returns the most recent check-in. | |
| 142 | -** | |
| 143 | -** Memory to hold the returned string comes from malloc() and needs to | |
| 144 | -** be freed by the caller. | |
| 145 | -*/ | |
| 146 | -char *tag_to_uuid(const char *zTag, const char *zType){ | |
| 147 | - int vid; | |
| 148 | - char *zUuid; | |
| 149 | - | |
| 150 | - if( zType==0 || zType[0]==0 ) zType = "*"; | |
| 151 | - zUuid = db_text(0, | |
| 152 | - "SELECT blob.uuid" | |
| 153 | - " FROM tag, tagxref, event, blob" | |
| 154 | - " WHERE tag.tagname='sym-%q' " | |
| 155 | - " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 156 | - " AND event.objid=tagxref.rid " | |
| 157 | - " AND blob.rid=event.objid " | |
| 158 | - " AND event.type GLOB '%q'" | |
| 159 | - " ORDER BY event.mtime DESC /*sort*/", | |
| 160 | - zTag, zType | |
| 161 | - ); | |
| 162 | - if( zUuid==0 ){ | |
| 163 | - int nTag = strlen(zTag); | |
| 164 | - int i; | |
| 165 | - for(i=0; i<nTag-10; i++){ | |
| 166 | - if( zTag[i]==':' && is_date(&zTag[i+1]) ){ | |
| 167 | - char *zDate = mprintf("%s", &zTag[i+1]); | |
| 168 | - char *zTagBase = mprintf("%.*s", i, zTag); | |
| 169 | - int nDate = strlen(zDate); | |
| 170 | - int useUtc = 0; | |
| 171 | - if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ | |
| 172 | - nDate -= 3; | |
| 173 | - zDate[nDate] = 0; | |
| 174 | - useUtc = 1; | |
| 175 | - } | |
| 176 | - zUuid = db_text(0, | |
| 177 | - "SELECT blob.uuid" | |
| 178 | - " FROM tag, tagxref, event, blob" | |
| 179 | - " WHERE tag.tagname='sym-%q' " | |
| 180 | - " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 181 | - " AND event.objid=tagxref.rid " | |
| 182 | - " AND blob.rid=event.objid " | |
| 183 | - " AND event.mtime<=julianday(%Q %s)" | |
| 184 | - " AND event.type GLOB '%q'" | |
| 185 | - " ORDER BY event.mtime DESC /*sort*/ ", | |
| 186 | - zTagBase, zDate, (useUtc ? "" : ",'utc'"), zType | |
| 187 | - ); | |
| 188 | - break; | |
| 189 | - } | |
| 190 | - } | |
| 191 | - if( zUuid==0 && fossil_strcmp(zTag, "tip")==0 ){ | |
| 192 | - zUuid = db_text(0, | |
| 193 | - "SELECT blob.uuid" | |
| 194 | - " FROM event, blob" | |
| 195 | - " WHERE event.type='ci'" | |
| 196 | - " AND blob.rid=event.objid" | |
| 197 | - " ORDER BY event.mtime DESC" | |
| 198 | - ); | |
| 199 | - } | |
| 200 | - if( zUuid==0 && g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ | |
| 201 | - if( fossil_strcmp(zTag, "current")==0 ){ | |
| 202 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); | |
| 203 | - }else if( fossil_strcmp(zTag, "prev")==0 | |
| 204 | - || fossil_strcmp(zTag, "previous")==0 ){ | |
| 205 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" | |
| 206 | - "(SELECT pid FROM plink WHERE cid=%d AND isprim)", | |
| 207 | - vid); | |
| 208 | - }else if( fossil_strcmp(zTag, "next")==0 ){ | |
| 209 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" | |
| 210 | - "(SELECT cid FROM plink WHERE pid=%d" | |
| 211 | - " ORDER BY isprim DESC, mtime DESC)", | |
| 212 | - vid); | |
| 213 | - } | |
| 214 | - } | |
| 215 | - } | |
| 216 | - return zUuid; | |
| 217 | -} | |
| 218 | - | |
| 219 | -/* | |
| 220 | -** Convert a date/time string into a UUID. | |
| 221 | -** | |
| 222 | -** Input forms accepted: | |
| 223 | -** | |
| 224 | -** date:DATE | |
| 225 | -** local:DATE | |
| 226 | -** utc:DATE | |
| 227 | -** | |
| 228 | -** The DATE is interpreted as localtime unless the "utc:" prefix is used | |
| 229 | -** or a "utc" string appears at the end of the DATE string. | |
| 230 | -*/ | |
| 231 | -char *date_to_uuid(const char *zDate, const char *zType){ | |
| 232 | - int useUtc = 0; | |
| 233 | - int n; | |
| 234 | - char *zCopy = 0; | |
| 235 | - char *zUuid; | |
| 236 | - | |
| 237 | - if( memcmp(zDate, "date:", 5)==0 ){ | |
| 238 | - zDate += 5; | |
| 239 | - }else if( memcmp(zDate, "local:", 6)==0 ){ | |
| 240 | - zDate += 6; | |
| 241 | - }else if( memcmp(zDate, "utc:", 4)==0 ){ | |
| 242 | - zDate += 4; | |
| 243 | - useUtc = 1; | |
| 244 | - } | |
| 245 | - n = strlen(zDate); | |
| 246 | - if( n<10 || !is_date(zDate) ) return 0; | |
| 247 | - if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){ | |
| 248 | - zCopy = mprintf("%s", zDate); | |
| 249 | - zCopy[n-3] = 0; | |
| 250 | - zDate = zCopy; | |
| 251 | - n -= 3; | |
| 252 | - useUtc = 1; | |
| 253 | - } | |
| 254 | - if( zType==0 || zType[0]==0 ) zType = "*"; | |
| 255 | - zUuid = db_text(0, | |
| 256 | - "SELECT (SELECT uuid FROM blob WHERE rid=event.objid)" | |
| 257 | - " FROM event" | |
| 258 | - " WHERE mtime<=julianday(%Q %s) AND type GLOB '%q'" | |
| 259 | - " ORDER BY mtime DESC LIMIT 1", | |
| 260 | - zDate, useUtc ? "" : ",'utc'", zType | |
| 261 | - ); | |
| 262 | - free(zCopy); | |
| 263 | - return zUuid; | |
| 256 | + char *zName = blob_str(pName); | |
| 257 | + int rid = symbolic_name_to_rid(zName, zType); | |
| 258 | + if( rid<0 ){ | |
| 259 | + fossil_error(iErrPriority, "ambiguous name: %s", zName); | |
| 260 | + return 2; | |
| 261 | + }else if( rid==0 ){ | |
| 262 | + fossil_error(iErrPriority, "not found: %s", zName); | |
| 263 | + return 1; | |
| 264 | + }else{ | |
| 265 | + blob_reset(pName); | |
| 266 | + db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 267 | + return 0; | |
| 268 | + } | |
| 264 | 269 | } |
| 265 | 270 | |
| 266 | 271 | /* |
| 267 | 272 | ** COMMAND: test-name-to-id |
| 268 | 273 | ** |
| @@ -284,41 +289,38 @@ | ||
| 284 | 289 | blob_reset(&name); |
| 285 | 290 | } |
| 286 | 291 | } |
| 287 | 292 | |
| 288 | 293 | /* |
| 289 | -** Convert a name to a rid. If the name is a small integer value then | |
| 290 | -** just use atoi() to do the conversion. If the name contains alphabetic | |
| 291 | -** characters or is not an existing rid, then use name_to_uuid then | |
| 292 | -** convert the uuid to a rid. | |
| 294 | +** Convert a name to a rid. If the name can be any of the various forms | |
| 295 | +** accepted: | |
| 296 | +** | |
| 297 | +** * SHA1 hash or prefix thereof | |
| 298 | +** * symbolic name | |
| 299 | +** * date | |
| 300 | +** * label:date | |
| 301 | +** * prev, previous | |
| 302 | +** * next | |
| 303 | +** * tip | |
| 293 | 304 | ** |
| 294 | 305 | ** This routine is used by command-line routines to resolve command-line inputs |
| 295 | 306 | ** into a rid. |
| 296 | 307 | */ |
| 297 | 308 | int name_to_typed_rid(const char *zName, const char *zType){ |
| 298 | - int i; | |
| 299 | 309 | int rid; |
| 300 | - Blob name; | |
| 301 | 310 | |
| 302 | 311 | if( zName==0 || zName[0]==0 ) return 0; |
| 303 | - blob_init(&name, zName, -1); | |
| 304 | - if( name_to_uuid(&name, -1, zType) ){ | |
| 305 | - blob_reset(&name); | |
| 306 | - for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} | |
| 307 | - if( zName[i]==0 ){ | |
| 308 | - rid = atoi(zName); | |
| 309 | - if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ | |
| 310 | - return rid; | |
| 311 | - } | |
| 312 | - } | |
| 313 | - fossil_error(1, "no such artifact: %s", zName); | |
| 312 | + rid = symbolic_name_to_rid(zName, zType); | |
| 313 | + if( rid<0 ){ | |
| 314 | + fossil_error(1, "ambiguous name: %s", zName); | |
| 315 | + return 0; | |
| 316 | + }else if( rid==0 ){ | |
| 317 | + fossil_error(1, "not found: %s", zName); | |
| 314 | 318 | return 0; |
| 315 | 319 | }else{ |
| 316 | - rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); | |
| 317 | - blob_reset(&name); | |
| 320 | + return rid; | |
| 318 | 321 | } |
| 319 | - return rid; | |
| 320 | 322 | } |
| 321 | 323 | int name_to_rid(const char *zName){ |
| 322 | 324 | return name_to_typed_rid(zName, "*"); |
| 323 | 325 | } |
| 324 | 326 | |
| @@ -362,34 +364,101 @@ | ||
| 362 | 364 | ** rid. If the CGI parameter is missing or is not a valid artifact tag, |
| 363 | 365 | ** return 0. If the CGI parameter is ambiguous, redirect to a page that |
| 364 | 366 | ** shows all possibilities and do not return. |
| 365 | 367 | */ |
| 366 | 368 | int name_to_rid_www(const char *zParamName){ |
| 367 | - int i, rc; | |
| 368 | 369 | int rid; |
| 369 | 370 | const char *zName = P(zParamName); |
| 370 | - Blob name; | |
| 371 | 371 | if(!zName && fossil_has_json()){ |
| 372 | 372 | zName = json_find_option_cstr(zParamName,NULL,NULL); |
| 373 | 373 | } |
| 374 | 374 | if( zName==0 || zName[0]==0 ) return 0; |
| 375 | - blob_init(&name, zName, -1); | |
| 376 | - rc = name_to_uuid(&name, -1, "*"); | |
| 377 | - if( rc==1 ){ | |
| 378 | - blob_reset(&name); | |
| 379 | - for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} | |
| 380 | - if( zName[i]==0 ){ | |
| 381 | - rid = atoi(zName); | |
| 382 | - if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ | |
| 383 | - return rid; | |
| 384 | - } | |
| 385 | - } | |
| 386 | - return 0; | |
| 387 | - }else if( rc==2 ){ | |
| 388 | - cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); | |
| 389 | - return 0; | |
| 390 | - }else{ | |
| 391 | - rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); | |
| 392 | - blob_reset(&name); | |
| 393 | - } | |
| 394 | - return rid; | |
| 375 | + rid = symbolic_name_to_rid(zName, "*"); | |
| 376 | + if( rid<0 ){ | |
| 377 | + cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); | |
| 378 | + rid = 0; | |
| 379 | + } | |
| 380 | + return rid; | |
| 381 | +} | |
| 382 | + | |
| 383 | +/* | |
| 384 | +** COMMAND: whatis* | |
| 385 | +** Usage: %fossil whatis NAME | |
| 386 | +** | |
| 387 | +** Resolve the symbol NAME into its canonical 40-character SHA1-hash | |
| 388 | +** artifact name and provide a description of what role that artifact | |
| 389 | +** plays. | |
| 390 | +*/ | |
| 391 | +void whatis_cmd(void){ | |
| 392 | + int rid; | |
| 393 | + const char *zName; | |
| 394 | + int fExtra; | |
| 395 | + db_find_and_open_repository(0,0); | |
| 396 | + fExtra = find_option("verbose","v",0)!=0; | |
| 397 | + if( g.argc!=3 ) usage("whatis NAME"); | |
| 398 | + zName = g.argv[2]; | |
| 399 | + rid = symbolic_name_to_rid(zName, 0); | |
| 400 | + if( rid<0 ){ | |
| 401 | + fossil_print("Ambiguous artifact name prefix: %s\n", zName); | |
| 402 | + }else if( rid==0 ){ | |
| 403 | + fossil_print("Unknown artifact: %s\n", zName); | |
| 404 | + }else{ | |
| 405 | + Stmt q; | |
| 406 | + db_prepare(&q, "SELECT uuid, size, datetime(mtime, 'localtime'), ipaddr" | |
| 407 | + " FROM blob, rcvfrom" | |
| 408 | + " WHERE rid=%d" | |
| 409 | + " AND rcvfrom.rcvid=blob.rcvid", | |
| 410 | + rid); | |
| 411 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 412 | + if( fExtra ){ | |
| 413 | + fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid); | |
| 414 | + fossil_print("size: %d bytes\n", db_column_int(&q,1)); | |
| 415 | + fossil_print("received: %s from %s\n", | |
| 416 | + db_column_text(&q, 2), | |
| 417 | + db_column_text(&q, 3)); | |
| 418 | + }else{ | |
| 419 | + fossil_print("artifact: %s\n", db_column_text(&q,0)); | |
| 420 | + fossil_print("size: %d bytes\n", db_column_int(&q,1)); | |
| 421 | + } | |
| 422 | + } | |
| 423 | + db_finalize(&q); | |
| 424 | + db_prepare(&q, | |
| 425 | + "SELECT type, datetime(mtime,'localtime')," | |
| 426 | + " coalesce(euser,user), coalesce(ecomment,comment)" | |
| 427 | + " FROM event WHERE objid=%d", rid); | |
| 428 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 429 | + const char *zType; | |
| 430 | + switch( db_column_text(&q,0)[0] ){ | |
| 431 | + case 'c': zType = "Check-in"; break; | |
| 432 | + case 'w': zType = "Wiki-edit"; break; | |
| 433 | + case 'e': zType = "Event"; break; | |
| 434 | + case 't': zType = "Ticket-change"; break; | |
| 435 | + case 'g': zType = "Tag-change"; break; | |
| 436 | + } | |
| 437 | + fossil_print("type: %s by %s on %s\n", zType, db_column_text(&q,2), | |
| 438 | + db_column_text(&q, 1)); | |
| 439 | + fossil_print("comment: "); | |
| 440 | + comment_print(db_column_text(&q,3), 10, 78); | |
| 441 | + } | |
| 442 | + db_finalize(&q); | |
| 443 | + db_prepare(&q, | |
| 444 | + "SELECT filename.name, blob.uuid, datetime(event.mtime,'localtime')," | |
| 445 | + " coalesce(euser,user), coalesce(ecomment,comment)" | |
| 446 | + " FROM mlink, filename, blob, event" | |
| 447 | + " WHERE mlink.fid=%d" | |
| 448 | + " AND filename.fnid=mlink.fnid" | |
| 449 | + " AND event.objid=mlink.mid" | |
| 450 | + " AND blob.rid=mlink.mid" | |
| 451 | + " ORDER BY event.mtime DESC /*sort*/", | |
| 452 | + rid); | |
| 453 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 454 | + fossil_print("file: %s\n", db_column_text(&q,0)); | |
| 455 | + fossil_print(" part of [%.10s] by %s on %s\n", | |
| 456 | + db_column_text(&q, 1), | |
| 457 | + db_column_text(&q, 3), | |
| 458 | + db_column_text(&q, 2)); | |
| 459 | + fossil_print(" "); | |
| 460 | + comment_print(db_column_text(&q,4), 10, 78); | |
| 461 | + } | |
| 462 | + db_finalize(&q); | |
| 463 | + } | |
| 395 | 464 | } |
| 396 | 465 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -22,10 +22,220 @@ | |
| 22 | ** not necessarily in canonical form. |
| 23 | */ |
| 24 | #include "config.h" |
| 25 | #include "name.h" |
| 26 | #include <assert.h> |
| 27 | |
| 28 | /* |
| 29 | ** This routine takes a user-entered UUID which might be in mixed |
| 30 | ** case and might only be a prefix of the full UUID and converts it |
| 31 | ** into the full-length UUID in canonical form. |
| @@ -41,228 +251,23 @@ | |
| 41 | ** |
| 42 | ** Return 0 on success. Return 1 if the name cannot be resolved. |
| 43 | ** Return 2 name is ambiguous. |
| 44 | */ |
| 45 | int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){ |
| 46 | int rc; |
| 47 | int sz; |
| 48 | sz = blob_size(pName); |
| 49 | if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){ |
| 50 | char *zUuid; |
| 51 | const char *zName = blob_str(pName); |
| 52 | if( memcmp(zName, "tag:", 4)==0 ){ |
| 53 | zName += 4; |
| 54 | zUuid = tag_to_uuid(zName, zType); |
| 55 | }else{ |
| 56 | zUuid = tag_to_uuid(zName, zType); |
| 57 | if( zUuid==0 ){ |
| 58 | zUuid = date_to_uuid(zName, zType); |
| 59 | } |
| 60 | } |
| 61 | if( zUuid ){ |
| 62 | blob_reset(pName); |
| 63 | blob_append(pName, zUuid, -1); |
| 64 | free(zUuid); |
| 65 | return 0; |
| 66 | } |
| 67 | fossil_error(iErrPriority, "not a valid object name: %s", zName); |
| 68 | return 1; |
| 69 | } |
| 70 | blob_materialize(pName); |
| 71 | canonical16(blob_buffer(pName), sz); |
| 72 | if( sz==UUID_SIZE ){ |
| 73 | rc = db_int(1, "SELECT 0 FROM blob WHERE uuid=%B", pName); |
| 74 | if( rc ){ |
| 75 | fossil_error(iErrPriority, "no such artifact: %b", pName); |
| 76 | blob_reset(pName); |
| 77 | } |
| 78 | }else if( sz<UUID_SIZE && sz>=4 ){ |
| 79 | Stmt q; |
| 80 | db_prepare(&q, "SELECT uuid FROM blob WHERE uuid GLOB '%b*'", pName); |
| 81 | if( db_step(&q)!=SQLITE_ROW ){ |
| 82 | char *zUuid; |
| 83 | db_finalize(&q); |
| 84 | zUuid = tag_to_uuid(blob_str(pName), "*"); |
| 85 | if( zUuid ){ |
| 86 | blob_reset(pName); |
| 87 | blob_append(pName, zUuid, -1); |
| 88 | free(zUuid); |
| 89 | return 0; |
| 90 | } |
| 91 | fossil_error(iErrPriority, "no artifacts match the prefix \"%b\"", pName); |
| 92 | return 1; |
| 93 | } |
| 94 | blob_reset(pName); |
| 95 | blob_append(pName, db_column_text(&q, 0), db_column_bytes(&q, 0)); |
| 96 | if( db_step(&q)==SQLITE_ROW ){ |
| 97 | fossil_error(iErrPriority, |
| 98 | "multiple artifacts match" |
| 99 | ); |
| 100 | blob_reset(pName); |
| 101 | db_finalize(&q); |
| 102 | return 2; |
| 103 | } |
| 104 | db_finalize(&q); |
| 105 | rc = 0; |
| 106 | }else{ |
| 107 | rc = 0; |
| 108 | } |
| 109 | return rc; |
| 110 | } |
| 111 | |
| 112 | /* |
| 113 | ** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. |
| 114 | */ |
| 115 | static int is_date(const char *z){ |
| 116 | if( !fossil_isdigit(z[0]) ) return 0; |
| 117 | if( !fossil_isdigit(z[1]) ) return 0; |
| 118 | if( !fossil_isdigit(z[2]) ) return 0; |
| 119 | if( !fossil_isdigit(z[3]) ) return 0; |
| 120 | if( z[4]!='-') return 0; |
| 121 | if( !fossil_isdigit(z[5]) ) return 0; |
| 122 | if( !fossil_isdigit(z[6]) ) return 0; |
| 123 | if( z[7]!='-') return 0; |
| 124 | if( !fossil_isdigit(z[8]) ) return 0; |
| 125 | if( !fossil_isdigit(z[9]) ) return 0; |
| 126 | return 1; |
| 127 | } |
| 128 | |
| 129 | /* |
| 130 | ** Convert a symbolic tag name into the UUID of a check-in that contains |
| 131 | ** that tag. If the tag appears on multiple check-ins, return the UUID |
| 132 | ** of the most recent check-in with the tag. |
| 133 | ** |
| 134 | ** If the input string is of the form: |
| 135 | ** |
| 136 | ** tag:date |
| 137 | ** |
| 138 | ** Then return the UUID of the oldest check-in with that tag that is |
| 139 | ** not older than 'date'. |
| 140 | ** |
| 141 | ** An input of "tip" returns the most recent check-in. |
| 142 | ** |
| 143 | ** Memory to hold the returned string comes from malloc() and needs to |
| 144 | ** be freed by the caller. |
| 145 | */ |
| 146 | char *tag_to_uuid(const char *zTag, const char *zType){ |
| 147 | int vid; |
| 148 | char *zUuid; |
| 149 | |
| 150 | if( zType==0 || zType[0]==0 ) zType = "*"; |
| 151 | zUuid = db_text(0, |
| 152 | "SELECT blob.uuid" |
| 153 | " FROM tag, tagxref, event, blob" |
| 154 | " WHERE tag.tagname='sym-%q' " |
| 155 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 156 | " AND event.objid=tagxref.rid " |
| 157 | " AND blob.rid=event.objid " |
| 158 | " AND event.type GLOB '%q'" |
| 159 | " ORDER BY event.mtime DESC /*sort*/", |
| 160 | zTag, zType |
| 161 | ); |
| 162 | if( zUuid==0 ){ |
| 163 | int nTag = strlen(zTag); |
| 164 | int i; |
| 165 | for(i=0; i<nTag-10; i++){ |
| 166 | if( zTag[i]==':' && is_date(&zTag[i+1]) ){ |
| 167 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 168 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 169 | int nDate = strlen(zDate); |
| 170 | int useUtc = 0; |
| 171 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 172 | nDate -= 3; |
| 173 | zDate[nDate] = 0; |
| 174 | useUtc = 1; |
| 175 | } |
| 176 | zUuid = db_text(0, |
| 177 | "SELECT blob.uuid" |
| 178 | " FROM tag, tagxref, event, blob" |
| 179 | " WHERE tag.tagname='sym-%q' " |
| 180 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 181 | " AND event.objid=tagxref.rid " |
| 182 | " AND blob.rid=event.objid " |
| 183 | " AND event.mtime<=julianday(%Q %s)" |
| 184 | " AND event.type GLOB '%q'" |
| 185 | " ORDER BY event.mtime DESC /*sort*/ ", |
| 186 | zTagBase, zDate, (useUtc ? "" : ",'utc'"), zType |
| 187 | ); |
| 188 | break; |
| 189 | } |
| 190 | } |
| 191 | if( zUuid==0 && fossil_strcmp(zTag, "tip")==0 ){ |
| 192 | zUuid = db_text(0, |
| 193 | "SELECT blob.uuid" |
| 194 | " FROM event, blob" |
| 195 | " WHERE event.type='ci'" |
| 196 | " AND blob.rid=event.objid" |
| 197 | " ORDER BY event.mtime DESC" |
| 198 | ); |
| 199 | } |
| 200 | if( zUuid==0 && g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ |
| 201 | if( fossil_strcmp(zTag, "current")==0 ){ |
| 202 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); |
| 203 | }else if( fossil_strcmp(zTag, "prev")==0 |
| 204 | || fossil_strcmp(zTag, "previous")==0 ){ |
| 205 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" |
| 206 | "(SELECT pid FROM plink WHERE cid=%d AND isprim)", |
| 207 | vid); |
| 208 | }else if( fossil_strcmp(zTag, "next")==0 ){ |
| 209 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" |
| 210 | "(SELECT cid FROM plink WHERE pid=%d" |
| 211 | " ORDER BY isprim DESC, mtime DESC)", |
| 212 | vid); |
| 213 | } |
| 214 | } |
| 215 | } |
| 216 | return zUuid; |
| 217 | } |
| 218 | |
| 219 | /* |
| 220 | ** Convert a date/time string into a UUID. |
| 221 | ** |
| 222 | ** Input forms accepted: |
| 223 | ** |
| 224 | ** date:DATE |
| 225 | ** local:DATE |
| 226 | ** utc:DATE |
| 227 | ** |
| 228 | ** The DATE is interpreted as localtime unless the "utc:" prefix is used |
| 229 | ** or a "utc" string appears at the end of the DATE string. |
| 230 | */ |
| 231 | char *date_to_uuid(const char *zDate, const char *zType){ |
| 232 | int useUtc = 0; |
| 233 | int n; |
| 234 | char *zCopy = 0; |
| 235 | char *zUuid; |
| 236 | |
| 237 | if( memcmp(zDate, "date:", 5)==0 ){ |
| 238 | zDate += 5; |
| 239 | }else if( memcmp(zDate, "local:", 6)==0 ){ |
| 240 | zDate += 6; |
| 241 | }else if( memcmp(zDate, "utc:", 4)==0 ){ |
| 242 | zDate += 4; |
| 243 | useUtc = 1; |
| 244 | } |
| 245 | n = strlen(zDate); |
| 246 | if( n<10 || !is_date(zDate) ) return 0; |
| 247 | if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){ |
| 248 | zCopy = mprintf("%s", zDate); |
| 249 | zCopy[n-3] = 0; |
| 250 | zDate = zCopy; |
| 251 | n -= 3; |
| 252 | useUtc = 1; |
| 253 | } |
| 254 | if( zType==0 || zType[0]==0 ) zType = "*"; |
| 255 | zUuid = db_text(0, |
| 256 | "SELECT (SELECT uuid FROM blob WHERE rid=event.objid)" |
| 257 | " FROM event" |
| 258 | " WHERE mtime<=julianday(%Q %s) AND type GLOB '%q'" |
| 259 | " ORDER BY mtime DESC LIMIT 1", |
| 260 | zDate, useUtc ? "" : ",'utc'", zType |
| 261 | ); |
| 262 | free(zCopy); |
| 263 | return zUuid; |
| 264 | } |
| 265 | |
| 266 | /* |
| 267 | ** COMMAND: test-name-to-id |
| 268 | ** |
| @@ -284,41 +289,38 @@ | |
| 284 | blob_reset(&name); |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | /* |
| 289 | ** Convert a name to a rid. If the name is a small integer value then |
| 290 | ** just use atoi() to do the conversion. If the name contains alphabetic |
| 291 | ** characters or is not an existing rid, then use name_to_uuid then |
| 292 | ** convert the uuid to a rid. |
| 293 | ** |
| 294 | ** This routine is used by command-line routines to resolve command-line inputs |
| 295 | ** into a rid. |
| 296 | */ |
| 297 | int name_to_typed_rid(const char *zName, const char *zType){ |
| 298 | int i; |
| 299 | int rid; |
| 300 | Blob name; |
| 301 | |
| 302 | if( zName==0 || zName[0]==0 ) return 0; |
| 303 | blob_init(&name, zName, -1); |
| 304 | if( name_to_uuid(&name, -1, zType) ){ |
| 305 | blob_reset(&name); |
| 306 | for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} |
| 307 | if( zName[i]==0 ){ |
| 308 | rid = atoi(zName); |
| 309 | if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ |
| 310 | return rid; |
| 311 | } |
| 312 | } |
| 313 | fossil_error(1, "no such artifact: %s", zName); |
| 314 | return 0; |
| 315 | }else{ |
| 316 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); |
| 317 | blob_reset(&name); |
| 318 | } |
| 319 | return rid; |
| 320 | } |
| 321 | int name_to_rid(const char *zName){ |
| 322 | return name_to_typed_rid(zName, "*"); |
| 323 | } |
| 324 | |
| @@ -362,34 +364,101 @@ | |
| 362 | ** rid. If the CGI parameter is missing or is not a valid artifact tag, |
| 363 | ** return 0. If the CGI parameter is ambiguous, redirect to a page that |
| 364 | ** shows all possibilities and do not return. |
| 365 | */ |
| 366 | int name_to_rid_www(const char *zParamName){ |
| 367 | int i, rc; |
| 368 | int rid; |
| 369 | const char *zName = P(zParamName); |
| 370 | Blob name; |
| 371 | if(!zName && fossil_has_json()){ |
| 372 | zName = json_find_option_cstr(zParamName,NULL,NULL); |
| 373 | } |
| 374 | if( zName==0 || zName[0]==0 ) return 0; |
| 375 | blob_init(&name, zName, -1); |
| 376 | rc = name_to_uuid(&name, -1, "*"); |
| 377 | if( rc==1 ){ |
| 378 | blob_reset(&name); |
| 379 | for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} |
| 380 | if( zName[i]==0 ){ |
| 381 | rid = atoi(zName); |
| 382 | if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ |
| 383 | return rid; |
| 384 | } |
| 385 | } |
| 386 | return 0; |
| 387 | }else if( rc==2 ){ |
| 388 | cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); |
| 389 | return 0; |
| 390 | }else{ |
| 391 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); |
| 392 | blob_reset(&name); |
| 393 | } |
| 394 | return rid; |
| 395 | } |
| 396 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -22,10 +22,220 @@ | |
| 22 | ** not necessarily in canonical form. |
| 23 | */ |
| 24 | #include "config.h" |
| 25 | #include "name.h" |
| 26 | #include <assert.h> |
| 27 | |
| 28 | /* |
| 29 | ** Return TRUE if the string begins with something that looks roughly |
| 30 | ** like an ISO date/time string. The SQLite date/time functions will |
| 31 | ** have the final say-so about whether or not the date/time string is |
| 32 | ** well-formed. |
| 33 | */ |
| 34 | static int is_date(const char *z){ |
| 35 | if( !fossil_isdigit(z[0]) ) return 0; |
| 36 | if( !fossil_isdigit(z[1]) ) return 0; |
| 37 | if( !fossil_isdigit(z[2]) ) return 0; |
| 38 | if( !fossil_isdigit(z[3]) ) return 0; |
| 39 | if( z[4]!='-') return 0; |
| 40 | if( !fossil_isdigit(z[5]) ) return 0; |
| 41 | if( !fossil_isdigit(z[6]) ) return 0; |
| 42 | if( z[7]!='-') return 0; |
| 43 | if( !fossil_isdigit(z[8]) ) return 0; |
| 44 | if( !fossil_isdigit(z[9]) ) return 0; |
| 45 | return 1; |
| 46 | } |
| 47 | |
| 48 | /* |
| 49 | ** Convert a symbolic name into a RID. Acceptable forms: |
| 50 | ** |
| 51 | ** * SHA1 hash |
| 52 | ** * SHA1 hash prefix of at least 4 characters |
| 53 | ** * Symbolic Name |
| 54 | ** * "tag:" + symbolic name |
| 55 | ** * Date or date-time |
| 56 | ** * "date:" + Date or date-time |
| 57 | ** * symbolic-name ":" date-time |
| 58 | ** * "tip" |
| 59 | ** |
| 60 | ** The following additional forms are available in local checkouts: |
| 61 | ** |
| 62 | ** * "current" |
| 63 | ** * "prev" or "previous" |
| 64 | ** * "next" |
| 65 | ** |
| 66 | ** Return the RID of the matching artifact. Or return 0 if the name does not |
| 67 | ** match any known object. Or return -1 if the name is ambiguious. |
| 68 | ** |
| 69 | ** The zType parameter specifies the type of artifact: ci, t, w, e, g. |
| 70 | ** If zType is NULL or "" or "*" then any type of artifact will serve. |
| 71 | ** zType is "ci" in most use cases since we are usually searching for |
| 72 | ** a check-in. |
| 73 | */ |
| 74 | static int symbolic_name_to_rid(const char *zTag, const char *zType){ |
| 75 | int vid; |
| 76 | int rid = 0; |
| 77 | int nTag; |
| 78 | int i; |
| 79 | |
| 80 | if( zType==0 || zType[0]==0 ) zType = "*"; |
| 81 | if( zTag==0 || zTag[0]==0 ) return 0; |
| 82 | |
| 83 | /* special keyword: "tip" */ |
| 84 | if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){ |
| 85 | rid = db_int(0, |
| 86 | "SELECT objid" |
| 87 | " FROM event" |
| 88 | " WHERE type='ci'" |
| 89 | " ORDER BY event.mtime DESC" |
| 90 | ); |
| 91 | if( rid ) return rid; |
| 92 | } |
| 93 | |
| 94 | /* special keywords: "prev", "previous", "current", and "next" */ |
| 95 | if( g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ |
| 96 | if( fossil_strcmp(zTag, "current")==0 ){ |
| 97 | rid = vid; |
| 98 | }else if( fossil_strcmp(zTag, "prev")==0 |
| 99 | || fossil_strcmp(zTag, "previous")==0 ){ |
| 100 | rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", vid); |
| 101 | }else if( fossil_strcmp(zTag, "next")==0 ){ |
| 102 | rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d" |
| 103 | " ORDER BY isprim DESC, mtime DESC", vid); |
| 104 | } |
| 105 | if( rid ) return rid; |
| 106 | } |
| 107 | |
| 108 | /* Date and times */ |
| 109 | if( memcmp(zTag, "date:", 5)==0 ){ |
| 110 | rid = db_int(0, |
| 111 | "SELECT objid FROM event" |
| 112 | " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" |
| 113 | " ORDER BY mtime DESC LIMIT 1", |
| 114 | &zTag[5], zType); |
| 115 | return rid; |
| 116 | } |
| 117 | if( is_date(zTag) ){ |
| 118 | rid = db_int(0, |
| 119 | "SELECT objid FROM event" |
| 120 | " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" |
| 121 | " ORDER BY mtime DESC LIMIT 1", |
| 122 | zTag, zType); |
| 123 | if( rid) return rid; |
| 124 | } |
| 125 | |
| 126 | /* Deprecated date & time formats: "local:" + date-time and |
| 127 | ** "utc:" + date-time */ |
| 128 | if( memcmp(zTag, "local:", 6)==0 ){ |
| 129 | rid = db_int(0, |
| 130 | "SELECT objid FROM event" |
| 131 | " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" |
| 132 | " ORDER BY mtime DESC LIMIT 1", |
| 133 | &zTag[6], zType); |
| 134 | return rid; |
| 135 | } |
| 136 | if( memcmp(zTag, "utc:", 4)==0 ){ |
| 137 | rid = db_int(0, |
| 138 | "SELECT objid FROM event" |
| 139 | " WHERE mtime<=julianday('%qz') AND type GLOB '%q'" |
| 140 | " ORDER BY mtime DESC LIMIT 1", |
| 141 | &zTag[4], zType); |
| 142 | return rid; |
| 143 | } |
| 144 | |
| 145 | /* "tag:" + symbolic-name */ |
| 146 | if( memcmp(zTag, "tag:", 4)==0 ){ |
| 147 | rid = db_int(0, |
| 148 | "SELECT event.objid" |
| 149 | " FROM tag, tagxref, event" |
| 150 | " WHERE tag.tagname='sym-%q' " |
| 151 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 152 | " AND event.objid=tagxref.rid " |
| 153 | " AND event.type GLOB '%q'" |
| 154 | " ORDER BY event.mtime DESC /*sort*/", |
| 155 | &zTag[4], zType |
| 156 | ); |
| 157 | return rid; |
| 158 | } |
| 159 | |
| 160 | /* symbolic-name ":" date-time */ |
| 161 | nTag = strlen(zTag); |
| 162 | for(i=0; i<nTag-10 && zTag[i]!=':'; i++){} |
| 163 | if( zTag[i]==':' && is_date(&zTag[i+1]) ){ |
| 164 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 165 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 166 | int nDate = strlen(zDate); |
| 167 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 168 | zDate[nDate-3] = 'z'; |
| 169 | zDate[nDate-2] = 0; |
| 170 | } |
| 171 | rid = db_int(0, |
| 172 | "SELECT event.objid" |
| 173 | " FROM tag, tagxref, event" |
| 174 | " WHERE tag.tagname='sym-%q' " |
| 175 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 176 | " AND event.objid=tagxref.rid " |
| 177 | " AND event.mtime<=julianday(%Q)" |
| 178 | " AND event.type GLOB '%q'" |
| 179 | " ORDER BY event.mtime DESC /*sort*/ ", |
| 180 | zTagBase, zDate, zType |
| 181 | ); |
| 182 | return rid; |
| 183 | } |
| 184 | |
| 185 | /* SHA1 hash or prefix */ |
| 186 | if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){ |
| 187 | Stmt q; |
| 188 | char zUuid[UUID_SIZE+1]; |
| 189 | memcpy(zUuid, zTag, nTag+1); |
| 190 | canonical16(zUuid, nTag); |
| 191 | rid = 0; |
| 192 | if( zType[0]=='*' ){ |
| 193 | db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%s*'", zUuid); |
| 194 | }else{ |
| 195 | db_prepare(&q, |
| 196 | "SELECT blob.rid" |
| 197 | " FROM blob, event" |
| 198 | " WHERE blob.uuid GLOB '%s*'" |
| 199 | " AND event.objid=blob.rid" |
| 200 | " AND event.type GLOB '%q'", |
| 201 | zUuid, zType |
| 202 | ); |
| 203 | } |
| 204 | if( db_step(&q)==SQLITE_ROW ){ |
| 205 | rid = db_column_int(&q, 0); |
| 206 | if( db_step(&q)==SQLITE_ROW ) rid = -1; |
| 207 | } |
| 208 | db_finalize(&q); |
| 209 | if( rid ) return rid; |
| 210 | } |
| 211 | |
| 212 | /* Symbolic name */ |
| 213 | rid = db_int(0, |
| 214 | "SELECT event.objid" |
| 215 | " FROM tag, tagxref, event" |
| 216 | " WHERE tag.tagname='sym-%q' " |
| 217 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 218 | " AND event.objid=tagxref.rid " |
| 219 | " AND event.type GLOB '%q'" |
| 220 | " ORDER BY event.mtime DESC /*sort*/ ", |
| 221 | zTag, zType |
| 222 | ); |
| 223 | if( rid>0 ) return rid; |
| 224 | |
| 225 | /* Undocumented: numeric tags get translated directly into the RID */ |
| 226 | for(i=0; fossil_isdigit(zTag[i]); i++){} |
| 227 | if( zTag[i]==0 ){ |
| 228 | rid = db_int(0, |
| 229 | "SELECT event.objid" |
| 230 | " FROM event" |
| 231 | " WHERE event.objid=%s" |
| 232 | " AND event.type GLOB '%q'", zTag, zType); |
| 233 | } |
| 234 | return rid; |
| 235 | } |
| 236 | |
| 237 | |
| 238 | /* |
| 239 | ** This routine takes a user-entered UUID which might be in mixed |
| 240 | ** case and might only be a prefix of the full UUID and converts it |
| 241 | ** into the full-length UUID in canonical form. |
| @@ -41,228 +251,23 @@ | |
| 251 | ** |
| 252 | ** Return 0 on success. Return 1 if the name cannot be resolved. |
| 253 | ** Return 2 name is ambiguous. |
| 254 | */ |
| 255 | int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){ |
| 256 | char *zName = blob_str(pName); |
| 257 | int rid = symbolic_name_to_rid(zName, zType); |
| 258 | if( rid<0 ){ |
| 259 | fossil_error(iErrPriority, "ambiguous name: %s", zName); |
| 260 | return 2; |
| 261 | }else if( rid==0 ){ |
| 262 | fossil_error(iErrPriority, "not found: %s", zName); |
| 263 | return 1; |
| 264 | }else{ |
| 265 | blob_reset(pName); |
| 266 | db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 267 | return 0; |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | /* |
| 272 | ** COMMAND: test-name-to-id |
| 273 | ** |
| @@ -284,41 +289,38 @@ | |
| 289 | blob_reset(&name); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | /* |
| 294 | ** Convert a name to a rid. If the name can be any of the various forms |
| 295 | ** accepted: |
| 296 | ** |
| 297 | ** * SHA1 hash or prefix thereof |
| 298 | ** * symbolic name |
| 299 | ** * date |
| 300 | ** * label:date |
| 301 | ** * prev, previous |
| 302 | ** * next |
| 303 | ** * tip |
| 304 | ** |
| 305 | ** This routine is used by command-line routines to resolve command-line inputs |
| 306 | ** into a rid. |
| 307 | */ |
| 308 | int name_to_typed_rid(const char *zName, const char *zType){ |
| 309 | int rid; |
| 310 | |
| 311 | if( zName==0 || zName[0]==0 ) return 0; |
| 312 | rid = symbolic_name_to_rid(zName, zType); |
| 313 | if( rid<0 ){ |
| 314 | fossil_error(1, "ambiguous name: %s", zName); |
| 315 | return 0; |
| 316 | }else if( rid==0 ){ |
| 317 | fossil_error(1, "not found: %s", zName); |
| 318 | return 0; |
| 319 | }else{ |
| 320 | return rid; |
| 321 | } |
| 322 | } |
| 323 | int name_to_rid(const char *zName){ |
| 324 | return name_to_typed_rid(zName, "*"); |
| 325 | } |
| 326 | |
| @@ -362,34 +364,101 @@ | |
| 364 | ** rid. If the CGI parameter is missing or is not a valid artifact tag, |
| 365 | ** return 0. If the CGI parameter is ambiguous, redirect to a page that |
| 366 | ** shows all possibilities and do not return. |
| 367 | */ |
| 368 | int name_to_rid_www(const char *zParamName){ |
| 369 | int rid; |
| 370 | const char *zName = P(zParamName); |
| 371 | if(!zName && fossil_has_json()){ |
| 372 | zName = json_find_option_cstr(zParamName,NULL,NULL); |
| 373 | } |
| 374 | if( zName==0 || zName[0]==0 ) return 0; |
| 375 | rid = symbolic_name_to_rid(zName, "*"); |
| 376 | if( rid<0 ){ |
| 377 | cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); |
| 378 | rid = 0; |
| 379 | } |
| 380 | return rid; |
| 381 | } |
| 382 | |
| 383 | /* |
| 384 | ** COMMAND: whatis* |
| 385 | ** Usage: %fossil whatis NAME |
| 386 | ** |
| 387 | ** Resolve the symbol NAME into its canonical 40-character SHA1-hash |
| 388 | ** artifact name and provide a description of what role that artifact |
| 389 | ** plays. |
| 390 | */ |
| 391 | void whatis_cmd(void){ |
| 392 | int rid; |
| 393 | const char *zName; |
| 394 | int fExtra; |
| 395 | db_find_and_open_repository(0,0); |
| 396 | fExtra = find_option("verbose","v",0)!=0; |
| 397 | if( g.argc!=3 ) usage("whatis NAME"); |
| 398 | zName = g.argv[2]; |
| 399 | rid = symbolic_name_to_rid(zName, 0); |
| 400 | if( rid<0 ){ |
| 401 | fossil_print("Ambiguous artifact name prefix: %s\n", zName); |
| 402 | }else if( rid==0 ){ |
| 403 | fossil_print("Unknown artifact: %s\n", zName); |
| 404 | }else{ |
| 405 | Stmt q; |
| 406 | db_prepare(&q, "SELECT uuid, size, datetime(mtime, 'localtime'), ipaddr" |
| 407 | " FROM blob, rcvfrom" |
| 408 | " WHERE rid=%d" |
| 409 | " AND rcvfrom.rcvid=blob.rcvid", |
| 410 | rid); |
| 411 | if( db_step(&q)==SQLITE_ROW ){ |
| 412 | if( fExtra ){ |
| 413 | fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid); |
| 414 | fossil_print("size: %d bytes\n", db_column_int(&q,1)); |
| 415 | fossil_print("received: %s from %s\n", |
| 416 | db_column_text(&q, 2), |
| 417 | db_column_text(&q, 3)); |
| 418 | }else{ |
| 419 | fossil_print("artifact: %s\n", db_column_text(&q,0)); |
| 420 | fossil_print("size: %d bytes\n", db_column_int(&q,1)); |
| 421 | } |
| 422 | } |
| 423 | db_finalize(&q); |
| 424 | db_prepare(&q, |
| 425 | "SELECT type, datetime(mtime,'localtime')," |
| 426 | " coalesce(euser,user), coalesce(ecomment,comment)" |
| 427 | " FROM event WHERE objid=%d", rid); |
| 428 | if( db_step(&q)==SQLITE_ROW ){ |
| 429 | const char *zType; |
| 430 | switch( db_column_text(&q,0)[0] ){ |
| 431 | case 'c': zType = "Check-in"; break; |
| 432 | case 'w': zType = "Wiki-edit"; break; |
| 433 | case 'e': zType = "Event"; break; |
| 434 | case 't': zType = "Ticket-change"; break; |
| 435 | case 'g': zType = "Tag-change"; break; |
| 436 | } |
| 437 | fossil_print("type: %s by %s on %s\n", zType, db_column_text(&q,2), |
| 438 | db_column_text(&q, 1)); |
| 439 | fossil_print("comment: "); |
| 440 | comment_print(db_column_text(&q,3), 10, 78); |
| 441 | } |
| 442 | db_finalize(&q); |
| 443 | db_prepare(&q, |
| 444 | "SELECT filename.name, blob.uuid, datetime(event.mtime,'localtime')," |
| 445 | " coalesce(euser,user), coalesce(ecomment,comment)" |
| 446 | " FROM mlink, filename, blob, event" |
| 447 | " WHERE mlink.fid=%d" |
| 448 | " AND filename.fnid=mlink.fnid" |
| 449 | " AND event.objid=mlink.mid" |
| 450 | " AND blob.rid=mlink.mid" |
| 451 | " ORDER BY event.mtime DESC /*sort*/", |
| 452 | rid); |
| 453 | while( db_step(&q)==SQLITE_ROW ){ |
| 454 | fossil_print("file: %s\n", db_column_text(&q,0)); |
| 455 | fossil_print(" part of [%.10s] by %s on %s\n", |
| 456 | db_column_text(&q, 1), |
| 457 | db_column_text(&q, 3), |
| 458 | db_column_text(&q, 2)); |
| 459 | fossil_print(" "); |
| 460 | comment_print(db_column_text(&q,4), 10, 78); |
| 461 | } |
| 462 | db_finalize(&q); |
| 463 | } |
| 464 | } |
| 465 |
+329
-260
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -22,10 +22,220 @@ | ||
| 22 | 22 | ** not necessarily in canonical form. |
| 23 | 23 | */ |
| 24 | 24 | #include "config.h" |
| 25 | 25 | #include "name.h" |
| 26 | 26 | #include <assert.h> |
| 27 | + | |
| 28 | +/* | |
| 29 | +** Return TRUE if the string begins with something that looks roughly | |
| 30 | +** like an ISO date/time string. The SQLite date/time functions will | |
| 31 | +** have the final say-so about whether or not the date/time string is | |
| 32 | +** well-formed. | |
| 33 | +*/ | |
| 34 | +static int is_date(const char *z){ | |
| 35 | + if( !fossil_isdigit(z[0]) ) return 0; | |
| 36 | + if( !fossil_isdigit(z[1]) ) return 0; | |
| 37 | + if( !fossil_isdigit(z[2]) ) return 0; | |
| 38 | + if( !fossil_isdigit(z[3]) ) return 0; | |
| 39 | + if( z[4]!='-') return 0; | |
| 40 | + if( !fossil_isdigit(z[5]) ) return 0; | |
| 41 | + if( !fossil_isdigit(z[6]) ) return 0; | |
| 42 | + if( z[7]!='-') return 0; | |
| 43 | + if( !fossil_isdigit(z[8]) ) return 0; | |
| 44 | + if( !fossil_isdigit(z[9]) ) return 0; | |
| 45 | + return 1; | |
| 46 | +} | |
| 47 | + | |
| 48 | +/* | |
| 49 | +** Convert a symbolic name into a RID. Acceptable forms: | |
| 50 | +** | |
| 51 | +** * SHA1 hash | |
| 52 | +** * SHA1 hash prefix of at least 4 characters | |
| 53 | +** * Symbolic Name | |
| 54 | +** * "tag:" + symbolic name | |
| 55 | +** * Date or date-time | |
| 56 | +** * "date:" + Date or date-time | |
| 57 | +** * symbolic-name ":" date-time | |
| 58 | +** * "tip" | |
| 59 | +** | |
| 60 | +** The following additional forms are available in local checkouts: | |
| 61 | +** | |
| 62 | +** * "current" | |
| 63 | +** * "prev" or "previous" | |
| 64 | +** * "next" | |
| 65 | +** | |
| 66 | +** Return the RID of the matching artifact. Or return 0 if the name does not | |
| 67 | +** match any known object. Or return -1 if the name is ambiguious. | |
| 68 | +** | |
| 69 | +** The zType parameter specifies the type of artifact: ci, t, w, e, g. | |
| 70 | +** If zType is NULL or "" or "*" then any type of artifact will serve. | |
| 71 | +** zType is "ci" in most use cases since we are usually searching for | |
| 72 | +** a check-in. | |
| 73 | +*/ | |
| 74 | +static int symbolic_name_to_rid(const char *zTag, const char *zType){ | |
| 75 | + int vid; | |
| 76 | + int rid = 0; | |
| 77 | + int nTag; | |
| 78 | + int i; | |
| 79 | + | |
| 80 | + if( zType==0 || zType[0]==0 ) zType = "*"; | |
| 81 | + if( zTag==0 || zTag[0]==0 ) return 0; | |
| 82 | + | |
| 83 | + /* special keyword: "tip" */ | |
| 84 | + if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){ | |
| 85 | + rid = db_int(0, | |
| 86 | + "SELECT objid" | |
| 87 | + " FROM event" | |
| 88 | + " WHERE type='ci'" | |
| 89 | + " ORDER BY event.mtime DESC" | |
| 90 | + ); | |
| 91 | + if( rid ) return rid; | |
| 92 | + } | |
| 93 | + | |
| 94 | + /* special keywords: "prev", "previous", "current", and "next" */ | |
| 95 | + if( g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ | |
| 96 | + if( fossil_strcmp(zTag, "current")==0 ){ | |
| 97 | + rid = vid; | |
| 98 | + }else if( fossil_strcmp(zTag, "prev")==0 | |
| 99 | + || fossil_strcmp(zTag, "previous")==0 ){ | |
| 100 | + rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", vid); | |
| 101 | + }else if( fossil_strcmp(zTag, "next")==0 ){ | |
| 102 | + rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d" | |
| 103 | + " ORDER BY isprim DESC, mtime DESC", vid); | |
| 104 | + } | |
| 105 | + if( rid ) return rid; | |
| 106 | + } | |
| 107 | + | |
| 108 | + /* Date and times */ | |
| 109 | + if( memcmp(zTag, "date:", 5)==0 ){ | |
| 110 | + rid = db_int(0, | |
| 111 | + "SELECT objid FROM event" | |
| 112 | + " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" | |
| 113 | + " ORDER BY mtime DESC LIMIT 1", | |
| 114 | + &zTag[5], zType); | |
| 115 | + return rid; | |
| 116 | + } | |
| 117 | + if( is_date(zTag) ){ | |
| 118 | + rid = db_int(0, | |
| 119 | + "SELECT objid FROM event" | |
| 120 | + " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" | |
| 121 | + " ORDER BY mtime DESC LIMIT 1", | |
| 122 | + zTag, zType); | |
| 123 | + if( rid) return rid; | |
| 124 | + } | |
| 125 | + | |
| 126 | + /* Deprecated date & time formats: "local:" + date-time and | |
| 127 | + ** "utc:" + date-time */ | |
| 128 | + if( memcmp(zTag, "local:", 6)==0 ){ | |
| 129 | + rid = db_int(0, | |
| 130 | + "SELECT objid FROM event" | |
| 131 | + " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" | |
| 132 | + " ORDER BY mtime DESC LIMIT 1", | |
| 133 | + &zTag[6], zType); | |
| 134 | + return rid; | |
| 135 | + } | |
| 136 | + if( memcmp(zTag, "utc:", 4)==0 ){ | |
| 137 | + rid = db_int(0, | |
| 138 | + "SELECT objid FROM event" | |
| 139 | + " WHERE mtime<=julianday('%qz') AND type GLOB '%q'" | |
| 140 | + " ORDER BY mtime DESC LIMIT 1", | |
| 141 | + &zTag[4], zType); | |
| 142 | + return rid; | |
| 143 | + } | |
| 144 | + | |
| 145 | + /* "tag:" + symbolic-name */ | |
| 146 | + if( memcmp(zTag, "tag:", 4)==0 ){ | |
| 147 | + rid = db_int(0, | |
| 148 | + "SELECT event.objid" | |
| 149 | + " FROM tag, tagxref, event" | |
| 150 | + " WHERE tag.tagname='sym-%q' " | |
| 151 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 152 | + " AND event.objid=tagxref.rid " | |
| 153 | + " AND event.type GLOB '%q'" | |
| 154 | + " ORDER BY event.mtime DESC /*sort*/", | |
| 155 | + &zTag[4], zType | |
| 156 | + ); | |
| 157 | + return rid; | |
| 158 | + } | |
| 159 | + | |
| 160 | + /* symbolic-name ":" date-time */ | |
| 161 | + nTag = strlen(zTag); | |
| 162 | + for(i=0; i<nTag-10 && zTag[i]!=':'; i++){} | |
| 163 | + if( zTag[i]==':' && is_date(&zTag[i+1]) ){ | |
| 164 | + char *zDate = mprintf("%s", &zTag[i+1]); | |
| 165 | + char *zTagBase = mprintf("%.*s", i, zTag); | |
| 166 | + int nDate = strlen(zDate); | |
| 167 | + if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ | |
| 168 | + zDate[nDate-3] = 'z'; | |
| 169 | + zDate[nDate-2] = 0; | |
| 170 | + } | |
| 171 | + rid = db_int(0, | |
| 172 | + "SELECT event.objid" | |
| 173 | + " FROM tag, tagxref, event" | |
| 174 | + " WHERE tag.tagname='sym-%q' " | |
| 175 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 176 | + " AND event.objid=tagxref.rid " | |
| 177 | + " AND event.mtime<=julianday(%Q)" | |
| 178 | + " AND event.type GLOB '%q'" | |
| 179 | + " ORDER BY event.mtime DESC /*sort*/ ", | |
| 180 | + zTagBase, zDate, zType | |
| 181 | + ); | |
| 182 | + return rid; | |
| 183 | + } | |
| 184 | + | |
| 185 | + /* SHA1 hash or prefix */ | |
| 186 | + if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){ | |
| 187 | + Stmt q; | |
| 188 | + char zUuid[UUID_SIZE+1]; | |
| 189 | + memcpy(zUuid, zTag, nTag+1); | |
| 190 | + canonical16(zUuid, nTag); | |
| 191 | + rid = 0; | |
| 192 | + if( zType[0]=='*' ){ | |
| 193 | + db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%s*'", zUuid); | |
| 194 | + }else{ | |
| 195 | + db_prepare(&q, | |
| 196 | + "SELECT blob.rid" | |
| 197 | + " FROM blob, event" | |
| 198 | + " WHERE blob.uuid GLOB '%s*'" | |
| 199 | + " AND event.objid=blob.rid" | |
| 200 | + " AND event.type GLOB '%q'", | |
| 201 | + zUuid, zType | |
| 202 | + ); | |
| 203 | + } | |
| 204 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 205 | + rid = db_column_int(&q, 0); | |
| 206 | + if( db_step(&q)==SQLITE_ROW ) rid = -1; | |
| 207 | + } | |
| 208 | + db_finalize(&q); | |
| 209 | + if( rid ) return rid; | |
| 210 | + } | |
| 211 | + | |
| 212 | + /* Symbolic name */ | |
| 213 | + rid = db_int(0, | |
| 214 | + "SELECT event.objid" | |
| 215 | + " FROM tag, tagxref, event" | |
| 216 | + " WHERE tag.tagname='sym-%q' " | |
| 217 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 218 | + " AND event.objid=tagxref.rid " | |
| 219 | + " AND event.type GLOB '%q'" | |
| 220 | + " ORDER BY event.mtime DESC /*sort*/ ", | |
| 221 | + zTag, zType | |
| 222 | + ); | |
| 223 | + if( rid>0 ) return rid; | |
| 224 | + | |
| 225 | + /* Undocumented: numeric tags get translated directly into the RID */ | |
| 226 | + for(i=0; fossil_isdigit(zTag[i]); i++){} | |
| 227 | + if( zTag[i]==0 ){ | |
| 228 | + rid = db_int(0, | |
| 229 | + "SELECT event.objid" | |
| 230 | + " FROM event" | |
| 231 | + " WHERE event.objid=%s" | |
| 232 | + " AND event.type GLOB '%q'", zTag, zType); | |
| 233 | + } | |
| 234 | + return rid; | |
| 235 | +} | |
| 236 | + | |
| 27 | 237 | |
| 28 | 238 | /* |
| 29 | 239 | ** This routine takes a user-entered UUID which might be in mixed |
| 30 | 240 | ** case and might only be a prefix of the full UUID and converts it |
| 31 | 241 | ** into the full-length UUID in canonical form. |
| @@ -41,228 +251,23 @@ | ||
| 41 | 251 | ** |
| 42 | 252 | ** Return 0 on success. Return 1 if the name cannot be resolved. |
| 43 | 253 | ** Return 2 name is ambiguous. |
| 44 | 254 | */ |
| 45 | 255 | int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){ |
| 46 | - int rc; | |
| 47 | - int sz; | |
| 48 | - sz = blob_size(pName); | |
| 49 | - if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){ | |
| 50 | - char *zUuid; | |
| 51 | - const char *zName = blob_str(pName); | |
| 52 | - if( memcmp(zName, "tag:", 4)==0 ){ | |
| 53 | - zName += 4; | |
| 54 | - zUuid = tag_to_uuid(zName, zType); | |
| 55 | - }else{ | |
| 56 | - zUuid = tag_to_uuid(zName, zType); | |
| 57 | - if( zUuid==0 ){ | |
| 58 | - zUuid = date_to_uuid(zName, zType); | |
| 59 | - } | |
| 60 | - } | |
| 61 | - if( zUuid ){ | |
| 62 | - blob_reset(pName); | |
| 63 | - blob_append(pName, zUuid, -1); | |
| 64 | - free(zUuid); | |
| 65 | - return 0; | |
| 66 | - } | |
| 67 | - fossil_error(iErrPriority, "not a valid object name: %s", zName); | |
| 68 | - return 1; | |
| 69 | - } | |
| 70 | - blob_materialize(pName); | |
| 71 | - canonical16(blob_buffer(pName), sz); | |
| 72 | - if( sz==UUID_SIZE ){ | |
| 73 | - rc = db_int(1, "SELECT 0 FROM blob WHERE uuid=%B", pName); | |
| 74 | - if( rc ){ | |
| 75 | - fossil_error(iErrPriority, "no such artifact: %b", pName); | |
| 76 | - blob_reset(pName); | |
| 77 | - } | |
| 78 | - }else if( sz<UUID_SIZE && sz>=4 ){ | |
| 79 | - Stmt q; | |
| 80 | - db_prepare(&q, "SELECT uuid FROM blob WHERE uuid GLOB '%b*'", pName); | |
| 81 | - if( db_step(&q)!=SQLITE_ROW ){ | |
| 82 | - char *zUuid; | |
| 83 | - db_finalize(&q); | |
| 84 | - zUuid = tag_to_uuid(blob_str(pName), "*"); | |
| 85 | - if( zUuid ){ | |
| 86 | - blob_reset(pName); | |
| 87 | - blob_append(pName, zUuid, -1); | |
| 88 | - free(zUuid); | |
| 89 | - return 0; | |
| 90 | - } | |
| 91 | - fossil_error(iErrPriority, "no artifacts match the prefix \"%b\"", pName); | |
| 92 | - return 1; | |
| 93 | - } | |
| 94 | - blob_reset(pName); | |
| 95 | - blob_append(pName, db_column_text(&q, 0), db_column_bytes(&q, 0)); | |
| 96 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 97 | - fossil_error(iErrPriority, | |
| 98 | - "multiple artifacts match" | |
| 99 | - ); | |
| 100 | - blob_reset(pName); | |
| 101 | - db_finalize(&q); | |
| 102 | - return 2; | |
| 103 | - } | |
| 104 | - db_finalize(&q); | |
| 105 | - rc = 0; | |
| 106 | - }else{ | |
| 107 | - rc = 0; | |
| 108 | - } | |
| 109 | - return rc; | |
| 110 | -} | |
| 111 | - | |
| 112 | -/* | |
| 113 | -** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. | |
| 114 | -*/ | |
| 115 | -static int is_date(const char *z){ | |
| 116 | - if( !fossil_isdigit(z[0]) ) return 0; | |
| 117 | - if( !fossil_isdigit(z[1]) ) return 0; | |
| 118 | - if( !fossil_isdigit(z[2]) ) return 0; | |
| 119 | - if( !fossil_isdigit(z[3]) ) return 0; | |
| 120 | - if( z[4]!='-') return 0; | |
| 121 | - if( !fossil_isdigit(z[5]) ) return 0; | |
| 122 | - if( !fossil_isdigit(z[6]) ) return 0; | |
| 123 | - if( z[7]!='-') return 0; | |
| 124 | - if( !fossil_isdigit(z[8]) ) return 0; | |
| 125 | - if( !fossil_isdigit(z[9]) ) return 0; | |
| 126 | - return 1; | |
| 127 | -} | |
| 128 | - | |
| 129 | -/* | |
| 130 | -** Convert a symbolic tag name into the UUID of a check-in that contains | |
| 131 | -** that tag. If the tag appears on multiple check-ins, return the UUID | |
| 132 | -** of the most recent check-in with the tag. | |
| 133 | -** | |
| 134 | -** If the input string is of the form: | |
| 135 | -** | |
| 136 | -** tag:date | |
| 137 | -** | |
| 138 | -** Then return the UUID of the oldest check-in with that tag that is | |
| 139 | -** not older than 'date'. | |
| 140 | -** | |
| 141 | -** An input of "tip" returns the most recent check-in. | |
| 142 | -** | |
| 143 | -** Memory to hold the returned string comes from malloc() and needs to | |
| 144 | -** be freed by the caller. | |
| 145 | -*/ | |
| 146 | -char *tag_to_uuid(const char *zTag, const char *zType){ | |
| 147 | - int vid; | |
| 148 | - char *zUuid; | |
| 149 | - | |
| 150 | - if( zType==0 || zType[0]==0 ) zType = "*"; | |
| 151 | - zUuid = db_text(0, | |
| 152 | - "SELECT blob.uuid" | |
| 153 | - " FROM tag, tagxref, event, blob" | |
| 154 | - " WHERE tag.tagname='sym-%q' " | |
| 155 | - " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 156 | - " AND event.objid=tagxref.rid " | |
| 157 | - " AND blob.rid=event.objid " | |
| 158 | - " AND event.type GLOB '%q'" | |
| 159 | - " ORDER BY event.mtime DESC /*sort*/", | |
| 160 | - zTag, zType | |
| 161 | - ); | |
| 162 | - if( zUuid==0 ){ | |
| 163 | - int nTag = strlen(zTag); | |
| 164 | - int i; | |
| 165 | - for(i=0; i<nTag-10; i++){ | |
| 166 | - if( zTag[i]==':' && is_date(&zTag[i+1]) ){ | |
| 167 | - char *zDate = mprintf("%s", &zTag[i+1]); | |
| 168 | - char *zTagBase = mprintf("%.*s", i, zTag); | |
| 169 | - int nDate = strlen(zDate); | |
| 170 | - int useUtc = 0; | |
| 171 | - if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ | |
| 172 | - nDate -= 3; | |
| 173 | - zDate[nDate] = 0; | |
| 174 | - useUtc = 1; | |
| 175 | - } | |
| 176 | - zUuid = db_text(0, | |
| 177 | - "SELECT blob.uuid" | |
| 178 | - " FROM tag, tagxref, event, blob" | |
| 179 | - " WHERE tag.tagname='sym-%q' " | |
| 180 | - " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 181 | - " AND event.objid=tagxref.rid " | |
| 182 | - " AND blob.rid=event.objid " | |
| 183 | - " AND event.mtime<=julianday(%Q %s)" | |
| 184 | - " AND event.type GLOB '%q'" | |
| 185 | - " ORDER BY event.mtime DESC /*sort*/ ", | |
| 186 | - zTagBase, zDate, (useUtc ? "" : ",'utc'"), zType | |
| 187 | - ); | |
| 188 | - break; | |
| 189 | - } | |
| 190 | - } | |
| 191 | - if( zUuid==0 && fossil_strcmp(zTag, "tip")==0 ){ | |
| 192 | - zUuid = db_text(0, | |
| 193 | - "SELECT blob.uuid" | |
| 194 | - " FROM event, blob" | |
| 195 | - " WHERE event.type='ci'" | |
| 196 | - " AND blob.rid=event.objid" | |
| 197 | - " ORDER BY event.mtime DESC" | |
| 198 | - ); | |
| 199 | - } | |
| 200 | - if( zUuid==0 && g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ | |
| 201 | - if( fossil_strcmp(zTag, "current")==0 ){ | |
| 202 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); | |
| 203 | - }else if( fossil_strcmp(zTag, "prev")==0 | |
| 204 | - || fossil_strcmp(zTag, "previous")==0 ){ | |
| 205 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" | |
| 206 | - "(SELECT pid FROM plink WHERE cid=%d AND isprim)", | |
| 207 | - vid); | |
| 208 | - }else if( fossil_strcmp(zTag, "next")==0 ){ | |
| 209 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" | |
| 210 | - "(SELECT cid FROM plink WHERE pid=%d" | |
| 211 | - " ORDER BY isprim DESC, mtime DESC)", | |
| 212 | - vid); | |
| 213 | - } | |
| 214 | - } | |
| 215 | - } | |
| 216 | - return zUuid; | |
| 217 | -} | |
| 218 | - | |
| 219 | -/* | |
| 220 | -** Convert a date/time string into a UUID. | |
| 221 | -** | |
| 222 | -** Input forms accepted: | |
| 223 | -** | |
| 224 | -** date:DATE | |
| 225 | -** local:DATE | |
| 226 | -** utc:DATE | |
| 227 | -** | |
| 228 | -** The DATE is interpreted as localtime unless the "utc:" prefix is used | |
| 229 | -** or a "utc" string appears at the end of the DATE string. | |
| 230 | -*/ | |
| 231 | -char *date_to_uuid(const char *zDate, const char *zType){ | |
| 232 | - int useUtc = 0; | |
| 233 | - int n; | |
| 234 | - char *zCopy = 0; | |
| 235 | - char *zUuid; | |
| 236 | - | |
| 237 | - if( memcmp(zDate, "date:", 5)==0 ){ | |
| 238 | - zDate += 5; | |
| 239 | - }else if( memcmp(zDate, "local:", 6)==0 ){ | |
| 240 | - zDate += 6; | |
| 241 | - }else if( memcmp(zDate, "utc:", 4)==0 ){ | |
| 242 | - zDate += 4; | |
| 243 | - useUtc = 1; | |
| 244 | - } | |
| 245 | - n = strlen(zDate); | |
| 246 | - if( n<10 || !is_date(zDate) ) return 0; | |
| 247 | - if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){ | |
| 248 | - zCopy = mprintf("%s", zDate); | |
| 249 | - zCopy[n-3] = 0; | |
| 250 | - zDate = zCopy; | |
| 251 | - n -= 3; | |
| 252 | - useUtc = 1; | |
| 253 | - } | |
| 254 | - if( zType==0 || zType[0]==0 ) zType = "*"; | |
| 255 | - zUuid = db_text(0, | |
| 256 | - "SELECT (SELECT uuid FROM blob WHERE rid=event.objid)" | |
| 257 | - " FROM event" | |
| 258 | - " WHERE mtime<=julianday(%Q %s) AND type GLOB '%q'" | |
| 259 | - " ORDER BY mtime DESC LIMIT 1", | |
| 260 | - zDate, useUtc ? "" : ",'utc'", zType | |
| 261 | - ); | |
| 262 | - free(zCopy); | |
| 263 | - return zUuid; | |
| 256 | + char *zName = blob_str(pName); | |
| 257 | + int rid = symbolic_name_to_rid(zName, zType); | |
| 258 | + if( rid<0 ){ | |
| 259 | + fossil_error(iErrPriority, "ambiguous name: %s", zName); | |
| 260 | + return 2; | |
| 261 | + }else if( rid==0 ){ | |
| 262 | + fossil_error(iErrPriority, "not found: %s", zName); | |
| 263 | + return 1; | |
| 264 | + }else{ | |
| 265 | + blob_reset(pName); | |
| 266 | + db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 267 | + return 0; | |
| 268 | + } | |
| 264 | 269 | } |
| 265 | 270 | |
| 266 | 271 | /* |
| 267 | 272 | ** COMMAND: test-name-to-id |
| 268 | 273 | ** |
| @@ -284,41 +289,38 @@ | ||
| 284 | 289 | blob_reset(&name); |
| 285 | 290 | } |
| 286 | 291 | } |
| 287 | 292 | |
| 288 | 293 | /* |
| 289 | -** Convert a name to a rid. If the name is a small integer value then | |
| 290 | -** just use atoi() to do the conversion. If the name contains alphabetic | |
| 291 | -** characters or is not an existing rid, then use name_to_uuid then | |
| 292 | -** convert the uuid to a rid. | |
| 294 | +** Convert a name to a rid. If the name can be any of the various forms | |
| 295 | +** accepted: | |
| 296 | +** | |
| 297 | +** * SHA1 hash or prefix thereof | |
| 298 | +** * symbolic name | |
| 299 | +** * date | |
| 300 | +** * label:date | |
| 301 | +** * prev, previous | |
| 302 | +** * next | |
| 303 | +** * tip | |
| 293 | 304 | ** |
| 294 | 305 | ** This routine is used by command-line routines to resolve command-line inputs |
| 295 | 306 | ** into a rid. |
| 296 | 307 | */ |
| 297 | 308 | int name_to_typed_rid(const char *zName, const char *zType){ |
| 298 | - int i; | |
| 299 | 309 | int rid; |
| 300 | - Blob name; | |
| 301 | 310 | |
| 302 | 311 | if( zName==0 || zName[0]==0 ) return 0; |
| 303 | - blob_init(&name, zName, -1); | |
| 304 | - if( name_to_uuid(&name, -1, zType) ){ | |
| 305 | - blob_reset(&name); | |
| 306 | - for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} | |
| 307 | - if( zName[i]==0 ){ | |
| 308 | - rid = atoi(zName); | |
| 309 | - if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ | |
| 310 | - return rid; | |
| 311 | - } | |
| 312 | - } | |
| 313 | - fossil_error(1, "no such artifact: %s", zName); | |
| 312 | + rid = symbolic_name_to_rid(zName, zType); | |
| 313 | + if( rid<0 ){ | |
| 314 | + fossil_error(1, "ambiguous name: %s", zName); | |
| 315 | + return 0; | |
| 316 | + }else if( rid==0 ){ | |
| 317 | + fossil_error(1, "not found: %s", zName); | |
| 314 | 318 | return 0; |
| 315 | 319 | }else{ |
| 316 | - rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); | |
| 317 | - blob_reset(&name); | |
| 320 | + return rid; | |
| 318 | 321 | } |
| 319 | - return rid; | |
| 320 | 322 | } |
| 321 | 323 | int name_to_rid(const char *zName){ |
| 322 | 324 | return name_to_typed_rid(zName, "*"); |
| 323 | 325 | } |
| 324 | 326 | |
| @@ -362,34 +364,101 @@ | ||
| 362 | 364 | ** rid. If the CGI parameter is missing or is not a valid artifact tag, |
| 363 | 365 | ** return 0. If the CGI parameter is ambiguous, redirect to a page that |
| 364 | 366 | ** shows all possibilities and do not return. |
| 365 | 367 | */ |
| 366 | 368 | int name_to_rid_www(const char *zParamName){ |
| 367 | - int i, rc; | |
| 368 | 369 | int rid; |
| 369 | 370 | const char *zName = P(zParamName); |
| 370 | - Blob name; | |
| 371 | 371 | if(!zName && fossil_has_json()){ |
| 372 | 372 | zName = json_find_option_cstr(zParamName,NULL,NULL); |
| 373 | 373 | } |
| 374 | 374 | if( zName==0 || zName[0]==0 ) return 0; |
| 375 | - blob_init(&name, zName, -1); | |
| 376 | - rc = name_to_uuid(&name, -1, "*"); | |
| 377 | - if( rc==1 ){ | |
| 378 | - blob_reset(&name); | |
| 379 | - for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} | |
| 380 | - if( zName[i]==0 ){ | |
| 381 | - rid = atoi(zName); | |
| 382 | - if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ | |
| 383 | - return rid; | |
| 384 | - } | |
| 385 | - } | |
| 386 | - return 0; | |
| 387 | - }else if( rc==2 ){ | |
| 388 | - cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); | |
| 389 | - return 0; | |
| 390 | - }else{ | |
| 391 | - rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); | |
| 392 | - blob_reset(&name); | |
| 393 | - } | |
| 394 | - return rid; | |
| 375 | + rid = symbolic_name_to_rid(zName, "*"); | |
| 376 | + if( rid<0 ){ | |
| 377 | + cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); | |
| 378 | + rid = 0; | |
| 379 | + } | |
| 380 | + return rid; | |
| 381 | +} | |
| 382 | + | |
| 383 | +/* | |
| 384 | +** COMMAND: whatis* | |
| 385 | +** Usage: %fossil whatis NAME | |
| 386 | +** | |
| 387 | +** Resolve the symbol NAME into its canonical 40-character SHA1-hash | |
| 388 | +** artifact name and provide a description of what role that artifact | |
| 389 | +** plays. | |
| 390 | +*/ | |
| 391 | +void whatis_cmd(void){ | |
| 392 | + int rid; | |
| 393 | + const char *zName; | |
| 394 | + int fExtra; | |
| 395 | + db_find_and_open_repository(0,0); | |
| 396 | + fExtra = find_option("verbose","v",0)!=0; | |
| 397 | + if( g.argc!=3 ) usage("whatis NAME"); | |
| 398 | + zName = g.argv[2]; | |
| 399 | + rid = symbolic_name_to_rid(zName, 0); | |
| 400 | + if( rid<0 ){ | |
| 401 | + fossil_print("Ambiguous artifact name prefix: %s\n", zName); | |
| 402 | + }else if( rid==0 ){ | |
| 403 | + fossil_print("Unknown artifact: %s\n", zName); | |
| 404 | + }else{ | |
| 405 | + Stmt q; | |
| 406 | + db_prepare(&q, "SELECT uuid, size, datetime(mtime, 'localtime'), ipaddr" | |
| 407 | + " FROM blob, rcvfrom" | |
| 408 | + " WHERE rid=%d" | |
| 409 | + " AND rcvfrom.rcvid=blob.rcvid", | |
| 410 | + rid); | |
| 411 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 412 | + if( fExtra ){ | |
| 413 | + fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid); | |
| 414 | + fossil_print("size: %d bytes\n", db_column_int(&q,1)); | |
| 415 | + fossil_print("received: %s from %s\n", | |
| 416 | + db_column_text(&q, 2), | |
| 417 | + db_column_text(&q, 3)); | |
| 418 | + }else{ | |
| 419 | + fossil_print("artifact: %s\n", db_column_text(&q,0)); | |
| 420 | + fossil_print("size: %d bytes\n", db_column_int(&q,1)); | |
| 421 | + } | |
| 422 | + } | |
| 423 | + db_finalize(&q); | |
| 424 | + db_prepare(&q, | |
| 425 | + "SELECT type, datetime(mtime,'localtime')," | |
| 426 | + " coalesce(euser,user), coalesce(ecomment,comment)" | |
| 427 | + " FROM event WHERE objid=%d", rid); | |
| 428 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 429 | + const char *zType; | |
| 430 | + switch( db_column_text(&q,0)[0] ){ | |
| 431 | + case 'c': zType = "Check-in"; break; | |
| 432 | + case 'w': zType = "Wiki-edit"; break; | |
| 433 | + case 'e': zType = "Event"; break; | |
| 434 | + case 't': zType = "Ticket-change"; break; | |
| 435 | + case 'g': zType = "Tag-change"; break; | |
| 436 | + } | |
| 437 | + fossil_print("type: %s by %s on %s\n", zType, db_column_text(&q,2), | |
| 438 | + db_column_text(&q, 1)); | |
| 439 | + fossil_print("comment: "); | |
| 440 | + comment_print(db_column_text(&q,3), 10, 78); | |
| 441 | + } | |
| 442 | + db_finalize(&q); | |
| 443 | + db_prepare(&q, | |
| 444 | + "SELECT filename.name, blob.uuid, datetime(event.mtime,'localtime')," | |
| 445 | + " coalesce(euser,user), coalesce(ecomment,comment)" | |
| 446 | + " FROM mlink, filename, blob, event" | |
| 447 | + " WHERE mlink.fid=%d" | |
| 448 | + " AND filename.fnid=mlink.fnid" | |
| 449 | + " AND event.objid=mlink.mid" | |
| 450 | + " AND blob.rid=mlink.mid" | |
| 451 | + " ORDER BY event.mtime DESC /*sort*/", | |
| 452 | + rid); | |
| 453 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 454 | + fossil_print("file: %s\n", db_column_text(&q,0)); | |
| 455 | + fossil_print(" part of [%.10s] by %s on %s\n", | |
| 456 | + db_column_text(&q, 1), | |
| 457 | + db_column_text(&q, 3), | |
| 458 | + db_column_text(&q, 2)); | |
| 459 | + fossil_print(" "); | |
| 460 | + comment_print(db_column_text(&q,4), 10, 78); | |
| 461 | + } | |
| 462 | + db_finalize(&q); | |
| 463 | + } | |
| 395 | 464 | } |
| 396 | 465 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -22,10 +22,220 @@ | |
| 22 | ** not necessarily in canonical form. |
| 23 | */ |
| 24 | #include "config.h" |
| 25 | #include "name.h" |
| 26 | #include <assert.h> |
| 27 | |
| 28 | /* |
| 29 | ** This routine takes a user-entered UUID which might be in mixed |
| 30 | ** case and might only be a prefix of the full UUID and converts it |
| 31 | ** into the full-length UUID in canonical form. |
| @@ -41,228 +251,23 @@ | |
| 41 | ** |
| 42 | ** Return 0 on success. Return 1 if the name cannot be resolved. |
| 43 | ** Return 2 name is ambiguous. |
| 44 | */ |
| 45 | int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){ |
| 46 | int rc; |
| 47 | int sz; |
| 48 | sz = blob_size(pName); |
| 49 | if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){ |
| 50 | char *zUuid; |
| 51 | const char *zName = blob_str(pName); |
| 52 | if( memcmp(zName, "tag:", 4)==0 ){ |
| 53 | zName += 4; |
| 54 | zUuid = tag_to_uuid(zName, zType); |
| 55 | }else{ |
| 56 | zUuid = tag_to_uuid(zName, zType); |
| 57 | if( zUuid==0 ){ |
| 58 | zUuid = date_to_uuid(zName, zType); |
| 59 | } |
| 60 | } |
| 61 | if( zUuid ){ |
| 62 | blob_reset(pName); |
| 63 | blob_append(pName, zUuid, -1); |
| 64 | free(zUuid); |
| 65 | return 0; |
| 66 | } |
| 67 | fossil_error(iErrPriority, "not a valid object name: %s", zName); |
| 68 | return 1; |
| 69 | } |
| 70 | blob_materialize(pName); |
| 71 | canonical16(blob_buffer(pName), sz); |
| 72 | if( sz==UUID_SIZE ){ |
| 73 | rc = db_int(1, "SELECT 0 FROM blob WHERE uuid=%B", pName); |
| 74 | if( rc ){ |
| 75 | fossil_error(iErrPriority, "no such artifact: %b", pName); |
| 76 | blob_reset(pName); |
| 77 | } |
| 78 | }else if( sz<UUID_SIZE && sz>=4 ){ |
| 79 | Stmt q; |
| 80 | db_prepare(&q, "SELECT uuid FROM blob WHERE uuid GLOB '%b*'", pName); |
| 81 | if( db_step(&q)!=SQLITE_ROW ){ |
| 82 | char *zUuid; |
| 83 | db_finalize(&q); |
| 84 | zUuid = tag_to_uuid(blob_str(pName), "*"); |
| 85 | if( zUuid ){ |
| 86 | blob_reset(pName); |
| 87 | blob_append(pName, zUuid, -1); |
| 88 | free(zUuid); |
| 89 | return 0; |
| 90 | } |
| 91 | fossil_error(iErrPriority, "no artifacts match the prefix \"%b\"", pName); |
| 92 | return 1; |
| 93 | } |
| 94 | blob_reset(pName); |
| 95 | blob_append(pName, db_column_text(&q, 0), db_column_bytes(&q, 0)); |
| 96 | if( db_step(&q)==SQLITE_ROW ){ |
| 97 | fossil_error(iErrPriority, |
| 98 | "multiple artifacts match" |
| 99 | ); |
| 100 | blob_reset(pName); |
| 101 | db_finalize(&q); |
| 102 | return 2; |
| 103 | } |
| 104 | db_finalize(&q); |
| 105 | rc = 0; |
| 106 | }else{ |
| 107 | rc = 0; |
| 108 | } |
| 109 | return rc; |
| 110 | } |
| 111 | |
| 112 | /* |
| 113 | ** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. |
| 114 | */ |
| 115 | static int is_date(const char *z){ |
| 116 | if( !fossil_isdigit(z[0]) ) return 0; |
| 117 | if( !fossil_isdigit(z[1]) ) return 0; |
| 118 | if( !fossil_isdigit(z[2]) ) return 0; |
| 119 | if( !fossil_isdigit(z[3]) ) return 0; |
| 120 | if( z[4]!='-') return 0; |
| 121 | if( !fossil_isdigit(z[5]) ) return 0; |
| 122 | if( !fossil_isdigit(z[6]) ) return 0; |
| 123 | if( z[7]!='-') return 0; |
| 124 | if( !fossil_isdigit(z[8]) ) return 0; |
| 125 | if( !fossil_isdigit(z[9]) ) return 0; |
| 126 | return 1; |
| 127 | } |
| 128 | |
| 129 | /* |
| 130 | ** Convert a symbolic tag name into the UUID of a check-in that contains |
| 131 | ** that tag. If the tag appears on multiple check-ins, return the UUID |
| 132 | ** of the most recent check-in with the tag. |
| 133 | ** |
| 134 | ** If the input string is of the form: |
| 135 | ** |
| 136 | ** tag:date |
| 137 | ** |
| 138 | ** Then return the UUID of the oldest check-in with that tag that is |
| 139 | ** not older than 'date'. |
| 140 | ** |
| 141 | ** An input of "tip" returns the most recent check-in. |
| 142 | ** |
| 143 | ** Memory to hold the returned string comes from malloc() and needs to |
| 144 | ** be freed by the caller. |
| 145 | */ |
| 146 | char *tag_to_uuid(const char *zTag, const char *zType){ |
| 147 | int vid; |
| 148 | char *zUuid; |
| 149 | |
| 150 | if( zType==0 || zType[0]==0 ) zType = "*"; |
| 151 | zUuid = db_text(0, |
| 152 | "SELECT blob.uuid" |
| 153 | " FROM tag, tagxref, event, blob" |
| 154 | " WHERE tag.tagname='sym-%q' " |
| 155 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 156 | " AND event.objid=tagxref.rid " |
| 157 | " AND blob.rid=event.objid " |
| 158 | " AND event.type GLOB '%q'" |
| 159 | " ORDER BY event.mtime DESC /*sort*/", |
| 160 | zTag, zType |
| 161 | ); |
| 162 | if( zUuid==0 ){ |
| 163 | int nTag = strlen(zTag); |
| 164 | int i; |
| 165 | for(i=0; i<nTag-10; i++){ |
| 166 | if( zTag[i]==':' && is_date(&zTag[i+1]) ){ |
| 167 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 168 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 169 | int nDate = strlen(zDate); |
| 170 | int useUtc = 0; |
| 171 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 172 | nDate -= 3; |
| 173 | zDate[nDate] = 0; |
| 174 | useUtc = 1; |
| 175 | } |
| 176 | zUuid = db_text(0, |
| 177 | "SELECT blob.uuid" |
| 178 | " FROM tag, tagxref, event, blob" |
| 179 | " WHERE tag.tagname='sym-%q' " |
| 180 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 181 | " AND event.objid=tagxref.rid " |
| 182 | " AND blob.rid=event.objid " |
| 183 | " AND event.mtime<=julianday(%Q %s)" |
| 184 | " AND event.type GLOB '%q'" |
| 185 | " ORDER BY event.mtime DESC /*sort*/ ", |
| 186 | zTagBase, zDate, (useUtc ? "" : ",'utc'"), zType |
| 187 | ); |
| 188 | break; |
| 189 | } |
| 190 | } |
| 191 | if( zUuid==0 && fossil_strcmp(zTag, "tip")==0 ){ |
| 192 | zUuid = db_text(0, |
| 193 | "SELECT blob.uuid" |
| 194 | " FROM event, blob" |
| 195 | " WHERE event.type='ci'" |
| 196 | " AND blob.rid=event.objid" |
| 197 | " ORDER BY event.mtime DESC" |
| 198 | ); |
| 199 | } |
| 200 | if( zUuid==0 && g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ |
| 201 | if( fossil_strcmp(zTag, "current")==0 ){ |
| 202 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); |
| 203 | }else if( fossil_strcmp(zTag, "prev")==0 |
| 204 | || fossil_strcmp(zTag, "previous")==0 ){ |
| 205 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" |
| 206 | "(SELECT pid FROM plink WHERE cid=%d AND isprim)", |
| 207 | vid); |
| 208 | }else if( fossil_strcmp(zTag, "next")==0 ){ |
| 209 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" |
| 210 | "(SELECT cid FROM plink WHERE pid=%d" |
| 211 | " ORDER BY isprim DESC, mtime DESC)", |
| 212 | vid); |
| 213 | } |
| 214 | } |
| 215 | } |
| 216 | return zUuid; |
| 217 | } |
| 218 | |
| 219 | /* |
| 220 | ** Convert a date/time string into a UUID. |
| 221 | ** |
| 222 | ** Input forms accepted: |
| 223 | ** |
| 224 | ** date:DATE |
| 225 | ** local:DATE |
| 226 | ** utc:DATE |
| 227 | ** |
| 228 | ** The DATE is interpreted as localtime unless the "utc:" prefix is used |
| 229 | ** or a "utc" string appears at the end of the DATE string. |
| 230 | */ |
| 231 | char *date_to_uuid(const char *zDate, const char *zType){ |
| 232 | int useUtc = 0; |
| 233 | int n; |
| 234 | char *zCopy = 0; |
| 235 | char *zUuid; |
| 236 | |
| 237 | if( memcmp(zDate, "date:", 5)==0 ){ |
| 238 | zDate += 5; |
| 239 | }else if( memcmp(zDate, "local:", 6)==0 ){ |
| 240 | zDate += 6; |
| 241 | }else if( memcmp(zDate, "utc:", 4)==0 ){ |
| 242 | zDate += 4; |
| 243 | useUtc = 1; |
| 244 | } |
| 245 | n = strlen(zDate); |
| 246 | if( n<10 || !is_date(zDate) ) return 0; |
| 247 | if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){ |
| 248 | zCopy = mprintf("%s", zDate); |
| 249 | zCopy[n-3] = 0; |
| 250 | zDate = zCopy; |
| 251 | n -= 3; |
| 252 | useUtc = 1; |
| 253 | } |
| 254 | if( zType==0 || zType[0]==0 ) zType = "*"; |
| 255 | zUuid = db_text(0, |
| 256 | "SELECT (SELECT uuid FROM blob WHERE rid=event.objid)" |
| 257 | " FROM event" |
| 258 | " WHERE mtime<=julianday(%Q %s) AND type GLOB '%q'" |
| 259 | " ORDER BY mtime DESC LIMIT 1", |
| 260 | zDate, useUtc ? "" : ",'utc'", zType |
| 261 | ); |
| 262 | free(zCopy); |
| 263 | return zUuid; |
| 264 | } |
| 265 | |
| 266 | /* |
| 267 | ** COMMAND: test-name-to-id |
| 268 | ** |
| @@ -284,41 +289,38 @@ | |
| 284 | blob_reset(&name); |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | /* |
| 289 | ** Convert a name to a rid. If the name is a small integer value then |
| 290 | ** just use atoi() to do the conversion. If the name contains alphabetic |
| 291 | ** characters or is not an existing rid, then use name_to_uuid then |
| 292 | ** convert the uuid to a rid. |
| 293 | ** |
| 294 | ** This routine is used by command-line routines to resolve command-line inputs |
| 295 | ** into a rid. |
| 296 | */ |
| 297 | int name_to_typed_rid(const char *zName, const char *zType){ |
| 298 | int i; |
| 299 | int rid; |
| 300 | Blob name; |
| 301 | |
| 302 | if( zName==0 || zName[0]==0 ) return 0; |
| 303 | blob_init(&name, zName, -1); |
| 304 | if( name_to_uuid(&name, -1, zType) ){ |
| 305 | blob_reset(&name); |
| 306 | for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} |
| 307 | if( zName[i]==0 ){ |
| 308 | rid = atoi(zName); |
| 309 | if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ |
| 310 | return rid; |
| 311 | } |
| 312 | } |
| 313 | fossil_error(1, "no such artifact: %s", zName); |
| 314 | return 0; |
| 315 | }else{ |
| 316 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); |
| 317 | blob_reset(&name); |
| 318 | } |
| 319 | return rid; |
| 320 | } |
| 321 | int name_to_rid(const char *zName){ |
| 322 | return name_to_typed_rid(zName, "*"); |
| 323 | } |
| 324 | |
| @@ -362,34 +364,101 @@ | |
| 362 | ** rid. If the CGI parameter is missing or is not a valid artifact tag, |
| 363 | ** return 0. If the CGI parameter is ambiguous, redirect to a page that |
| 364 | ** shows all possibilities and do not return. |
| 365 | */ |
| 366 | int name_to_rid_www(const char *zParamName){ |
| 367 | int i, rc; |
| 368 | int rid; |
| 369 | const char *zName = P(zParamName); |
| 370 | Blob name; |
| 371 | if(!zName && fossil_has_json()){ |
| 372 | zName = json_find_option_cstr(zParamName,NULL,NULL); |
| 373 | } |
| 374 | if( zName==0 || zName[0]==0 ) return 0; |
| 375 | blob_init(&name, zName, -1); |
| 376 | rc = name_to_uuid(&name, -1, "*"); |
| 377 | if( rc==1 ){ |
| 378 | blob_reset(&name); |
| 379 | for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} |
| 380 | if( zName[i]==0 ){ |
| 381 | rid = atoi(zName); |
| 382 | if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ |
| 383 | return rid; |
| 384 | } |
| 385 | } |
| 386 | return 0; |
| 387 | }else if( rc==2 ){ |
| 388 | cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); |
| 389 | return 0; |
| 390 | }else{ |
| 391 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); |
| 392 | blob_reset(&name); |
| 393 | } |
| 394 | return rid; |
| 395 | } |
| 396 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -22,10 +22,220 @@ | |
| 22 | ** not necessarily in canonical form. |
| 23 | */ |
| 24 | #include "config.h" |
| 25 | #include "name.h" |
| 26 | #include <assert.h> |
| 27 | |
| 28 | /* |
| 29 | ** Return TRUE if the string begins with something that looks roughly |
| 30 | ** like an ISO date/time string. The SQLite date/time functions will |
| 31 | ** have the final say-so about whether or not the date/time string is |
| 32 | ** well-formed. |
| 33 | */ |
| 34 | static int is_date(const char *z){ |
| 35 | if( !fossil_isdigit(z[0]) ) return 0; |
| 36 | if( !fossil_isdigit(z[1]) ) return 0; |
| 37 | if( !fossil_isdigit(z[2]) ) return 0; |
| 38 | if( !fossil_isdigit(z[3]) ) return 0; |
| 39 | if( z[4]!='-') return 0; |
| 40 | if( !fossil_isdigit(z[5]) ) return 0; |
| 41 | if( !fossil_isdigit(z[6]) ) return 0; |
| 42 | if( z[7]!='-') return 0; |
| 43 | if( !fossil_isdigit(z[8]) ) return 0; |
| 44 | if( !fossil_isdigit(z[9]) ) return 0; |
| 45 | return 1; |
| 46 | } |
| 47 | |
| 48 | /* |
| 49 | ** Convert a symbolic name into a RID. Acceptable forms: |
| 50 | ** |
| 51 | ** * SHA1 hash |
| 52 | ** * SHA1 hash prefix of at least 4 characters |
| 53 | ** * Symbolic Name |
| 54 | ** * "tag:" + symbolic name |
| 55 | ** * Date or date-time |
| 56 | ** * "date:" + Date or date-time |
| 57 | ** * symbolic-name ":" date-time |
| 58 | ** * "tip" |
| 59 | ** |
| 60 | ** The following additional forms are available in local checkouts: |
| 61 | ** |
| 62 | ** * "current" |
| 63 | ** * "prev" or "previous" |
| 64 | ** * "next" |
| 65 | ** |
| 66 | ** Return the RID of the matching artifact. Or return 0 if the name does not |
| 67 | ** match any known object. Or return -1 if the name is ambiguious. |
| 68 | ** |
| 69 | ** The zType parameter specifies the type of artifact: ci, t, w, e, g. |
| 70 | ** If zType is NULL or "" or "*" then any type of artifact will serve. |
| 71 | ** zType is "ci" in most use cases since we are usually searching for |
| 72 | ** a check-in. |
| 73 | */ |
| 74 | static int symbolic_name_to_rid(const char *zTag, const char *zType){ |
| 75 | int vid; |
| 76 | int rid = 0; |
| 77 | int nTag; |
| 78 | int i; |
| 79 | |
| 80 | if( zType==0 || zType[0]==0 ) zType = "*"; |
| 81 | if( zTag==0 || zTag[0]==0 ) return 0; |
| 82 | |
| 83 | /* special keyword: "tip" */ |
| 84 | if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){ |
| 85 | rid = db_int(0, |
| 86 | "SELECT objid" |
| 87 | " FROM event" |
| 88 | " WHERE type='ci'" |
| 89 | " ORDER BY event.mtime DESC" |
| 90 | ); |
| 91 | if( rid ) return rid; |
| 92 | } |
| 93 | |
| 94 | /* special keywords: "prev", "previous", "current", and "next" */ |
| 95 | if( g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ |
| 96 | if( fossil_strcmp(zTag, "current")==0 ){ |
| 97 | rid = vid; |
| 98 | }else if( fossil_strcmp(zTag, "prev")==0 |
| 99 | || fossil_strcmp(zTag, "previous")==0 ){ |
| 100 | rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", vid); |
| 101 | }else if( fossil_strcmp(zTag, "next")==0 ){ |
| 102 | rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d" |
| 103 | " ORDER BY isprim DESC, mtime DESC", vid); |
| 104 | } |
| 105 | if( rid ) return rid; |
| 106 | } |
| 107 | |
| 108 | /* Date and times */ |
| 109 | if( memcmp(zTag, "date:", 5)==0 ){ |
| 110 | rid = db_int(0, |
| 111 | "SELECT objid FROM event" |
| 112 | " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" |
| 113 | " ORDER BY mtime DESC LIMIT 1", |
| 114 | &zTag[5], zType); |
| 115 | return rid; |
| 116 | } |
| 117 | if( is_date(zTag) ){ |
| 118 | rid = db_int(0, |
| 119 | "SELECT objid FROM event" |
| 120 | " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" |
| 121 | " ORDER BY mtime DESC LIMIT 1", |
| 122 | zTag, zType); |
| 123 | if( rid) return rid; |
| 124 | } |
| 125 | |
| 126 | /* Deprecated date & time formats: "local:" + date-time and |
| 127 | ** "utc:" + date-time */ |
| 128 | if( memcmp(zTag, "local:", 6)==0 ){ |
| 129 | rid = db_int(0, |
| 130 | "SELECT objid FROM event" |
| 131 | " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" |
| 132 | " ORDER BY mtime DESC LIMIT 1", |
| 133 | &zTag[6], zType); |
| 134 | return rid; |
| 135 | } |
| 136 | if( memcmp(zTag, "utc:", 4)==0 ){ |
| 137 | rid = db_int(0, |
| 138 | "SELECT objid FROM event" |
| 139 | " WHERE mtime<=julianday('%qz') AND type GLOB '%q'" |
| 140 | " ORDER BY mtime DESC LIMIT 1", |
| 141 | &zTag[4], zType); |
| 142 | return rid; |
| 143 | } |
| 144 | |
| 145 | /* "tag:" + symbolic-name */ |
| 146 | if( memcmp(zTag, "tag:", 4)==0 ){ |
| 147 | rid = db_int(0, |
| 148 | "SELECT event.objid" |
| 149 | " FROM tag, tagxref, event" |
| 150 | " WHERE tag.tagname='sym-%q' " |
| 151 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 152 | " AND event.objid=tagxref.rid " |
| 153 | " AND event.type GLOB '%q'" |
| 154 | " ORDER BY event.mtime DESC /*sort*/", |
| 155 | &zTag[4], zType |
| 156 | ); |
| 157 | return rid; |
| 158 | } |
| 159 | |
| 160 | /* symbolic-name ":" date-time */ |
| 161 | nTag = strlen(zTag); |
| 162 | for(i=0; i<nTag-10 && zTag[i]!=':'; i++){} |
| 163 | if( zTag[i]==':' && is_date(&zTag[i+1]) ){ |
| 164 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 165 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 166 | int nDate = strlen(zDate); |
| 167 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 168 | zDate[nDate-3] = 'z'; |
| 169 | zDate[nDate-2] = 0; |
| 170 | } |
| 171 | rid = db_int(0, |
| 172 | "SELECT event.objid" |
| 173 | " FROM tag, tagxref, event" |
| 174 | " WHERE tag.tagname='sym-%q' " |
| 175 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 176 | " AND event.objid=tagxref.rid " |
| 177 | " AND event.mtime<=julianday(%Q)" |
| 178 | " AND event.type GLOB '%q'" |
| 179 | " ORDER BY event.mtime DESC /*sort*/ ", |
| 180 | zTagBase, zDate, zType |
| 181 | ); |
| 182 | return rid; |
| 183 | } |
| 184 | |
| 185 | /* SHA1 hash or prefix */ |
| 186 | if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){ |
| 187 | Stmt q; |
| 188 | char zUuid[UUID_SIZE+1]; |
| 189 | memcpy(zUuid, zTag, nTag+1); |
| 190 | canonical16(zUuid, nTag); |
| 191 | rid = 0; |
| 192 | if( zType[0]=='*' ){ |
| 193 | db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%s*'", zUuid); |
| 194 | }else{ |
| 195 | db_prepare(&q, |
| 196 | "SELECT blob.rid" |
| 197 | " FROM blob, event" |
| 198 | " WHERE blob.uuid GLOB '%s*'" |
| 199 | " AND event.objid=blob.rid" |
| 200 | " AND event.type GLOB '%q'", |
| 201 | zUuid, zType |
| 202 | ); |
| 203 | } |
| 204 | if( db_step(&q)==SQLITE_ROW ){ |
| 205 | rid = db_column_int(&q, 0); |
| 206 | if( db_step(&q)==SQLITE_ROW ) rid = -1; |
| 207 | } |
| 208 | db_finalize(&q); |
| 209 | if( rid ) return rid; |
| 210 | } |
| 211 | |
| 212 | /* Symbolic name */ |
| 213 | rid = db_int(0, |
| 214 | "SELECT event.objid" |
| 215 | " FROM tag, tagxref, event" |
| 216 | " WHERE tag.tagname='sym-%q' " |
| 217 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " |
| 218 | " AND event.objid=tagxref.rid " |
| 219 | " AND event.type GLOB '%q'" |
| 220 | " ORDER BY event.mtime DESC /*sort*/ ", |
| 221 | zTag, zType |
| 222 | ); |
| 223 | if( rid>0 ) return rid; |
| 224 | |
| 225 | /* Undocumented: numeric tags get translated directly into the RID */ |
| 226 | for(i=0; fossil_isdigit(zTag[i]); i++){} |
| 227 | if( zTag[i]==0 ){ |
| 228 | rid = db_int(0, |
| 229 | "SELECT event.objid" |
| 230 | " FROM event" |
| 231 | " WHERE event.objid=%s" |
| 232 | " AND event.type GLOB '%q'", zTag, zType); |
| 233 | } |
| 234 | return rid; |
| 235 | } |
| 236 | |
| 237 | |
| 238 | /* |
| 239 | ** This routine takes a user-entered UUID which might be in mixed |
| 240 | ** case and might only be a prefix of the full UUID and converts it |
| 241 | ** into the full-length UUID in canonical form. |
| @@ -41,228 +251,23 @@ | |
| 251 | ** |
| 252 | ** Return 0 on success. Return 1 if the name cannot be resolved. |
| 253 | ** Return 2 name is ambiguous. |
| 254 | */ |
| 255 | int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){ |
| 256 | char *zName = blob_str(pName); |
| 257 | int rid = symbolic_name_to_rid(zName, zType); |
| 258 | if( rid<0 ){ |
| 259 | fossil_error(iErrPriority, "ambiguous name: %s", zName); |
| 260 | return 2; |
| 261 | }else if( rid==0 ){ |
| 262 | fossil_error(iErrPriority, "not found: %s", zName); |
| 263 | return 1; |
| 264 | }else{ |
| 265 | blob_reset(pName); |
| 266 | db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 267 | return 0; |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | /* |
| 272 | ** COMMAND: test-name-to-id |
| 273 | ** |
| @@ -284,41 +289,38 @@ | |
| 289 | blob_reset(&name); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | /* |
| 294 | ** Convert a name to a rid. If the name can be any of the various forms |
| 295 | ** accepted: |
| 296 | ** |
| 297 | ** * SHA1 hash or prefix thereof |
| 298 | ** * symbolic name |
| 299 | ** * date |
| 300 | ** * label:date |
| 301 | ** * prev, previous |
| 302 | ** * next |
| 303 | ** * tip |
| 304 | ** |
| 305 | ** This routine is used by command-line routines to resolve command-line inputs |
| 306 | ** into a rid. |
| 307 | */ |
| 308 | int name_to_typed_rid(const char *zName, const char *zType){ |
| 309 | int rid; |
| 310 | |
| 311 | if( zName==0 || zName[0]==0 ) return 0; |
| 312 | rid = symbolic_name_to_rid(zName, zType); |
| 313 | if( rid<0 ){ |
| 314 | fossil_error(1, "ambiguous name: %s", zName); |
| 315 | return 0; |
| 316 | }else if( rid==0 ){ |
| 317 | fossil_error(1, "not found: %s", zName); |
| 318 | return 0; |
| 319 | }else{ |
| 320 | return rid; |
| 321 | } |
| 322 | } |
| 323 | int name_to_rid(const char *zName){ |
| 324 | return name_to_typed_rid(zName, "*"); |
| 325 | } |
| 326 | |
| @@ -362,34 +364,101 @@ | |
| 364 | ** rid. If the CGI parameter is missing or is not a valid artifact tag, |
| 365 | ** return 0. If the CGI parameter is ambiguous, redirect to a page that |
| 366 | ** shows all possibilities and do not return. |
| 367 | */ |
| 368 | int name_to_rid_www(const char *zParamName){ |
| 369 | int rid; |
| 370 | const char *zName = P(zParamName); |
| 371 | if(!zName && fossil_has_json()){ |
| 372 | zName = json_find_option_cstr(zParamName,NULL,NULL); |
| 373 | } |
| 374 | if( zName==0 || zName[0]==0 ) return 0; |
| 375 | rid = symbolic_name_to_rid(zName, "*"); |
| 376 | if( rid<0 ){ |
| 377 | cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); |
| 378 | rid = 0; |
| 379 | } |
| 380 | return rid; |
| 381 | } |
| 382 | |
| 383 | /* |
| 384 | ** COMMAND: whatis* |
| 385 | ** Usage: %fossil whatis NAME |
| 386 | ** |
| 387 | ** Resolve the symbol NAME into its canonical 40-character SHA1-hash |
| 388 | ** artifact name and provide a description of what role that artifact |
| 389 | ** plays. |
| 390 | */ |
| 391 | void whatis_cmd(void){ |
| 392 | int rid; |
| 393 | const char *zName; |
| 394 | int fExtra; |
| 395 | db_find_and_open_repository(0,0); |
| 396 | fExtra = find_option("verbose","v",0)!=0; |
| 397 | if( g.argc!=3 ) usage("whatis NAME"); |
| 398 | zName = g.argv[2]; |
| 399 | rid = symbolic_name_to_rid(zName, 0); |
| 400 | if( rid<0 ){ |
| 401 | fossil_print("Ambiguous artifact name prefix: %s\n", zName); |
| 402 | }else if( rid==0 ){ |
| 403 | fossil_print("Unknown artifact: %s\n", zName); |
| 404 | }else{ |
| 405 | Stmt q; |
| 406 | db_prepare(&q, "SELECT uuid, size, datetime(mtime, 'localtime'), ipaddr" |
| 407 | " FROM blob, rcvfrom" |
| 408 | " WHERE rid=%d" |
| 409 | " AND rcvfrom.rcvid=blob.rcvid", |
| 410 | rid); |
| 411 | if( db_step(&q)==SQLITE_ROW ){ |
| 412 | if( fExtra ){ |
| 413 | fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid); |
| 414 | fossil_print("size: %d bytes\n", db_column_int(&q,1)); |
| 415 | fossil_print("received: %s from %s\n", |
| 416 | db_column_text(&q, 2), |
| 417 | db_column_text(&q, 3)); |
| 418 | }else{ |
| 419 | fossil_print("artifact: %s\n", db_column_text(&q,0)); |
| 420 | fossil_print("size: %d bytes\n", db_column_int(&q,1)); |
| 421 | } |
| 422 | } |
| 423 | db_finalize(&q); |
| 424 | db_prepare(&q, |
| 425 | "SELECT type, datetime(mtime,'localtime')," |
| 426 | " coalesce(euser,user), coalesce(ecomment,comment)" |
| 427 | " FROM event WHERE objid=%d", rid); |
| 428 | if( db_step(&q)==SQLITE_ROW ){ |
| 429 | const char *zType; |
| 430 | switch( db_column_text(&q,0)[0] ){ |
| 431 | case 'c': zType = "Check-in"; break; |
| 432 | case 'w': zType = "Wiki-edit"; break; |
| 433 | case 'e': zType = "Event"; break; |
| 434 | case 't': zType = "Ticket-change"; break; |
| 435 | case 'g': zType = "Tag-change"; break; |
| 436 | } |
| 437 | fossil_print("type: %s by %s on %s\n", zType, db_column_text(&q,2), |
| 438 | db_column_text(&q, 1)); |
| 439 | fossil_print("comment: "); |
| 440 | comment_print(db_column_text(&q,3), 10, 78); |
| 441 | } |
| 442 | db_finalize(&q); |
| 443 | db_prepare(&q, |
| 444 | "SELECT filename.name, blob.uuid, datetime(event.mtime,'localtime')," |
| 445 | " coalesce(euser,user), coalesce(ecomment,comment)" |
| 446 | " FROM mlink, filename, blob, event" |
| 447 | " WHERE mlink.fid=%d" |
| 448 | " AND filename.fnid=mlink.fnid" |
| 449 | " AND event.objid=mlink.mid" |
| 450 | " AND blob.rid=mlink.mid" |
| 451 | " ORDER BY event.mtime DESC /*sort*/", |
| 452 | rid); |
| 453 | while( db_step(&q)==SQLITE_ROW ){ |
| 454 | fossil_print("file: %s\n", db_column_text(&q,0)); |
| 455 | fossil_print(" part of [%.10s] by %s on %s\n", |
| 456 | db_column_text(&q, 1), |
| 457 | db_column_text(&q, 3), |
| 458 | db_column_text(&q, 2)); |
| 459 | fossil_print(" "); |
| 460 | comment_print(db_column_text(&q,4), 10, 78); |
| 461 | } |
| 462 | db_finalize(&q); |
| 463 | } |
| 464 | } |
| 465 |
+9
-5
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -370,11 +370,15 @@ | ||
| 370 | 370 | " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))" |
| 371 | 371 | ); |
| 372 | 372 | db_multi_exec( |
| 373 | 373 | "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')" |
| 374 | 374 | ); |
| 375 | - totalSize = db_int(0, "SELECT count(*) FROM blob"); | |
| 375 | + | |
| 376 | + /* The following should be count(*) instead of max(rid). max(rid) is | |
| 377 | + ** an adequate approximation, however, and is much faster for large | |
| 378 | + ** repositories. */ | |
| 379 | + totalSize = db_int(0, "SELECT max(rid) FROM blob"); | |
| 376 | 380 | incrSize = totalSize/100; |
| 377 | 381 | totalSize += incrSize*2; |
| 378 | 382 | db_prepare(&s, |
| 379 | 383 | "SELECT rid, size FROM blob /*scan*/" |
| 380 | 384 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| @@ -483,11 +487,11 @@ | ||
| 483 | 487 | |
| 484 | 488 | db_end_transaction(0); |
| 485 | 489 | } |
| 486 | 490 | |
| 487 | 491 | /* |
| 488 | -** COMMAND: rebuild | |
| 492 | +** COMMAND: rebuild | |
| 489 | 493 | ** |
| 490 | 494 | ** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS? |
| 491 | 495 | ** |
| 492 | 496 | ** Reconstruct the named repository database from the core |
| 493 | 497 | ** records. Run this command after updating the fossil |
| @@ -716,11 +720,11 @@ | ||
| 716 | 720 | db_finalize(&q); |
| 717 | 721 | } |
| 718 | 722 | } |
| 719 | 723 | |
| 720 | 724 | /* |
| 721 | -** COMMAND: scrub | |
| 725 | +** COMMAND: scrub* | |
| 722 | 726 | ** %fossil scrub ?OPTIONS? ?REPOSITORY? |
| 723 | 727 | ** |
| 724 | 728 | ** The command removes sensitive information (such as passwords) from a |
| 725 | 729 | ** repository so that the respository can be sent to an untrusted reader. |
| 726 | 730 | ** |
| @@ -850,11 +854,11 @@ | ||
| 850 | 854 | } |
| 851 | 855 | fossil_mbcs_free(zMbcsPath); |
| 852 | 856 | } |
| 853 | 857 | |
| 854 | 858 | /* |
| 855 | -** COMMAND: reconstruct | |
| 859 | +** COMMAND: reconstruct* | |
| 856 | 860 | ** |
| 857 | 861 | ** Usage: %fossil reconstruct FILENAME DIRECTORY |
| 858 | 862 | ** |
| 859 | 863 | ** This command studies the artifacts (files) in DIRECTORY and |
| 860 | 864 | ** reconstructs the fossil record from them. It places the new |
| @@ -911,11 +915,11 @@ | ||
| 911 | 915 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 912 | 916 | fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
| 913 | 917 | } |
| 914 | 918 | |
| 915 | 919 | /* |
| 916 | -** COMMAND: deconstruct | |
| 920 | +** COMMAND: deconstruct* | |
| 917 | 921 | ** |
| 918 | 922 | ** Usage %fossil deconstruct ?OPTIONS? DESTINATION |
| 919 | 923 | ** |
| 920 | 924 | ** |
| 921 | 925 | ** This command exports all artifacts of a given repository and |
| 922 | 926 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -370,11 +370,15 @@ | |
| 370 | " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))" |
| 371 | ); |
| 372 | db_multi_exec( |
| 373 | "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')" |
| 374 | ); |
| 375 | totalSize = db_int(0, "SELECT count(*) FROM blob"); |
| 376 | incrSize = totalSize/100; |
| 377 | totalSize += incrSize*2; |
| 378 | db_prepare(&s, |
| 379 | "SELECT rid, size FROM blob /*scan*/" |
| 380 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| @@ -483,11 +487,11 @@ | |
| 483 | |
| 484 | db_end_transaction(0); |
| 485 | } |
| 486 | |
| 487 | /* |
| 488 | ** COMMAND: rebuild |
| 489 | ** |
| 490 | ** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS? |
| 491 | ** |
| 492 | ** Reconstruct the named repository database from the core |
| 493 | ** records. Run this command after updating the fossil |
| @@ -716,11 +720,11 @@ | |
| 716 | db_finalize(&q); |
| 717 | } |
| 718 | } |
| 719 | |
| 720 | /* |
| 721 | ** COMMAND: scrub |
| 722 | ** %fossil scrub ?OPTIONS? ?REPOSITORY? |
| 723 | ** |
| 724 | ** The command removes sensitive information (such as passwords) from a |
| 725 | ** repository so that the respository can be sent to an untrusted reader. |
| 726 | ** |
| @@ -850,11 +854,11 @@ | |
| 850 | } |
| 851 | fossil_mbcs_free(zMbcsPath); |
| 852 | } |
| 853 | |
| 854 | /* |
| 855 | ** COMMAND: reconstruct |
| 856 | ** |
| 857 | ** Usage: %fossil reconstruct FILENAME DIRECTORY |
| 858 | ** |
| 859 | ** This command studies the artifacts (files) in DIRECTORY and |
| 860 | ** reconstructs the fossil record from them. It places the new |
| @@ -911,11 +915,11 @@ | |
| 911 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 912 | fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
| 913 | } |
| 914 | |
| 915 | /* |
| 916 | ** COMMAND: deconstruct |
| 917 | ** |
| 918 | ** Usage %fossil deconstruct ?OPTIONS? DESTINATION |
| 919 | ** |
| 920 | ** |
| 921 | ** This command exports all artifacts of a given repository and |
| 922 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -370,11 +370,15 @@ | |
| 370 | " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))" |
| 371 | ); |
| 372 | db_multi_exec( |
| 373 | "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')" |
| 374 | ); |
| 375 | |
| 376 | /* The following should be count(*) instead of max(rid). max(rid) is |
| 377 | ** an adequate approximation, however, and is much faster for large |
| 378 | ** repositories. */ |
| 379 | totalSize = db_int(0, "SELECT max(rid) FROM blob"); |
| 380 | incrSize = totalSize/100; |
| 381 | totalSize += incrSize*2; |
| 382 | db_prepare(&s, |
| 383 | "SELECT rid, size FROM blob /*scan*/" |
| 384 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| @@ -483,11 +487,11 @@ | |
| 487 | |
| 488 | db_end_transaction(0); |
| 489 | } |
| 490 | |
| 491 | /* |
| 492 | ** COMMAND: rebuild |
| 493 | ** |
| 494 | ** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS? |
| 495 | ** |
| 496 | ** Reconstruct the named repository database from the core |
| 497 | ** records. Run this command after updating the fossil |
| @@ -716,11 +720,11 @@ | |
| 720 | db_finalize(&q); |
| 721 | } |
| 722 | } |
| 723 | |
| 724 | /* |
| 725 | ** COMMAND: scrub* |
| 726 | ** %fossil scrub ?OPTIONS? ?REPOSITORY? |
| 727 | ** |
| 728 | ** The command removes sensitive information (such as passwords) from a |
| 729 | ** repository so that the respository can be sent to an untrusted reader. |
| 730 | ** |
| @@ -850,11 +854,11 @@ | |
| 854 | } |
| 855 | fossil_mbcs_free(zMbcsPath); |
| 856 | } |
| 857 | |
| 858 | /* |
| 859 | ** COMMAND: reconstruct* |
| 860 | ** |
| 861 | ** Usage: %fossil reconstruct FILENAME DIRECTORY |
| 862 | ** |
| 863 | ** This command studies the artifacts (files) in DIRECTORY and |
| 864 | ** reconstructs the fossil record from them. It places the new |
| @@ -911,11 +915,11 @@ | |
| 915 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 916 | fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
| 917 | } |
| 918 | |
| 919 | /* |
| 920 | ** COMMAND: deconstruct* |
| 921 | ** |
| 922 | ** Usage %fossil deconstruct ?OPTIONS? DESTINATION |
| 923 | ** |
| 924 | ** |
| 925 | ** This command exports all artifacts of a given repository and |
| 926 |
+1
-1
| --- src/search.c | ||
| +++ src/search.c | ||
| @@ -165,11 +165,11 @@ | ||
| 165 | 165 | } |
| 166 | 166 | |
| 167 | 167 | /* |
| 168 | 168 | ** Testing the search function. |
| 169 | 169 | ** |
| 170 | -** COMMAND: search | |
| 170 | +** COMMAND: search* | |
| 171 | 171 | ** %fossil search pattern... |
| 172 | 172 | ** |
| 173 | 173 | ** Search for timeline entries matching the pattern. |
| 174 | 174 | */ |
| 175 | 175 | void search_cmd(void){ |
| 176 | 176 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -165,11 +165,11 @@ | |
| 165 | } |
| 166 | |
| 167 | /* |
| 168 | ** Testing the search function. |
| 169 | ** |
| 170 | ** COMMAND: search |
| 171 | ** %fossil search pattern... |
| 172 | ** |
| 173 | ** Search for timeline entries matching the pattern. |
| 174 | */ |
| 175 | void search_cmd(void){ |
| 176 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -165,11 +165,11 @@ | |
| 165 | } |
| 166 | |
| 167 | /* |
| 168 | ** Testing the search function. |
| 169 | ** |
| 170 | ** COMMAND: search* |
| 171 | ** %fossil search pattern... |
| 172 | ** |
| 173 | ** Search for timeline entries matching the pattern. |
| 174 | */ |
| 175 | void search_cmd(void){ |
| 176 |
+1
-1
| --- src/sha1.c | ||
| +++ src/sha1.c | ||
| @@ -437,11 +437,11 @@ | ||
| 437 | 437 | sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1, |
| 438 | 438 | fossil_free); |
| 439 | 439 | } |
| 440 | 440 | |
| 441 | 441 | /* |
| 442 | -** COMMAND: sha1sum | |
| 442 | +** COMMAND: sha1sum* | |
| 443 | 443 | ** %fossil sha1sum FILE... |
| 444 | 444 | ** |
| 445 | 445 | ** Compute an SHA1 checksum of all files named on the command-line. |
| 446 | 446 | ** If an file is named "-" then take its content from standard input. |
| 447 | 447 | */ |
| 448 | 448 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -437,11 +437,11 @@ | |
| 437 | sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1, |
| 438 | fossil_free); |
| 439 | } |
| 440 | |
| 441 | /* |
| 442 | ** COMMAND: sha1sum |
| 443 | ** %fossil sha1sum FILE... |
| 444 | ** |
| 445 | ** Compute an SHA1 checksum of all files named on the command-line. |
| 446 | ** If an file is named "-" then take its content from standard input. |
| 447 | */ |
| 448 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -437,11 +437,11 @@ | |
| 437 | sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1, |
| 438 | fossil_free); |
| 439 | } |
| 440 | |
| 441 | /* |
| 442 | ** COMMAND: sha1sum* |
| 443 | ** %fossil sha1sum FILE... |
| 444 | ** |
| 445 | ** Compute an SHA1 checksum of all files named on the command-line. |
| 446 | ** If an file is named "-" then take its content from standard input. |
| 447 | */ |
| 448 |
+98
-64
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -314,17 +314,10 @@ | ||
| 314 | 314 | #endif |
| 315 | 315 | #ifdef HAVE_INTTYPES_H |
| 316 | 316 | #include <inttypes.h> |
| 317 | 317 | #endif |
| 318 | 318 | |
| 319 | -/* | |
| 320 | -** The number of samples of an index that SQLite takes in order to | |
| 321 | -** construct a histogram of the table content when running ANALYZE | |
| 322 | -** and with SQLITE_ENABLE_STAT2 | |
| 323 | -*/ | |
| 324 | -#define SQLITE_INDEX_SAMPLES 10 | |
| 325 | - | |
| 326 | 319 | /* |
| 327 | 320 | ** The following macros are used to cast pointers to integers and |
| 328 | 321 | ** integers to pointers. The way you do this varies from one compiler |
| 329 | 322 | ** to the next, so we have developed the following set of #if statements |
| 330 | 323 | ** to generate appropriate macros for a wide range of compilers. |
| @@ -656,11 +649,11 @@ | ||
| 656 | 649 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 657 | 650 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 658 | 651 | */ |
| 659 | 652 | #define SQLITE_VERSION "3.7.9" |
| 660 | 653 | #define SQLITE_VERSION_NUMBER 3007009 |
| 661 | -#define SQLITE_SOURCE_ID "2011-10-20 00:55:54 4344483f7d7f64dffadde0053e6c745948db9486" | |
| 654 | +#define SQLITE_SOURCE_ID "2011-10-29 19:25:08 5b82ec6fbbd2f4195ad06dd911de3817373ad5bf" | |
| 662 | 655 | |
| 663 | 656 | /* |
| 664 | 657 | ** CAPI3REF: Run-Time Library Version Numbers |
| 665 | 658 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 666 | 659 | ** |
| @@ -8792,10 +8785,11 @@ | ||
| 8792 | 8785 | SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); |
| 8793 | 8786 | SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); |
| 8794 | 8787 | SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); |
| 8795 | 8788 | SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); |
| 8796 | 8789 | SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); |
| 8790 | +SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *); | |
| 8797 | 8791 | |
| 8798 | 8792 | /* Functions used to truncate the database file. */ |
| 8799 | 8793 | SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); |
| 8800 | 8794 | |
| 8801 | 8795 | #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) |
| @@ -10189,12 +10183,13 @@ | ||
| 10189 | 10183 | IndexSample *aSample; /* Samples of the left-most key */ |
| 10190 | 10184 | #endif |
| 10191 | 10185 | }; |
| 10192 | 10186 | |
| 10193 | 10187 | /* |
| 10194 | -** Each sample stored in the sqlite_stat2 table is represented in memory | |
| 10195 | -** using a structure of this type. | |
| 10188 | +** Each sample stored in the sqlite_stat3 table is represented in memory | |
| 10189 | +** using a structure of this type. See documentation at the top of the | |
| 10190 | +** analyze.c source file for additional information. | |
| 10196 | 10191 | */ |
| 10197 | 10192 | struct IndexSample { |
| 10198 | 10193 | union { |
| 10199 | 10194 | char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ |
| 10200 | 10195 | double r; /* Value if eType is SQLITE_FLOAT */ |
| @@ -12293,13 +12288,10 @@ | ||
| 12293 | 12288 | "ENABLE_OVERSIZE_CELL_CHECK", |
| 12294 | 12289 | #endif |
| 12295 | 12290 | #ifdef SQLITE_ENABLE_RTREE |
| 12296 | 12291 | "ENABLE_RTREE", |
| 12297 | 12292 | #endif |
| 12298 | -#ifdef SQLITE_ENABLE_STAT2 | |
| 12299 | - "ENABLE_STAT2", | |
| 12300 | -#endif | |
| 12301 | 12293 | #ifdef SQLITE_ENABLE_STAT3 |
| 12302 | 12294 | "ENABLE_STAT3", |
| 12303 | 12295 | #endif |
| 12304 | 12296 | #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY |
| 12305 | 12297 | "ENABLE_UNLOCK_NOTIFY", |
| @@ -12996,10 +12988,11 @@ | ||
| 12996 | 12988 | SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); |
| 12997 | 12989 | SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); |
| 12998 | 12990 | SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); |
| 12999 | 12991 | SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *); |
| 13000 | 12992 | SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem); |
| 12993 | +SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p); | |
| 13001 | 12994 | |
| 13002 | 12995 | #ifdef SQLITE_OMIT_MERGE_SORT |
| 13003 | 12996 | # define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK |
| 13004 | 12997 | # define sqlite3VdbeSorterWrite(X,Y,Z) SQLITE_OK |
| 13005 | 12998 | # define sqlite3VdbeSorterClose(Y,Z) |
| @@ -29483,17 +29476,17 @@ | ||
| 29483 | 29476 | ** "<path to db>-journal" |
| 29484 | 29477 | ** "<path to db>-wal" |
| 29485 | 29478 | ** "<path to db>-journalNN" |
| 29486 | 29479 | ** "<path to db>-walNN" |
| 29487 | 29480 | ** |
| 29488 | - ** where NN is a 4 digit decimal number. The NN naming schemes are | |
| 29481 | + ** where NN is a decimal number. The NN naming schemes are | |
| 29489 | 29482 | ** used by the test_multiplex.c module. |
| 29490 | 29483 | */ |
| 29491 | 29484 | nDb = sqlite3Strlen30(zPath) - 1; |
| 29492 | 29485 | #ifdef SQLITE_ENABLE_8_3_NAMES |
| 29493 | - while( nDb>0 && zPath[nDb]!='-' && zPath[nDb]!='/' ) nDb--; | |
| 29494 | - if( nDb==0 || zPath[nDb]=='/' ) return SQLITE_OK; | |
| 29486 | + while( nDb>0 && !sqlite3Isalnum(zPath[nDb]) ) nDb--; | |
| 29487 | + if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK; | |
| 29495 | 29488 | #else |
| 29496 | 29489 | while( zPath[nDb]!='-' ){ |
| 29497 | 29490 | assert( nDb>0 ); |
| 29498 | 29491 | assert( zPath[nDb]!='\n' ); |
| 29499 | 29492 | nDb--; |
| @@ -44163,10 +44156,17 @@ | ||
| 44163 | 44156 | pPager->pWal = 0; |
| 44164 | 44157 | } |
| 44165 | 44158 | } |
| 44166 | 44159 | return rc; |
| 44167 | 44160 | } |
| 44161 | + | |
| 44162 | +/* | |
| 44163 | +** Unless this is an in-memory or temporary database, clear the pager cache. | |
| 44164 | +*/ | |
| 44165 | +SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *pPager){ | |
| 44166 | + if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager); | |
| 44167 | +} | |
| 44168 | 44168 | |
| 44169 | 44169 | #ifdef SQLITE_HAS_CODEC |
| 44170 | 44170 | /* |
| 44171 | 44171 | ** This function is called by the wal module when writing page content |
| 44172 | 44172 | ** into the log file. |
| @@ -57006,10 +57006,12 @@ | ||
| 57006 | 57006 | sqlite3_backup_step(&b, 0x7FFFFFFF); |
| 57007 | 57007 | assert( b.rc!=SQLITE_OK ); |
| 57008 | 57008 | rc = sqlite3_backup_finish(&b); |
| 57009 | 57009 | if( rc==SQLITE_OK ){ |
| 57010 | 57010 | pTo->pBt->pageSizeFixed = 0; |
| 57011 | + }else{ | |
| 57012 | + sqlite3PagerClearCache(sqlite3BtreePager(b.pDest)); | |
| 57011 | 57013 | } |
| 57012 | 57014 | |
| 57013 | 57015 | assert( sqlite3BtreeIsInTrans(pTo)==0 ); |
| 57014 | 57016 | sqlite3BtreeLeave(pFrom); |
| 57015 | 57017 | sqlite3BtreeLeave(pTo); |
| @@ -60480,10 +60482,34 @@ | ||
| 60480 | 60482 | ** in p->rc. This routine sets that result back to SQLITE_OK. |
| 60481 | 60483 | */ |
| 60482 | 60484 | SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe *p){ |
| 60483 | 60485 | p->rc = SQLITE_OK; |
| 60484 | 60486 | } |
| 60487 | + | |
| 60488 | +/* | |
| 60489 | +** Copy the error code and error message belonging to the VDBE passed | |
| 60490 | +** as the first argument to its database handle (so that they will be | |
| 60491 | +** returned by calls to sqlite3_errcode() and sqlite3_errmsg()). | |
| 60492 | +** | |
| 60493 | +** This function does not clear the VDBE error code or message, just | |
| 60494 | +** copies them to the database handle. | |
| 60495 | +*/ | |
| 60496 | +SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){ | |
| 60497 | + sqlite3 *db = p->db; | |
| 60498 | + int rc = p->rc; | |
| 60499 | + if( p->zErrMsg ){ | |
| 60500 | + u8 mallocFailed = db->mallocFailed; | |
| 60501 | + sqlite3BeginBenignMalloc(); | |
| 60502 | + sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); | |
| 60503 | + sqlite3EndBenignMalloc(); | |
| 60504 | + db->mallocFailed = mallocFailed; | |
| 60505 | + db->errCode = rc; | |
| 60506 | + }else{ | |
| 60507 | + sqlite3Error(db, rc, 0); | |
| 60508 | + } | |
| 60509 | + return rc; | |
| 60510 | +} | |
| 60485 | 60511 | |
| 60486 | 60512 | /* |
| 60487 | 60513 | ** Clean up a VDBE after execution but do not delete the VDBE just yet. |
| 60488 | 60514 | ** Write any error messages into *pzErrMsg. Return the result code. |
| 60489 | 60515 | ** |
| @@ -60508,22 +60534,13 @@ | ||
| 60508 | 60534 | ** and error message from the VDBE into the main database structure. But |
| 60509 | 60535 | ** if the VDBE has just been set to run but has not actually executed any |
| 60510 | 60536 | ** instructions yet, leave the main database error information unchanged. |
| 60511 | 60537 | */ |
| 60512 | 60538 | if( p->pc>=0 ){ |
| 60513 | - if( p->zErrMsg ){ | |
| 60514 | - sqlite3BeginBenignMalloc(); | |
| 60515 | - sqlite3ValueSetStr(db->pErr,-1,p->zErrMsg,SQLITE_UTF8,SQLITE_TRANSIENT); | |
| 60516 | - sqlite3EndBenignMalloc(); | |
| 60517 | - db->errCode = p->rc; | |
| 60518 | - sqlite3DbFree(db, p->zErrMsg); | |
| 60519 | - p->zErrMsg = 0; | |
| 60520 | - }else if( p->rc ){ | |
| 60521 | - sqlite3Error(db, p->rc, 0); | |
| 60522 | - }else{ | |
| 60523 | - sqlite3Error(db, SQLITE_OK, 0); | |
| 60524 | - } | |
| 60539 | + sqlite3VdbeTransferError(p); | |
| 60540 | + sqlite3DbFree(db, p->zErrMsg); | |
| 60541 | + p->zErrMsg = 0; | |
| 60525 | 60542 | if( p->runOnlyOnce ) p->expired = 1; |
| 60526 | 60543 | }else if( p->rc && p->expired ){ |
| 60527 | 60544 | /* The expired flag was set on the VDBE before the first call |
| 60528 | 60545 | ** to sqlite3_step(). For consistency (since sqlite3_step() was |
| 60529 | 60546 | ** called), set the database error in this case as well. |
| @@ -61865,11 +61882,11 @@ | ||
| 61865 | 61882 | if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ |
| 61866 | 61883 | /* If this statement was prepared using sqlite3_prepare_v2(), and an |
| 61867 | 61884 | ** error has occured, then return the error code in p->rc to the |
| 61868 | 61885 | ** caller. Set the error code in the database handle to the same value. |
| 61869 | 61886 | */ |
| 61870 | - rc = db->errCode = p->rc; | |
| 61887 | + rc = sqlite3VdbeTransferError(p); | |
| 61871 | 61888 | } |
| 61872 | 61889 | return (rc&db->errMask); |
| 61873 | 61890 | } |
| 61874 | 61891 | |
| 61875 | 61892 | /* |
| @@ -67765,13 +67782,12 @@ | ||
| 67765 | 67782 | |
| 67766 | 67783 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 67767 | 67784 | u.bn.pC = p->apCsr[pOp->p1]; |
| 67768 | 67785 | assert( u.bn.pC!=0 ); |
| 67769 | 67786 | u.bn.pCrsr = u.bn.pC->pCursor; |
| 67770 | - if( NEVER(u.bn.pCrsr==0) ){ | |
| 67771 | - u.bn.res = 1; | |
| 67772 | - }else{ | |
| 67787 | + u.bn.res = 0; | |
| 67788 | + if( ALWAYS(u.bn.pCrsr!=0) ){ | |
| 67773 | 67789 | rc = sqlite3BtreeLast(u.bn.pCrsr, &u.bn.res); |
| 67774 | 67790 | } |
| 67775 | 67791 | u.bn.pC->nullRow = (u8)u.bn.res; |
| 67776 | 67792 | u.bn.pC->deferredMoveto = 0; |
| 67777 | 67793 | u.bn.pC->rowidIsValid = 0; |
| @@ -77610,20 +77626,20 @@ | ||
| 77610 | 77626 | #ifndef SQLITE_OMIT_ANALYZE |
| 77611 | 77627 | |
| 77612 | 77628 | /* |
| 77613 | 77629 | ** This routine generates code that opens the sqlite_stat1 table for |
| 77614 | 77630 | ** writing with cursor iStatCur. If the library was built with the |
| 77615 | -** SQLITE_ENABLE_STAT2 macro defined, then the sqlite_stat2 table is | |
| 77631 | +** SQLITE_ENABLE_STAT3 macro defined, then the sqlite_stat3 table is | |
| 77616 | 77632 | ** opened for writing using cursor (iStatCur+1) |
| 77617 | 77633 | ** |
| 77618 | 77634 | ** If the sqlite_stat1 tables does not previously exist, it is created. |
| 77619 | -** Similarly, if the sqlite_stat2 table does not exist and the library | |
| 77620 | -** is compiled with SQLITE_ENABLE_STAT2 defined, it is created. | |
| 77635 | +** Similarly, if the sqlite_stat3 table does not exist and the library | |
| 77636 | +** is compiled with SQLITE_ENABLE_STAT3 defined, it is created. | |
| 77621 | 77637 | ** |
| 77622 | 77638 | ** Argument zWhere may be a pointer to a buffer containing a table name, |
| 77623 | 77639 | ** or it may be a NULL pointer. If it is not NULL, then all entries in |
| 77624 | -** the sqlite_stat1 and (if applicable) sqlite_stat2 tables associated | |
| 77640 | +** the sqlite_stat1 and (if applicable) sqlite_stat3 tables associated | |
| 77625 | 77641 | ** with the named table are deleted. If zWhere==0, then code is generated |
| 77626 | 77642 | ** to delete all stat table entries. |
| 77627 | 77643 | */ |
| 77628 | 77644 | static void openStatTable( |
| 77629 | 77645 | Parse *pParse, /* Parsing context */ |
| @@ -81404,31 +81420,28 @@ | ||
| 81404 | 81420 | } |
| 81405 | 81421 | #endif |
| 81406 | 81422 | } |
| 81407 | 81423 | |
| 81408 | 81424 | /* |
| 81409 | -** Remove entries from the sqlite_stat1 and sqlite_stat2 tables | |
| 81425 | +** Remove entries from the sqlite_statN tables (for N in (1,2,3)) | |
| 81410 | 81426 | ** after a DROP INDEX or DROP TABLE command. |
| 81411 | 81427 | */ |
| 81412 | 81428 | static void sqlite3ClearStatTables( |
| 81413 | 81429 | Parse *pParse, /* The parsing context */ |
| 81414 | 81430 | int iDb, /* The database number */ |
| 81415 | 81431 | const char *zType, /* "idx" or "tbl" */ |
| 81416 | 81432 | const char *zName /* Name of index or table */ |
| 81417 | 81433 | ){ |
| 81418 | - static const char *azStatTab[] = { | |
| 81419 | - "sqlite_stat1", | |
| 81420 | - "sqlite_stat2", | |
| 81421 | - "sqlite_stat3", | |
| 81422 | - }; | |
| 81423 | 81434 | int i; |
| 81424 | 81435 | const char *zDbName = pParse->db->aDb[iDb].zName; |
| 81425 | - for(i=0; i<ArraySize(azStatTab); i++){ | |
| 81426 | - if( sqlite3FindTable(pParse->db, azStatTab[i], zDbName) ){ | |
| 81436 | + for(i=1; i<=3; i++){ | |
| 81437 | + char zTab[24]; | |
| 81438 | + sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i); | |
| 81439 | + if( sqlite3FindTable(pParse->db, zTab, zDbName) ){ | |
| 81427 | 81440 | sqlite3NestedParse(pParse, |
| 81428 | 81441 | "DELETE FROM %Q.%s WHERE %s=%Q", |
| 81429 | - zDbName, azStatTab[i], zType, zName | |
| 81442 | + zDbName, zTab, zType, zName | |
| 81430 | 81443 | ); |
| 81431 | 81444 | } |
| 81432 | 81445 | } |
| 81433 | 81446 | } |
| 81434 | 81447 | |
| @@ -89250,12 +89263,14 @@ | ||
| 89250 | 89263 | int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*); |
| 89251 | 89264 | int (*busy_handler)(sqlite3*,int(*)(void*,int),void*); |
| 89252 | 89265 | int (*busy_timeout)(sqlite3*,int ms); |
| 89253 | 89266 | int (*changes)(sqlite3*); |
| 89254 | 89267 | int (*close)(sqlite3*); |
| 89255 | - int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const char*)); | |
| 89256 | - int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const void*)); | |
| 89268 | + int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*, | |
| 89269 | + int eTextRep,const char*)); | |
| 89270 | + int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*, | |
| 89271 | + int eTextRep,const void*)); | |
| 89257 | 89272 | const void * (*column_blob)(sqlite3_stmt*,int iCol); |
| 89258 | 89273 | int (*column_bytes)(sqlite3_stmt*,int iCol); |
| 89259 | 89274 | int (*column_bytes16)(sqlite3_stmt*,int iCol); |
| 89260 | 89275 | int (*column_count)(sqlite3_stmt*pStmt); |
| 89261 | 89276 | const char * (*column_database_name)(sqlite3_stmt*,int); |
| @@ -89276,14 +89291,22 @@ | ||
| 89276 | 89291 | int (*column_type)(sqlite3_stmt*,int iCol); |
| 89277 | 89292 | sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); |
| 89278 | 89293 | void * (*commit_hook)(sqlite3*,int(*)(void*),void*); |
| 89279 | 89294 | int (*complete)(const char*sql); |
| 89280 | 89295 | int (*complete16)(const void*sql); |
| 89281 | - int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*)); | |
| 89282 | - int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*)); | |
| 89283 | - int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*)); | |
| 89284 | - int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*)); | |
| 89296 | + int (*create_collation)(sqlite3*,const char*,int,void*, | |
| 89297 | + int(*)(void*,int,const void*,int,const void*)); | |
| 89298 | + int (*create_collation16)(sqlite3*,const void*,int,void*, | |
| 89299 | + int(*)(void*,int,const void*,int,const void*)); | |
| 89300 | + int (*create_function)(sqlite3*,const char*,int,int,void*, | |
| 89301 | + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), | |
| 89302 | + void (*xStep)(sqlite3_context*,int,sqlite3_value**), | |
| 89303 | + void (*xFinal)(sqlite3_context*)); | |
| 89304 | + int (*create_function16)(sqlite3*,const void*,int,int,void*, | |
| 89305 | + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), | |
| 89306 | + void (*xStep)(sqlite3_context*,int,sqlite3_value**), | |
| 89307 | + void (*xFinal)(sqlite3_context*)); | |
| 89285 | 89308 | int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); |
| 89286 | 89309 | int (*data_count)(sqlite3_stmt*pStmt); |
| 89287 | 89310 | sqlite3 * (*db_handle)(sqlite3_stmt*); |
| 89288 | 89311 | int (*declare_vtab)(sqlite3*,const char*); |
| 89289 | 89312 | int (*enable_shared_cache)(int); |
| @@ -89324,20 +89347,23 @@ | ||
| 89324 | 89347 | void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); |
| 89325 | 89348 | void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); |
| 89326 | 89349 | void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); |
| 89327 | 89350 | void (*result_value)(sqlite3_context*,sqlite3_value*); |
| 89328 | 89351 | void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); |
| 89329 | - int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,const char*,const char*),void*); | |
| 89352 | + int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, | |
| 89353 | + const char*,const char*),void*); | |
| 89330 | 89354 | void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); |
| 89331 | 89355 | char * (*snprintf)(int,char*,const char*,...); |
| 89332 | 89356 | int (*step)(sqlite3_stmt*); |
| 89333 | - int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,char const**,char const**,int*,int*,int*); | |
| 89357 | + int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*, | |
| 89358 | + char const**,char const**,int*,int*,int*); | |
| 89334 | 89359 | void (*thread_cleanup)(void); |
| 89335 | 89360 | int (*total_changes)(sqlite3*); |
| 89336 | 89361 | void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*); |
| 89337 | 89362 | int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*); |
| 89338 | - void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,sqlite_int64),void*); | |
| 89363 | + void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*, | |
| 89364 | + sqlite_int64),void*); | |
| 89339 | 89365 | void * (*user_data)(sqlite3_context*); |
| 89340 | 89366 | const void * (*value_blob)(sqlite3_value*); |
| 89341 | 89367 | int (*value_bytes)(sqlite3_value*); |
| 89342 | 89368 | int (*value_bytes16)(sqlite3_value*); |
| 89343 | 89369 | double (*value_double)(sqlite3_value*); |
| @@ -89355,19 +89381,23 @@ | ||
| 89355 | 89381 | /* Added by 3.3.13 */ |
| 89356 | 89382 | int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); |
| 89357 | 89383 | int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); |
| 89358 | 89384 | int (*clear_bindings)(sqlite3_stmt*); |
| 89359 | 89385 | /* Added by 3.4.1 */ |
| 89360 | - int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,void (*xDestroy)(void *)); | |
| 89386 | + int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*, | |
| 89387 | + void (*xDestroy)(void *)); | |
| 89361 | 89388 | /* Added by 3.5.0 */ |
| 89362 | 89389 | int (*bind_zeroblob)(sqlite3_stmt*,int,int); |
| 89363 | 89390 | int (*blob_bytes)(sqlite3_blob*); |
| 89364 | 89391 | int (*blob_close)(sqlite3_blob*); |
| 89365 | - int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,int,sqlite3_blob**); | |
| 89392 | + int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64, | |
| 89393 | + int,sqlite3_blob**); | |
| 89366 | 89394 | int (*blob_read)(sqlite3_blob*,void*,int,int); |
| 89367 | 89395 | int (*blob_write)(sqlite3_blob*,const void*,int,int); |
| 89368 | - int (*create_collation_v2)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*),void(*)(void*)); | |
| 89396 | + int (*create_collation_v2)(sqlite3*,const char*,int,void*, | |
| 89397 | + int(*)(void*,int,const void*,int,const void*), | |
| 89398 | + void(*)(void*)); | |
| 89369 | 89399 | int (*file_control)(sqlite3*,const char*,int,void*); |
| 89370 | 89400 | sqlite3_int64 (*memory_highwater)(int); |
| 89371 | 89401 | sqlite3_int64 (*memory_used)(void); |
| 89372 | 89402 | sqlite3_mutex *(*mutex_alloc)(int); |
| 89373 | 89403 | void (*mutex_enter)(sqlite3_mutex*); |
| @@ -89399,11 +89429,15 @@ | ||
| 89399 | 89429 | int (*backup_pagecount)(sqlite3_backup*); |
| 89400 | 89430 | int (*backup_remaining)(sqlite3_backup*); |
| 89401 | 89431 | int (*backup_step)(sqlite3_backup*,int); |
| 89402 | 89432 | const char *(*compileoption_get)(int); |
| 89403 | 89433 | int (*compileoption_used)(const char*); |
| 89404 | - int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*)); | |
| 89434 | + int (*create_function_v2)(sqlite3*,const char*,int,int,void*, | |
| 89435 | + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), | |
| 89436 | + void (*xStep)(sqlite3_context*,int,sqlite3_value**), | |
| 89437 | + void (*xFinal)(sqlite3_context*), | |
| 89438 | + void(*xDestroy)(void*)); | |
| 89405 | 89439 | int (*db_config)(sqlite3*,int,...); |
| 89406 | 89440 | sqlite3_mutex *(*db_mutex)(sqlite3*); |
| 89407 | 89441 | int (*db_status)(sqlite3*,int,int*,int*,int); |
| 89408 | 89442 | int (*extended_errcode)(sqlite3*); |
| 89409 | 89443 | void (*log)(int,const char*,...); |
| @@ -100485,11 +100519,11 @@ | ||
| 100485 | 100519 | if( db->aVTrans ){ |
| 100486 | 100520 | int i; |
| 100487 | 100521 | for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){ |
| 100488 | 100522 | VTable *pVTab = db->aVTrans[i]; |
| 100489 | 100523 | const sqlite3_module *pMod = pVTab->pMod->pModule; |
| 100490 | - if( pMod->iVersion>=2 ){ | |
| 100524 | + if( pVTab->pVtab && pMod->iVersion>=2 ){ | |
| 100491 | 100525 | int (*xMethod)(sqlite3_vtab *, int); |
| 100492 | 100526 | switch( op ){ |
| 100493 | 100527 | case SAVEPOINT_BEGIN: |
| 100494 | 100528 | xMethod = pMod->xSavepoint; |
| 100495 | 100529 | pVTab->iSavepoint = iSavepoint+1; |
| @@ -100500,11 +100534,11 @@ | ||
| 100500 | 100534 | default: |
| 100501 | 100535 | xMethod = pMod->xRelease; |
| 100502 | 100536 | break; |
| 100503 | 100537 | } |
| 100504 | 100538 | if( xMethod && pVTab->iSavepoint>iSavepoint ){ |
| 100505 | - rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint); | |
| 100539 | + rc = xMethod(pVTab->pVtab, iSavepoint); | |
| 100506 | 100540 | } |
| 100507 | 100541 | } |
| 100508 | 100542 | } |
| 100509 | 100543 | } |
| 100510 | 100544 | return rc; |
| @@ -101367,11 +101401,11 @@ | ||
| 101367 | 101401 | int iCol = pRight->iColumn; |
| 101368 | 101402 | pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE); |
| 101369 | 101403 | if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ |
| 101370 | 101404 | z = (char *)sqlite3_value_text(pVal); |
| 101371 | 101405 | } |
| 101372 | - sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-31526-56213 */ | |
| 101406 | + sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); | |
| 101373 | 101407 | assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); |
| 101374 | 101408 | }else if( op==TK_STRING ){ |
| 101375 | 101409 | z = pRight->u.zToken; |
| 101376 | 101410 | } |
| 101377 | 101411 | if( z ){ |
| @@ -101385,11 +101419,11 @@ | ||
| 101385 | 101419 | pPrefix = sqlite3Expr(db, TK_STRING, z); |
| 101386 | 101420 | if( pPrefix ) pPrefix->u.zToken[cnt] = 0; |
| 101387 | 101421 | *ppPrefix = pPrefix; |
| 101388 | 101422 | if( op==TK_VARIABLE ){ |
| 101389 | 101423 | Vdbe *v = pParse->pVdbe; |
| 101390 | - sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-31526-56213 */ | |
| 101424 | + sqlite3VdbeSetVarmask(v, pRight->iColumn); | |
| 101391 | 101425 | if( *pisComplete && pRight->u.zToken[1] ){ |
| 101392 | 101426 | /* If the rhs of the LIKE expression is a variable, and the current |
| 101393 | 101427 | ** value of the variable means there is no need to invoke the LIKE |
| 101394 | 101428 | ** function, then no OP_Variable will be added to the program. |
| 101395 | 101429 | ** This causes problems for the sqlite3_bind_parameter_name() |
| @@ -103299,11 +103333,11 @@ | ||
| 103299 | 103333 | ){ |
| 103300 | 103334 | if( pExpr->op==TK_VARIABLE |
| 103301 | 103335 | || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) |
| 103302 | 103336 | ){ |
| 103303 | 103337 | int iVar = pExpr->iColumn; |
| 103304 | - sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-31526-56213 */ | |
| 103338 | + sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); | |
| 103305 | 103339 | *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); |
| 103306 | 103340 | return SQLITE_OK; |
| 103307 | 103341 | } |
| 103308 | 103342 | return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); |
| 103309 | 103343 | } |
| @@ -103829,11 +103863,11 @@ | ||
| 103829 | 103863 | ** a table or index. The actual times can vary, with the size of |
| 103830 | 103864 | ** records being an important factor. Both moves and searches are |
| 103831 | 103865 | ** slower with larger records, presumably because fewer records fit |
| 103832 | 103866 | ** on one page and hence more pages have to be fetched. |
| 103833 | 103867 | ** |
| 103834 | - ** The ANALYZE command and the sqlite_stat1 and sqlite_stat2 tables do | |
| 103868 | + ** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do | |
| 103835 | 103869 | ** not give us data on the relative sizes of table and index records. |
| 103836 | 103870 | ** So this computation assumes table records are about twice as big |
| 103837 | 103871 | ** as index records |
| 103838 | 103872 | */ |
| 103839 | 103873 | if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 103840 | 103874 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -314,17 +314,10 @@ | |
| 314 | #endif |
| 315 | #ifdef HAVE_INTTYPES_H |
| 316 | #include <inttypes.h> |
| 317 | #endif |
| 318 | |
| 319 | /* |
| 320 | ** The number of samples of an index that SQLite takes in order to |
| 321 | ** construct a histogram of the table content when running ANALYZE |
| 322 | ** and with SQLITE_ENABLE_STAT2 |
| 323 | */ |
| 324 | #define SQLITE_INDEX_SAMPLES 10 |
| 325 | |
| 326 | /* |
| 327 | ** The following macros are used to cast pointers to integers and |
| 328 | ** integers to pointers. The way you do this varies from one compiler |
| 329 | ** to the next, so we have developed the following set of #if statements |
| 330 | ** to generate appropriate macros for a wide range of compilers. |
| @@ -656,11 +649,11 @@ | |
| 656 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 657 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 658 | */ |
| 659 | #define SQLITE_VERSION "3.7.9" |
| 660 | #define SQLITE_VERSION_NUMBER 3007009 |
| 661 | #define SQLITE_SOURCE_ID "2011-10-20 00:55:54 4344483f7d7f64dffadde0053e6c745948db9486" |
| 662 | |
| 663 | /* |
| 664 | ** CAPI3REF: Run-Time Library Version Numbers |
| 665 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 666 | ** |
| @@ -8792,10 +8785,11 @@ | |
| 8792 | SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); |
| 8793 | SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); |
| 8794 | SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); |
| 8795 | SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); |
| 8796 | SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); |
| 8797 | |
| 8798 | /* Functions used to truncate the database file. */ |
| 8799 | SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); |
| 8800 | |
| 8801 | #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) |
| @@ -10189,12 +10183,13 @@ | |
| 10189 | IndexSample *aSample; /* Samples of the left-most key */ |
| 10190 | #endif |
| 10191 | }; |
| 10192 | |
| 10193 | /* |
| 10194 | ** Each sample stored in the sqlite_stat2 table is represented in memory |
| 10195 | ** using a structure of this type. |
| 10196 | */ |
| 10197 | struct IndexSample { |
| 10198 | union { |
| 10199 | char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ |
| 10200 | double r; /* Value if eType is SQLITE_FLOAT */ |
| @@ -12293,13 +12288,10 @@ | |
| 12293 | "ENABLE_OVERSIZE_CELL_CHECK", |
| 12294 | #endif |
| 12295 | #ifdef SQLITE_ENABLE_RTREE |
| 12296 | "ENABLE_RTREE", |
| 12297 | #endif |
| 12298 | #ifdef SQLITE_ENABLE_STAT2 |
| 12299 | "ENABLE_STAT2", |
| 12300 | #endif |
| 12301 | #ifdef SQLITE_ENABLE_STAT3 |
| 12302 | "ENABLE_STAT3", |
| 12303 | #endif |
| 12304 | #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY |
| 12305 | "ENABLE_UNLOCK_NOTIFY", |
| @@ -12996,10 +12988,11 @@ | |
| 12996 | SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); |
| 12997 | SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); |
| 12998 | SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); |
| 12999 | SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *); |
| 13000 | SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem); |
| 13001 | |
| 13002 | #ifdef SQLITE_OMIT_MERGE_SORT |
| 13003 | # define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK |
| 13004 | # define sqlite3VdbeSorterWrite(X,Y,Z) SQLITE_OK |
| 13005 | # define sqlite3VdbeSorterClose(Y,Z) |
| @@ -29483,17 +29476,17 @@ | |
| 29483 | ** "<path to db>-journal" |
| 29484 | ** "<path to db>-wal" |
| 29485 | ** "<path to db>-journalNN" |
| 29486 | ** "<path to db>-walNN" |
| 29487 | ** |
| 29488 | ** where NN is a 4 digit decimal number. The NN naming schemes are |
| 29489 | ** used by the test_multiplex.c module. |
| 29490 | */ |
| 29491 | nDb = sqlite3Strlen30(zPath) - 1; |
| 29492 | #ifdef SQLITE_ENABLE_8_3_NAMES |
| 29493 | while( nDb>0 && zPath[nDb]!='-' && zPath[nDb]!='/' ) nDb--; |
| 29494 | if( nDb==0 || zPath[nDb]=='/' ) return SQLITE_OK; |
| 29495 | #else |
| 29496 | while( zPath[nDb]!='-' ){ |
| 29497 | assert( nDb>0 ); |
| 29498 | assert( zPath[nDb]!='\n' ); |
| 29499 | nDb--; |
| @@ -44163,10 +44156,17 @@ | |
| 44163 | pPager->pWal = 0; |
| 44164 | } |
| 44165 | } |
| 44166 | return rc; |
| 44167 | } |
| 44168 | |
| 44169 | #ifdef SQLITE_HAS_CODEC |
| 44170 | /* |
| 44171 | ** This function is called by the wal module when writing page content |
| 44172 | ** into the log file. |
| @@ -57006,10 +57006,12 @@ | |
| 57006 | sqlite3_backup_step(&b, 0x7FFFFFFF); |
| 57007 | assert( b.rc!=SQLITE_OK ); |
| 57008 | rc = sqlite3_backup_finish(&b); |
| 57009 | if( rc==SQLITE_OK ){ |
| 57010 | pTo->pBt->pageSizeFixed = 0; |
| 57011 | } |
| 57012 | |
| 57013 | assert( sqlite3BtreeIsInTrans(pTo)==0 ); |
| 57014 | sqlite3BtreeLeave(pFrom); |
| 57015 | sqlite3BtreeLeave(pTo); |
| @@ -60480,10 +60482,34 @@ | |
| 60480 | ** in p->rc. This routine sets that result back to SQLITE_OK. |
| 60481 | */ |
| 60482 | SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe *p){ |
| 60483 | p->rc = SQLITE_OK; |
| 60484 | } |
| 60485 | |
| 60486 | /* |
| 60487 | ** Clean up a VDBE after execution but do not delete the VDBE just yet. |
| 60488 | ** Write any error messages into *pzErrMsg. Return the result code. |
| 60489 | ** |
| @@ -60508,22 +60534,13 @@ | |
| 60508 | ** and error message from the VDBE into the main database structure. But |
| 60509 | ** if the VDBE has just been set to run but has not actually executed any |
| 60510 | ** instructions yet, leave the main database error information unchanged. |
| 60511 | */ |
| 60512 | if( p->pc>=0 ){ |
| 60513 | if( p->zErrMsg ){ |
| 60514 | sqlite3BeginBenignMalloc(); |
| 60515 | sqlite3ValueSetStr(db->pErr,-1,p->zErrMsg,SQLITE_UTF8,SQLITE_TRANSIENT); |
| 60516 | sqlite3EndBenignMalloc(); |
| 60517 | db->errCode = p->rc; |
| 60518 | sqlite3DbFree(db, p->zErrMsg); |
| 60519 | p->zErrMsg = 0; |
| 60520 | }else if( p->rc ){ |
| 60521 | sqlite3Error(db, p->rc, 0); |
| 60522 | }else{ |
| 60523 | sqlite3Error(db, SQLITE_OK, 0); |
| 60524 | } |
| 60525 | if( p->runOnlyOnce ) p->expired = 1; |
| 60526 | }else if( p->rc && p->expired ){ |
| 60527 | /* The expired flag was set on the VDBE before the first call |
| 60528 | ** to sqlite3_step(). For consistency (since sqlite3_step() was |
| 60529 | ** called), set the database error in this case as well. |
| @@ -61865,11 +61882,11 @@ | |
| 61865 | if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ |
| 61866 | /* If this statement was prepared using sqlite3_prepare_v2(), and an |
| 61867 | ** error has occured, then return the error code in p->rc to the |
| 61868 | ** caller. Set the error code in the database handle to the same value. |
| 61869 | */ |
| 61870 | rc = db->errCode = p->rc; |
| 61871 | } |
| 61872 | return (rc&db->errMask); |
| 61873 | } |
| 61874 | |
| 61875 | /* |
| @@ -67765,13 +67782,12 @@ | |
| 67765 | |
| 67766 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 67767 | u.bn.pC = p->apCsr[pOp->p1]; |
| 67768 | assert( u.bn.pC!=0 ); |
| 67769 | u.bn.pCrsr = u.bn.pC->pCursor; |
| 67770 | if( NEVER(u.bn.pCrsr==0) ){ |
| 67771 | u.bn.res = 1; |
| 67772 | }else{ |
| 67773 | rc = sqlite3BtreeLast(u.bn.pCrsr, &u.bn.res); |
| 67774 | } |
| 67775 | u.bn.pC->nullRow = (u8)u.bn.res; |
| 67776 | u.bn.pC->deferredMoveto = 0; |
| 67777 | u.bn.pC->rowidIsValid = 0; |
| @@ -77610,20 +77626,20 @@ | |
| 77610 | #ifndef SQLITE_OMIT_ANALYZE |
| 77611 | |
| 77612 | /* |
| 77613 | ** This routine generates code that opens the sqlite_stat1 table for |
| 77614 | ** writing with cursor iStatCur. If the library was built with the |
| 77615 | ** SQLITE_ENABLE_STAT2 macro defined, then the sqlite_stat2 table is |
| 77616 | ** opened for writing using cursor (iStatCur+1) |
| 77617 | ** |
| 77618 | ** If the sqlite_stat1 tables does not previously exist, it is created. |
| 77619 | ** Similarly, if the sqlite_stat2 table does not exist and the library |
| 77620 | ** is compiled with SQLITE_ENABLE_STAT2 defined, it is created. |
| 77621 | ** |
| 77622 | ** Argument zWhere may be a pointer to a buffer containing a table name, |
| 77623 | ** or it may be a NULL pointer. If it is not NULL, then all entries in |
| 77624 | ** the sqlite_stat1 and (if applicable) sqlite_stat2 tables associated |
| 77625 | ** with the named table are deleted. If zWhere==0, then code is generated |
| 77626 | ** to delete all stat table entries. |
| 77627 | */ |
| 77628 | static void openStatTable( |
| 77629 | Parse *pParse, /* Parsing context */ |
| @@ -81404,31 +81420,28 @@ | |
| 81404 | } |
| 81405 | #endif |
| 81406 | } |
| 81407 | |
| 81408 | /* |
| 81409 | ** Remove entries from the sqlite_stat1 and sqlite_stat2 tables |
| 81410 | ** after a DROP INDEX or DROP TABLE command. |
| 81411 | */ |
| 81412 | static void sqlite3ClearStatTables( |
| 81413 | Parse *pParse, /* The parsing context */ |
| 81414 | int iDb, /* The database number */ |
| 81415 | const char *zType, /* "idx" or "tbl" */ |
| 81416 | const char *zName /* Name of index or table */ |
| 81417 | ){ |
| 81418 | static const char *azStatTab[] = { |
| 81419 | "sqlite_stat1", |
| 81420 | "sqlite_stat2", |
| 81421 | "sqlite_stat3", |
| 81422 | }; |
| 81423 | int i; |
| 81424 | const char *zDbName = pParse->db->aDb[iDb].zName; |
| 81425 | for(i=0; i<ArraySize(azStatTab); i++){ |
| 81426 | if( sqlite3FindTable(pParse->db, azStatTab[i], zDbName) ){ |
| 81427 | sqlite3NestedParse(pParse, |
| 81428 | "DELETE FROM %Q.%s WHERE %s=%Q", |
| 81429 | zDbName, azStatTab[i], zType, zName |
| 81430 | ); |
| 81431 | } |
| 81432 | } |
| 81433 | } |
| 81434 | |
| @@ -89250,12 +89263,14 @@ | |
| 89250 | int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*); |
| 89251 | int (*busy_handler)(sqlite3*,int(*)(void*,int),void*); |
| 89252 | int (*busy_timeout)(sqlite3*,int ms); |
| 89253 | int (*changes)(sqlite3*); |
| 89254 | int (*close)(sqlite3*); |
| 89255 | int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const char*)); |
| 89256 | int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const void*)); |
| 89257 | const void * (*column_blob)(sqlite3_stmt*,int iCol); |
| 89258 | int (*column_bytes)(sqlite3_stmt*,int iCol); |
| 89259 | int (*column_bytes16)(sqlite3_stmt*,int iCol); |
| 89260 | int (*column_count)(sqlite3_stmt*pStmt); |
| 89261 | const char * (*column_database_name)(sqlite3_stmt*,int); |
| @@ -89276,14 +89291,22 @@ | |
| 89276 | int (*column_type)(sqlite3_stmt*,int iCol); |
| 89277 | sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); |
| 89278 | void * (*commit_hook)(sqlite3*,int(*)(void*),void*); |
| 89279 | int (*complete)(const char*sql); |
| 89280 | int (*complete16)(const void*sql); |
| 89281 | int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*)); |
| 89282 | int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*)); |
| 89283 | int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*)); |
| 89284 | int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*)); |
| 89285 | int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); |
| 89286 | int (*data_count)(sqlite3_stmt*pStmt); |
| 89287 | sqlite3 * (*db_handle)(sqlite3_stmt*); |
| 89288 | int (*declare_vtab)(sqlite3*,const char*); |
| 89289 | int (*enable_shared_cache)(int); |
| @@ -89324,20 +89347,23 @@ | |
| 89324 | void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); |
| 89325 | void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); |
| 89326 | void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); |
| 89327 | void (*result_value)(sqlite3_context*,sqlite3_value*); |
| 89328 | void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); |
| 89329 | int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,const char*,const char*),void*); |
| 89330 | void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); |
| 89331 | char * (*snprintf)(int,char*,const char*,...); |
| 89332 | int (*step)(sqlite3_stmt*); |
| 89333 | int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,char const**,char const**,int*,int*,int*); |
| 89334 | void (*thread_cleanup)(void); |
| 89335 | int (*total_changes)(sqlite3*); |
| 89336 | void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*); |
| 89337 | int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*); |
| 89338 | void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,sqlite_int64),void*); |
| 89339 | void * (*user_data)(sqlite3_context*); |
| 89340 | const void * (*value_blob)(sqlite3_value*); |
| 89341 | int (*value_bytes)(sqlite3_value*); |
| 89342 | int (*value_bytes16)(sqlite3_value*); |
| 89343 | double (*value_double)(sqlite3_value*); |
| @@ -89355,19 +89381,23 @@ | |
| 89355 | /* Added by 3.3.13 */ |
| 89356 | int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); |
| 89357 | int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); |
| 89358 | int (*clear_bindings)(sqlite3_stmt*); |
| 89359 | /* Added by 3.4.1 */ |
| 89360 | int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,void (*xDestroy)(void *)); |
| 89361 | /* Added by 3.5.0 */ |
| 89362 | int (*bind_zeroblob)(sqlite3_stmt*,int,int); |
| 89363 | int (*blob_bytes)(sqlite3_blob*); |
| 89364 | int (*blob_close)(sqlite3_blob*); |
| 89365 | int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,int,sqlite3_blob**); |
| 89366 | int (*blob_read)(sqlite3_blob*,void*,int,int); |
| 89367 | int (*blob_write)(sqlite3_blob*,const void*,int,int); |
| 89368 | int (*create_collation_v2)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*),void(*)(void*)); |
| 89369 | int (*file_control)(sqlite3*,const char*,int,void*); |
| 89370 | sqlite3_int64 (*memory_highwater)(int); |
| 89371 | sqlite3_int64 (*memory_used)(void); |
| 89372 | sqlite3_mutex *(*mutex_alloc)(int); |
| 89373 | void (*mutex_enter)(sqlite3_mutex*); |
| @@ -89399,11 +89429,15 @@ | |
| 89399 | int (*backup_pagecount)(sqlite3_backup*); |
| 89400 | int (*backup_remaining)(sqlite3_backup*); |
| 89401 | int (*backup_step)(sqlite3_backup*,int); |
| 89402 | const char *(*compileoption_get)(int); |
| 89403 | int (*compileoption_used)(const char*); |
| 89404 | int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*)); |
| 89405 | int (*db_config)(sqlite3*,int,...); |
| 89406 | sqlite3_mutex *(*db_mutex)(sqlite3*); |
| 89407 | int (*db_status)(sqlite3*,int,int*,int*,int); |
| 89408 | int (*extended_errcode)(sqlite3*); |
| 89409 | void (*log)(int,const char*,...); |
| @@ -100485,11 +100519,11 @@ | |
| 100485 | if( db->aVTrans ){ |
| 100486 | int i; |
| 100487 | for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){ |
| 100488 | VTable *pVTab = db->aVTrans[i]; |
| 100489 | const sqlite3_module *pMod = pVTab->pMod->pModule; |
| 100490 | if( pMod->iVersion>=2 ){ |
| 100491 | int (*xMethod)(sqlite3_vtab *, int); |
| 100492 | switch( op ){ |
| 100493 | case SAVEPOINT_BEGIN: |
| 100494 | xMethod = pMod->xSavepoint; |
| 100495 | pVTab->iSavepoint = iSavepoint+1; |
| @@ -100500,11 +100534,11 @@ | |
| 100500 | default: |
| 100501 | xMethod = pMod->xRelease; |
| 100502 | break; |
| 100503 | } |
| 100504 | if( xMethod && pVTab->iSavepoint>iSavepoint ){ |
| 100505 | rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint); |
| 100506 | } |
| 100507 | } |
| 100508 | } |
| 100509 | } |
| 100510 | return rc; |
| @@ -101367,11 +101401,11 @@ | |
| 101367 | int iCol = pRight->iColumn; |
| 101368 | pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE); |
| 101369 | if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ |
| 101370 | z = (char *)sqlite3_value_text(pVal); |
| 101371 | } |
| 101372 | sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-31526-56213 */ |
| 101373 | assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); |
| 101374 | }else if( op==TK_STRING ){ |
| 101375 | z = pRight->u.zToken; |
| 101376 | } |
| 101377 | if( z ){ |
| @@ -101385,11 +101419,11 @@ | |
| 101385 | pPrefix = sqlite3Expr(db, TK_STRING, z); |
| 101386 | if( pPrefix ) pPrefix->u.zToken[cnt] = 0; |
| 101387 | *ppPrefix = pPrefix; |
| 101388 | if( op==TK_VARIABLE ){ |
| 101389 | Vdbe *v = pParse->pVdbe; |
| 101390 | sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-31526-56213 */ |
| 101391 | if( *pisComplete && pRight->u.zToken[1] ){ |
| 101392 | /* If the rhs of the LIKE expression is a variable, and the current |
| 101393 | ** value of the variable means there is no need to invoke the LIKE |
| 101394 | ** function, then no OP_Variable will be added to the program. |
| 101395 | ** This causes problems for the sqlite3_bind_parameter_name() |
| @@ -103299,11 +103333,11 @@ | |
| 103299 | ){ |
| 103300 | if( pExpr->op==TK_VARIABLE |
| 103301 | || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) |
| 103302 | ){ |
| 103303 | int iVar = pExpr->iColumn; |
| 103304 | sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-31526-56213 */ |
| 103305 | *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); |
| 103306 | return SQLITE_OK; |
| 103307 | } |
| 103308 | return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); |
| 103309 | } |
| @@ -103829,11 +103863,11 @@ | |
| 103829 | ** a table or index. The actual times can vary, with the size of |
| 103830 | ** records being an important factor. Both moves and searches are |
| 103831 | ** slower with larger records, presumably because fewer records fit |
| 103832 | ** on one page and hence more pages have to be fetched. |
| 103833 | ** |
| 103834 | ** The ANALYZE command and the sqlite_stat1 and sqlite_stat2 tables do |
| 103835 | ** not give us data on the relative sizes of table and index records. |
| 103836 | ** So this computation assumes table records are about twice as big |
| 103837 | ** as index records |
| 103838 | */ |
| 103839 | if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 103840 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -314,17 +314,10 @@ | |
| 314 | #endif |
| 315 | #ifdef HAVE_INTTYPES_H |
| 316 | #include <inttypes.h> |
| 317 | #endif |
| 318 | |
| 319 | /* |
| 320 | ** The following macros are used to cast pointers to integers and |
| 321 | ** integers to pointers. The way you do this varies from one compiler |
| 322 | ** to the next, so we have developed the following set of #if statements |
| 323 | ** to generate appropriate macros for a wide range of compilers. |
| @@ -656,11 +649,11 @@ | |
| 649 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 650 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 651 | */ |
| 652 | #define SQLITE_VERSION "3.7.9" |
| 653 | #define SQLITE_VERSION_NUMBER 3007009 |
| 654 | #define SQLITE_SOURCE_ID "2011-10-29 19:25:08 5b82ec6fbbd2f4195ad06dd911de3817373ad5bf" |
| 655 | |
| 656 | /* |
| 657 | ** CAPI3REF: Run-Time Library Version Numbers |
| 658 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 659 | ** |
| @@ -8792,10 +8785,11 @@ | |
| 8785 | SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); |
| 8786 | SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); |
| 8787 | SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); |
| 8788 | SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); |
| 8789 | SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); |
| 8790 | SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *); |
| 8791 | |
| 8792 | /* Functions used to truncate the database file. */ |
| 8793 | SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); |
| 8794 | |
| 8795 | #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) |
| @@ -10189,12 +10183,13 @@ | |
| 10183 | IndexSample *aSample; /* Samples of the left-most key */ |
| 10184 | #endif |
| 10185 | }; |
| 10186 | |
| 10187 | /* |
| 10188 | ** Each sample stored in the sqlite_stat3 table is represented in memory |
| 10189 | ** using a structure of this type. See documentation at the top of the |
| 10190 | ** analyze.c source file for additional information. |
| 10191 | */ |
| 10192 | struct IndexSample { |
| 10193 | union { |
| 10194 | char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ |
| 10195 | double r; /* Value if eType is SQLITE_FLOAT */ |
| @@ -12293,13 +12288,10 @@ | |
| 12288 | "ENABLE_OVERSIZE_CELL_CHECK", |
| 12289 | #endif |
| 12290 | #ifdef SQLITE_ENABLE_RTREE |
| 12291 | "ENABLE_RTREE", |
| 12292 | #endif |
| 12293 | #ifdef SQLITE_ENABLE_STAT3 |
| 12294 | "ENABLE_STAT3", |
| 12295 | #endif |
| 12296 | #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY |
| 12297 | "ENABLE_UNLOCK_NOTIFY", |
| @@ -12996,10 +12988,11 @@ | |
| 12988 | SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); |
| 12989 | SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); |
| 12990 | SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); |
| 12991 | SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *); |
| 12992 | SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem); |
| 12993 | SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p); |
| 12994 | |
| 12995 | #ifdef SQLITE_OMIT_MERGE_SORT |
| 12996 | # define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK |
| 12997 | # define sqlite3VdbeSorterWrite(X,Y,Z) SQLITE_OK |
| 12998 | # define sqlite3VdbeSorterClose(Y,Z) |
| @@ -29483,17 +29476,17 @@ | |
| 29476 | ** "<path to db>-journal" |
| 29477 | ** "<path to db>-wal" |
| 29478 | ** "<path to db>-journalNN" |
| 29479 | ** "<path to db>-walNN" |
| 29480 | ** |
| 29481 | ** where NN is a decimal number. The NN naming schemes are |
| 29482 | ** used by the test_multiplex.c module. |
| 29483 | */ |
| 29484 | nDb = sqlite3Strlen30(zPath) - 1; |
| 29485 | #ifdef SQLITE_ENABLE_8_3_NAMES |
| 29486 | while( nDb>0 && !sqlite3Isalnum(zPath[nDb]) ) nDb--; |
| 29487 | if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK; |
| 29488 | #else |
| 29489 | while( zPath[nDb]!='-' ){ |
| 29490 | assert( nDb>0 ); |
| 29491 | assert( zPath[nDb]!='\n' ); |
| 29492 | nDb--; |
| @@ -44163,10 +44156,17 @@ | |
| 44156 | pPager->pWal = 0; |
| 44157 | } |
| 44158 | } |
| 44159 | return rc; |
| 44160 | } |
| 44161 | |
| 44162 | /* |
| 44163 | ** Unless this is an in-memory or temporary database, clear the pager cache. |
| 44164 | */ |
| 44165 | SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *pPager){ |
| 44166 | if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager); |
| 44167 | } |
| 44168 | |
| 44169 | #ifdef SQLITE_HAS_CODEC |
| 44170 | /* |
| 44171 | ** This function is called by the wal module when writing page content |
| 44172 | ** into the log file. |
| @@ -57006,10 +57006,12 @@ | |
| 57006 | sqlite3_backup_step(&b, 0x7FFFFFFF); |
| 57007 | assert( b.rc!=SQLITE_OK ); |
| 57008 | rc = sqlite3_backup_finish(&b); |
| 57009 | if( rc==SQLITE_OK ){ |
| 57010 | pTo->pBt->pageSizeFixed = 0; |
| 57011 | }else{ |
| 57012 | sqlite3PagerClearCache(sqlite3BtreePager(b.pDest)); |
| 57013 | } |
| 57014 | |
| 57015 | assert( sqlite3BtreeIsInTrans(pTo)==0 ); |
| 57016 | sqlite3BtreeLeave(pFrom); |
| 57017 | sqlite3BtreeLeave(pTo); |
| @@ -60480,10 +60482,34 @@ | |
| 60482 | ** in p->rc. This routine sets that result back to SQLITE_OK. |
| 60483 | */ |
| 60484 | SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe *p){ |
| 60485 | p->rc = SQLITE_OK; |
| 60486 | } |
| 60487 | |
| 60488 | /* |
| 60489 | ** Copy the error code and error message belonging to the VDBE passed |
| 60490 | ** as the first argument to its database handle (so that they will be |
| 60491 | ** returned by calls to sqlite3_errcode() and sqlite3_errmsg()). |
| 60492 | ** |
| 60493 | ** This function does not clear the VDBE error code or message, just |
| 60494 | ** copies them to the database handle. |
| 60495 | */ |
| 60496 | SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){ |
| 60497 | sqlite3 *db = p->db; |
| 60498 | int rc = p->rc; |
| 60499 | if( p->zErrMsg ){ |
| 60500 | u8 mallocFailed = db->mallocFailed; |
| 60501 | sqlite3BeginBenignMalloc(); |
| 60502 | sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); |
| 60503 | sqlite3EndBenignMalloc(); |
| 60504 | db->mallocFailed = mallocFailed; |
| 60505 | db->errCode = rc; |
| 60506 | }else{ |
| 60507 | sqlite3Error(db, rc, 0); |
| 60508 | } |
| 60509 | return rc; |
| 60510 | } |
| 60511 | |
| 60512 | /* |
| 60513 | ** Clean up a VDBE after execution but do not delete the VDBE just yet. |
| 60514 | ** Write any error messages into *pzErrMsg. Return the result code. |
| 60515 | ** |
| @@ -60508,22 +60534,13 @@ | |
| 60534 | ** and error message from the VDBE into the main database structure. But |
| 60535 | ** if the VDBE has just been set to run but has not actually executed any |
| 60536 | ** instructions yet, leave the main database error information unchanged. |
| 60537 | */ |
| 60538 | if( p->pc>=0 ){ |
| 60539 | sqlite3VdbeTransferError(p); |
| 60540 | sqlite3DbFree(db, p->zErrMsg); |
| 60541 | p->zErrMsg = 0; |
| 60542 | if( p->runOnlyOnce ) p->expired = 1; |
| 60543 | }else if( p->rc && p->expired ){ |
| 60544 | /* The expired flag was set on the VDBE before the first call |
| 60545 | ** to sqlite3_step(). For consistency (since sqlite3_step() was |
| 60546 | ** called), set the database error in this case as well. |
| @@ -61865,11 +61882,11 @@ | |
| 61882 | if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ |
| 61883 | /* If this statement was prepared using sqlite3_prepare_v2(), and an |
| 61884 | ** error has occured, then return the error code in p->rc to the |
| 61885 | ** caller. Set the error code in the database handle to the same value. |
| 61886 | */ |
| 61887 | rc = sqlite3VdbeTransferError(p); |
| 61888 | } |
| 61889 | return (rc&db->errMask); |
| 61890 | } |
| 61891 | |
| 61892 | /* |
| @@ -67765,13 +67782,12 @@ | |
| 67782 | |
| 67783 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 67784 | u.bn.pC = p->apCsr[pOp->p1]; |
| 67785 | assert( u.bn.pC!=0 ); |
| 67786 | u.bn.pCrsr = u.bn.pC->pCursor; |
| 67787 | u.bn.res = 0; |
| 67788 | if( ALWAYS(u.bn.pCrsr!=0) ){ |
| 67789 | rc = sqlite3BtreeLast(u.bn.pCrsr, &u.bn.res); |
| 67790 | } |
| 67791 | u.bn.pC->nullRow = (u8)u.bn.res; |
| 67792 | u.bn.pC->deferredMoveto = 0; |
| 67793 | u.bn.pC->rowidIsValid = 0; |
| @@ -77610,20 +77626,20 @@ | |
| 77626 | #ifndef SQLITE_OMIT_ANALYZE |
| 77627 | |
| 77628 | /* |
| 77629 | ** This routine generates code that opens the sqlite_stat1 table for |
| 77630 | ** writing with cursor iStatCur. If the library was built with the |
| 77631 | ** SQLITE_ENABLE_STAT3 macro defined, then the sqlite_stat3 table is |
| 77632 | ** opened for writing using cursor (iStatCur+1) |
| 77633 | ** |
| 77634 | ** If the sqlite_stat1 tables does not previously exist, it is created. |
| 77635 | ** Similarly, if the sqlite_stat3 table does not exist and the library |
| 77636 | ** is compiled with SQLITE_ENABLE_STAT3 defined, it is created. |
| 77637 | ** |
| 77638 | ** Argument zWhere may be a pointer to a buffer containing a table name, |
| 77639 | ** or it may be a NULL pointer. If it is not NULL, then all entries in |
| 77640 | ** the sqlite_stat1 and (if applicable) sqlite_stat3 tables associated |
| 77641 | ** with the named table are deleted. If zWhere==0, then code is generated |
| 77642 | ** to delete all stat table entries. |
| 77643 | */ |
| 77644 | static void openStatTable( |
| 77645 | Parse *pParse, /* Parsing context */ |
| @@ -81404,31 +81420,28 @@ | |
| 81420 | } |
| 81421 | #endif |
| 81422 | } |
| 81423 | |
| 81424 | /* |
| 81425 | ** Remove entries from the sqlite_statN tables (for N in (1,2,3)) |
| 81426 | ** after a DROP INDEX or DROP TABLE command. |
| 81427 | */ |
| 81428 | static void sqlite3ClearStatTables( |
| 81429 | Parse *pParse, /* The parsing context */ |
| 81430 | int iDb, /* The database number */ |
| 81431 | const char *zType, /* "idx" or "tbl" */ |
| 81432 | const char *zName /* Name of index or table */ |
| 81433 | ){ |
| 81434 | int i; |
| 81435 | const char *zDbName = pParse->db->aDb[iDb].zName; |
| 81436 | for(i=1; i<=3; i++){ |
| 81437 | char zTab[24]; |
| 81438 | sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i); |
| 81439 | if( sqlite3FindTable(pParse->db, zTab, zDbName) ){ |
| 81440 | sqlite3NestedParse(pParse, |
| 81441 | "DELETE FROM %Q.%s WHERE %s=%Q", |
| 81442 | zDbName, zTab, zType, zName |
| 81443 | ); |
| 81444 | } |
| 81445 | } |
| 81446 | } |
| 81447 | |
| @@ -89250,12 +89263,14 @@ | |
| 89263 | int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*); |
| 89264 | int (*busy_handler)(sqlite3*,int(*)(void*,int),void*); |
| 89265 | int (*busy_timeout)(sqlite3*,int ms); |
| 89266 | int (*changes)(sqlite3*); |
| 89267 | int (*close)(sqlite3*); |
| 89268 | int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*, |
| 89269 | int eTextRep,const char*)); |
| 89270 | int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*, |
| 89271 | int eTextRep,const void*)); |
| 89272 | const void * (*column_blob)(sqlite3_stmt*,int iCol); |
| 89273 | int (*column_bytes)(sqlite3_stmt*,int iCol); |
| 89274 | int (*column_bytes16)(sqlite3_stmt*,int iCol); |
| 89275 | int (*column_count)(sqlite3_stmt*pStmt); |
| 89276 | const char * (*column_database_name)(sqlite3_stmt*,int); |
| @@ -89276,14 +89291,22 @@ | |
| 89291 | int (*column_type)(sqlite3_stmt*,int iCol); |
| 89292 | sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); |
| 89293 | void * (*commit_hook)(sqlite3*,int(*)(void*),void*); |
| 89294 | int (*complete)(const char*sql); |
| 89295 | int (*complete16)(const void*sql); |
| 89296 | int (*create_collation)(sqlite3*,const char*,int,void*, |
| 89297 | int(*)(void*,int,const void*,int,const void*)); |
| 89298 | int (*create_collation16)(sqlite3*,const void*,int,void*, |
| 89299 | int(*)(void*,int,const void*,int,const void*)); |
| 89300 | int (*create_function)(sqlite3*,const char*,int,int,void*, |
| 89301 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**), |
| 89302 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), |
| 89303 | void (*xFinal)(sqlite3_context*)); |
| 89304 | int (*create_function16)(sqlite3*,const void*,int,int,void*, |
| 89305 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**), |
| 89306 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), |
| 89307 | void (*xFinal)(sqlite3_context*)); |
| 89308 | int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); |
| 89309 | int (*data_count)(sqlite3_stmt*pStmt); |
| 89310 | sqlite3 * (*db_handle)(sqlite3_stmt*); |
| 89311 | int (*declare_vtab)(sqlite3*,const char*); |
| 89312 | int (*enable_shared_cache)(int); |
| @@ -89324,20 +89347,23 @@ | |
| 89347 | void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); |
| 89348 | void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); |
| 89349 | void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); |
| 89350 | void (*result_value)(sqlite3_context*,sqlite3_value*); |
| 89351 | void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); |
| 89352 | int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, |
| 89353 | const char*,const char*),void*); |
| 89354 | void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); |
| 89355 | char * (*snprintf)(int,char*,const char*,...); |
| 89356 | int (*step)(sqlite3_stmt*); |
| 89357 | int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*, |
| 89358 | char const**,char const**,int*,int*,int*); |
| 89359 | void (*thread_cleanup)(void); |
| 89360 | int (*total_changes)(sqlite3*); |
| 89361 | void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*); |
| 89362 | int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*); |
| 89363 | void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*, |
| 89364 | sqlite_int64),void*); |
| 89365 | void * (*user_data)(sqlite3_context*); |
| 89366 | const void * (*value_blob)(sqlite3_value*); |
| 89367 | int (*value_bytes)(sqlite3_value*); |
| 89368 | int (*value_bytes16)(sqlite3_value*); |
| 89369 | double (*value_double)(sqlite3_value*); |
| @@ -89355,19 +89381,23 @@ | |
| 89381 | /* Added by 3.3.13 */ |
| 89382 | int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); |
| 89383 | int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); |
| 89384 | int (*clear_bindings)(sqlite3_stmt*); |
| 89385 | /* Added by 3.4.1 */ |
| 89386 | int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*, |
| 89387 | void (*xDestroy)(void *)); |
| 89388 | /* Added by 3.5.0 */ |
| 89389 | int (*bind_zeroblob)(sqlite3_stmt*,int,int); |
| 89390 | int (*blob_bytes)(sqlite3_blob*); |
| 89391 | int (*blob_close)(sqlite3_blob*); |
| 89392 | int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64, |
| 89393 | int,sqlite3_blob**); |
| 89394 | int (*blob_read)(sqlite3_blob*,void*,int,int); |
| 89395 | int (*blob_write)(sqlite3_blob*,const void*,int,int); |
| 89396 | int (*create_collation_v2)(sqlite3*,const char*,int,void*, |
| 89397 | int(*)(void*,int,const void*,int,const void*), |
| 89398 | void(*)(void*)); |
| 89399 | int (*file_control)(sqlite3*,const char*,int,void*); |
| 89400 | sqlite3_int64 (*memory_highwater)(int); |
| 89401 | sqlite3_int64 (*memory_used)(void); |
| 89402 | sqlite3_mutex *(*mutex_alloc)(int); |
| 89403 | void (*mutex_enter)(sqlite3_mutex*); |
| @@ -89399,11 +89429,15 @@ | |
| 89429 | int (*backup_pagecount)(sqlite3_backup*); |
| 89430 | int (*backup_remaining)(sqlite3_backup*); |
| 89431 | int (*backup_step)(sqlite3_backup*,int); |
| 89432 | const char *(*compileoption_get)(int); |
| 89433 | int (*compileoption_used)(const char*); |
| 89434 | int (*create_function_v2)(sqlite3*,const char*,int,int,void*, |
| 89435 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**), |
| 89436 | void (*xStep)(sqlite3_context*,int,sqlite3_value**), |
| 89437 | void (*xFinal)(sqlite3_context*), |
| 89438 | void(*xDestroy)(void*)); |
| 89439 | int (*db_config)(sqlite3*,int,...); |
| 89440 | sqlite3_mutex *(*db_mutex)(sqlite3*); |
| 89441 | int (*db_status)(sqlite3*,int,int*,int*,int); |
| 89442 | int (*extended_errcode)(sqlite3*); |
| 89443 | void (*log)(int,const char*,...); |
| @@ -100485,11 +100519,11 @@ | |
| 100519 | if( db->aVTrans ){ |
| 100520 | int i; |
| 100521 | for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){ |
| 100522 | VTable *pVTab = db->aVTrans[i]; |
| 100523 | const sqlite3_module *pMod = pVTab->pMod->pModule; |
| 100524 | if( pVTab->pVtab && pMod->iVersion>=2 ){ |
| 100525 | int (*xMethod)(sqlite3_vtab *, int); |
| 100526 | switch( op ){ |
| 100527 | case SAVEPOINT_BEGIN: |
| 100528 | xMethod = pMod->xSavepoint; |
| 100529 | pVTab->iSavepoint = iSavepoint+1; |
| @@ -100500,11 +100534,11 @@ | |
| 100534 | default: |
| 100535 | xMethod = pMod->xRelease; |
| 100536 | break; |
| 100537 | } |
| 100538 | if( xMethod && pVTab->iSavepoint>iSavepoint ){ |
| 100539 | rc = xMethod(pVTab->pVtab, iSavepoint); |
| 100540 | } |
| 100541 | } |
| 100542 | } |
| 100543 | } |
| 100544 | return rc; |
| @@ -101367,11 +101401,11 @@ | |
| 101401 | int iCol = pRight->iColumn; |
| 101402 | pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE); |
| 101403 | if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ |
| 101404 | z = (char *)sqlite3_value_text(pVal); |
| 101405 | } |
| 101406 | sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); |
| 101407 | assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); |
| 101408 | }else if( op==TK_STRING ){ |
| 101409 | z = pRight->u.zToken; |
| 101410 | } |
| 101411 | if( z ){ |
| @@ -101385,11 +101419,11 @@ | |
| 101419 | pPrefix = sqlite3Expr(db, TK_STRING, z); |
| 101420 | if( pPrefix ) pPrefix->u.zToken[cnt] = 0; |
| 101421 | *ppPrefix = pPrefix; |
| 101422 | if( op==TK_VARIABLE ){ |
| 101423 | Vdbe *v = pParse->pVdbe; |
| 101424 | sqlite3VdbeSetVarmask(v, pRight->iColumn); |
| 101425 | if( *pisComplete && pRight->u.zToken[1] ){ |
| 101426 | /* If the rhs of the LIKE expression is a variable, and the current |
| 101427 | ** value of the variable means there is no need to invoke the LIKE |
| 101428 | ** function, then no OP_Variable will be added to the program. |
| 101429 | ** This causes problems for the sqlite3_bind_parameter_name() |
| @@ -103299,11 +103333,11 @@ | |
| 103333 | ){ |
| 103334 | if( pExpr->op==TK_VARIABLE |
| 103335 | || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) |
| 103336 | ){ |
| 103337 | int iVar = pExpr->iColumn; |
| 103338 | sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); |
| 103339 | *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); |
| 103340 | return SQLITE_OK; |
| 103341 | } |
| 103342 | return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); |
| 103343 | } |
| @@ -103829,11 +103863,11 @@ | |
| 103863 | ** a table or index. The actual times can vary, with the size of |
| 103864 | ** records being an important factor. Both moves and searches are |
| 103865 | ** slower with larger records, presumably because fewer records fit |
| 103866 | ** on one page and hence more pages have to be fetched. |
| 103867 | ** |
| 103868 | ** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do |
| 103869 | ** not give us data on the relative sizes of table and index records. |
| 103870 | ** So this computation assumes table records are about twice as big |
| 103871 | ** as index records |
| 103872 | */ |
| 103873 | if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 103874 |
+1
-1
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -107,11 +107,11 @@ | ||
| 107 | 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | 109 | */ |
| 110 | 110 | #define SQLITE_VERSION "3.7.9" |
| 111 | 111 | #define SQLITE_VERSION_NUMBER 3007009 |
| 112 | -#define SQLITE_SOURCE_ID "2011-10-20 00:55:54 4344483f7d7f64dffadde0053e6c745948db9486" | |
| 112 | +#define SQLITE_SOURCE_ID "2011-10-29 19:25:08 5b82ec6fbbd2f4195ad06dd911de3817373ad5bf" | |
| 113 | 113 | |
| 114 | 114 | /* |
| 115 | 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | 117 | ** |
| 118 | 118 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.7.9" |
| 111 | #define SQLITE_VERSION_NUMBER 3007009 |
| 112 | #define SQLITE_SOURCE_ID "2011-10-20 00:55:54 4344483f7d7f64dffadde0053e6c745948db9486" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.7.9" |
| 111 | #define SQLITE_VERSION_NUMBER 3007009 |
| 112 | #define SQLITE_SOURCE_ID "2011-10-29 19:25:08 5b82ec6fbbd2f4195ad06dd911de3817373ad5bf" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
+56
-26
| --- src/stash.c | ||
| +++ src/stash.c | ||
| @@ -170,11 +170,11 @@ | ||
| 170 | 170 | int i; |
| 171 | 171 | for(i=3; i<g.argc; i++){ |
| 172 | 172 | stash_add_file_or_dir(stashid, vid, g.argv[i]); |
| 173 | 173 | } |
| 174 | 174 | }else{ |
| 175 | - stash_add_file_or_dir(stashid, vid, "."); | |
| 175 | + stash_add_file_or_dir(stashid, vid, g.zLocalRoot); | |
| 176 | 176 | } |
| 177 | 177 | return stashid; |
| 178 | 178 | } |
| 179 | 179 | |
| 180 | 180 | /* |
| @@ -258,12 +258,13 @@ | ||
| 258 | 258 | file_delete(zOPath); |
| 259 | 259 | } |
| 260 | 260 | } |
| 261 | 261 | db_finalize(&q); |
| 262 | 262 | if( nConflict ){ |
| 263 | - fossil_print("WARNING: %d merge conflicts - see messages above for details.\n", | |
| 264 | - nConflict); | |
| 263 | + fossil_print( | |
| 264 | + "WARNING: %d merge conflicts - see messages above for details.\n", | |
| 265 | + nConflict); | |
| 265 | 266 | } |
| 266 | 267 | } |
| 267 | 268 | |
| 268 | 269 | /* |
| 269 | 270 | ** Show the diffs associate with a single stash. |
| @@ -363,35 +364,32 @@ | ||
| 363 | 364 | ** |
| 364 | 365 | ** Usage: %fossil stash SUBCOMMAND ARGS... |
| 365 | 366 | ** |
| 366 | 367 | ** fossil stash |
| 367 | 368 | ** fossil stash save ?-m COMMENT? ?FILES...? |
| 369 | +** fossil stash snapshot ?-m COMMENT? ?FILES...? | |
| 368 | 370 | ** |
| 369 | 371 | ** Save the current changes in the working tree as a new stash. |
| 370 | 372 | ** Then revert the changes back to the last check-in. If FILES |
| 371 | 373 | ** are listed, then only stash and revert the named files. The |
| 372 | 374 | ** "save" verb can be omitted if and only if there are no other |
| 373 | -** arguments. | |
| 375 | +** arguments. The "snapshot" verb works the same as "save" but | |
| 376 | +** omits the revert, keeping the check-out unchanged. | |
| 374 | 377 | ** |
| 375 | -** fossil stash list | |
| 376 | -** fossil stash ls | |
| 378 | +** fossil stash list ?--detail? | |
| 379 | +** fossil stash ls ?-l? | |
| 377 | 380 | ** |
| 378 | -** List all changes sets currently stashed. | |
| 381 | +** List all changes sets currently stashed. Show information about | |
| 382 | +** individual files in each changeset if --detail or -l is used. | |
| 379 | 383 | ** |
| 380 | 384 | ** fossil stash pop |
| 381 | -** | |
| 382 | -** Apply the most recently create stash to the current working | |
| 383 | -** check-out. Then delete that stash. This is equivalent to | |
| 384 | -** doing an "apply" and a "drop" against the most recent stash. | |
| 385 | -** This command is undoable. | |
| 386 | -** | |
| 387 | 385 | ** fossil stash apply ?STASHID? |
| 388 | 386 | ** |
| 389 | -** Apply the identified stash to the current working check-out. | |
| 390 | -** If no STASHID is specified, use the most recent stash. Unlike | |
| 391 | -** the "pop" command, the stash is retained so that it can be used | |
| 392 | -** again. This command is undoable. | |
| 387 | +** Apply STASHID or the most recently create stash to the current | |
| 388 | +** working check-out. The "pop" command deletes that changeset from | |
| 389 | +** the stash after applying it but the "apply" command retains the | |
| 390 | +** changeset. | |
| 393 | 391 | ** |
| 394 | 392 | ** fossil stash goto ?STASHID? |
| 395 | 393 | ** |
| 396 | 394 | ** Update to the baseline checkout for STASHID then apply the |
| 397 | 395 | ** changes of STASHID. Keep STASHID so that it can be reused |
| @@ -401,15 +399,10 @@ | ||
| 401 | 399 | ** fossil stash rm ?STASHID? ?--all? |
| 402 | 400 | ** |
| 403 | 401 | ** Forget everything about STASHID. Forget the whole stash if the |
| 404 | 402 | ** --all flag is used. Individual drops are undoable but --all is not. |
| 405 | 403 | ** |
| 406 | -** fossil stash snapshot ?-m COMMENT? ?FILES...? | |
| 407 | -** | |
| 408 | -** Save the current changes in the working tree as a new stash | |
| 409 | -** but, unlike "save", do not revert those changes. | |
| 410 | -** | |
| 411 | 404 | ** fossil stash diff ?STASHID? |
| 412 | 405 | ** fossil stash gdiff ?STASHID? |
| 413 | 406 | ** |
| 414 | 407 | ** Show diffs of the current working directory and what that |
| 415 | 408 | ** directory would be if STASHID were applied. |
| @@ -455,40 +448,71 @@ | ||
| 455 | 448 | }else |
| 456 | 449 | if( memcmp(zCmd, "snapshot", nCmd)==0 ){ |
| 457 | 450 | stash_create(); |
| 458 | 451 | }else |
| 459 | 452 | if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){ |
| 460 | - Stmt q; | |
| 453 | + Stmt q, q2; | |
| 461 | 454 | int n = 0; |
| 455 | + int fDetail = find_option("detail","l",0)!=0; | |
| 462 | 456 | verify_all_options(); |
| 463 | 457 | db_prepare(&q, |
| 464 | 458 | "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid)," |
| 465 | 459 | " comment, datetime(ctime) FROM stash" |
| 466 | 460 | " ORDER BY ctime DESC" |
| 467 | 461 | ); |
| 462 | + if( fDetail ){ | |
| 463 | + db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname" | |
| 464 | + " FROM stashfile WHERE stashid=$id"); | |
| 465 | + } | |
| 468 | 466 | while( db_step(&q)==SQLITE_ROW ){ |
| 467 | + int stashid = db_column_int(&q, 0); | |
| 469 | 468 | const char *zCom; |
| 470 | 469 | n++; |
| 471 | 470 | fossil_print("%5d: [%.14s] on %s\n", |
| 472 | - db_column_int(&q, 0), | |
| 471 | + stashid, | |
| 473 | 472 | db_column_text(&q, 1), |
| 474 | 473 | db_column_text(&q, 3) |
| 475 | 474 | ); |
| 476 | 475 | zCom = db_column_text(&q, 2); |
| 477 | 476 | if( zCom && zCom[0] ){ |
| 478 | 477 | fossil_print(" "); |
| 479 | 478 | comment_print(zCom, 7, 79); |
| 480 | 479 | } |
| 480 | + if( fDetail ){ | |
| 481 | + db_bind_int(&q2, "$id", stashid); | |
| 482 | + while( db_step(&q2)==SQLITE_ROW ){ | |
| 483 | + int isAdded = db_column_int(&q2, 0); | |
| 484 | + int isRemoved = db_column_int(&q2, 1); | |
| 485 | + const char *zOrig = db_column_text(&q2, 2); | |
| 486 | + const char *zNew = db_column_text(&q2, 3); | |
| 487 | + if( isAdded ){ | |
| 488 | + fossil_print(" ADD %s\n", zNew); | |
| 489 | + }else if( isRemoved ){ | |
| 490 | + fossil_print(" REMOVE %s\n", zOrig); | |
| 491 | + }else if( fossil_strcmp(zOrig,zNew)!=0 ){ | |
| 492 | + fossil_print(" RENAME %s -> %s\n", zOrig, zNew); | |
| 493 | + }else{ | |
| 494 | + fossil_print(" EDIT %s\n", zOrig); | |
| 495 | + } | |
| 496 | + } | |
| 497 | + db_reset(&q2); | |
| 498 | + } | |
| 481 | 499 | } |
| 482 | 500 | db_finalize(&q); |
| 501 | + if( fDetail ) db_finalize(&q2); | |
| 483 | 502 | if( n==0 ) fossil_print("empty stash\n"); |
| 484 | 503 | }else |
| 485 | 504 | if( memcmp(zCmd, "drop", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 ){ |
| 486 | 505 | int allFlag = find_option("all", 0, 0)!=0; |
| 487 | - if( g.argc>4 ) usage("apply STASHID"); | |
| 506 | + if( g.argc>4 ) usage("drop STASHID"); | |
| 488 | 507 | if( allFlag ){ |
| 489 | - db_multi_exec("DELETE FROM stash; DELETE FROM stashfile;"); | |
| 508 | + Blob ans; | |
| 509 | + blob_zero(&ans); | |
| 510 | + prompt_user("This action is not undoable. Continue (y/N)? ", &ans); | |
| 511 | + if( blob_str(&ans)[0]=='y' ){ | |
| 512 | + db_multi_exec("DELETE FROM stash; DELETE FROM stashfile;"); | |
| 513 | + } | |
| 490 | 514 | }else{ |
| 491 | 515 | stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); |
| 492 | 516 | undo_begin(); |
| 493 | 517 | undo_save_stash(stashid); |
| 494 | 518 | stash_drop(stashid); |
| @@ -537,11 +561,17 @@ | ||
| 537 | 561 | const char *zDiffCmd = db_get("gdiff-command", 0); |
| 538 | 562 | int diffFlags = diff_options(); |
| 539 | 563 | if( g.argc>4 ) usage("diff STASHID"); |
| 540 | 564 | stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); |
| 541 | 565 | stash_diff(stashid, zDiffCmd, diffFlags); |
| 566 | + }else | |
| 567 | + if( memcmp(zCmd, "help", nCmd)==0 ){ | |
| 568 | + g.argv[1] = "help"; | |
| 569 | + g.argv[2] = "stash"; | |
| 570 | + g.argc = 3; | |
| 571 | + help_cmd(); | |
| 542 | 572 | }else |
| 543 | 573 | { |
| 544 | 574 | usage("SUBCOMMAND ARGS..."); |
| 545 | 575 | } |
| 546 | 576 | db_end_transaction(0); |
| 547 | 577 | } |
| 548 | 578 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -170,11 +170,11 @@ | |
| 170 | int i; |
| 171 | for(i=3; i<g.argc; i++){ |
| 172 | stash_add_file_or_dir(stashid, vid, g.argv[i]); |
| 173 | } |
| 174 | }else{ |
| 175 | stash_add_file_or_dir(stashid, vid, "."); |
| 176 | } |
| 177 | return stashid; |
| 178 | } |
| 179 | |
| 180 | /* |
| @@ -258,12 +258,13 @@ | |
| 258 | file_delete(zOPath); |
| 259 | } |
| 260 | } |
| 261 | db_finalize(&q); |
| 262 | if( nConflict ){ |
| 263 | fossil_print("WARNING: %d merge conflicts - see messages above for details.\n", |
| 264 | nConflict); |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | /* |
| 269 | ** Show the diffs associate with a single stash. |
| @@ -363,35 +364,32 @@ | |
| 363 | ** |
| 364 | ** Usage: %fossil stash SUBCOMMAND ARGS... |
| 365 | ** |
| 366 | ** fossil stash |
| 367 | ** fossil stash save ?-m COMMENT? ?FILES...? |
| 368 | ** |
| 369 | ** Save the current changes in the working tree as a new stash. |
| 370 | ** Then revert the changes back to the last check-in. If FILES |
| 371 | ** are listed, then only stash and revert the named files. The |
| 372 | ** "save" verb can be omitted if and only if there are no other |
| 373 | ** arguments. |
| 374 | ** |
| 375 | ** fossil stash list |
| 376 | ** fossil stash ls |
| 377 | ** |
| 378 | ** List all changes sets currently stashed. |
| 379 | ** |
| 380 | ** fossil stash pop |
| 381 | ** |
| 382 | ** Apply the most recently create stash to the current working |
| 383 | ** check-out. Then delete that stash. This is equivalent to |
| 384 | ** doing an "apply" and a "drop" against the most recent stash. |
| 385 | ** This command is undoable. |
| 386 | ** |
| 387 | ** fossil stash apply ?STASHID? |
| 388 | ** |
| 389 | ** Apply the identified stash to the current working check-out. |
| 390 | ** If no STASHID is specified, use the most recent stash. Unlike |
| 391 | ** the "pop" command, the stash is retained so that it can be used |
| 392 | ** again. This command is undoable. |
| 393 | ** |
| 394 | ** fossil stash goto ?STASHID? |
| 395 | ** |
| 396 | ** Update to the baseline checkout for STASHID then apply the |
| 397 | ** changes of STASHID. Keep STASHID so that it can be reused |
| @@ -401,15 +399,10 @@ | |
| 401 | ** fossil stash rm ?STASHID? ?--all? |
| 402 | ** |
| 403 | ** Forget everything about STASHID. Forget the whole stash if the |
| 404 | ** --all flag is used. Individual drops are undoable but --all is not. |
| 405 | ** |
| 406 | ** fossil stash snapshot ?-m COMMENT? ?FILES...? |
| 407 | ** |
| 408 | ** Save the current changes in the working tree as a new stash |
| 409 | ** but, unlike "save", do not revert those changes. |
| 410 | ** |
| 411 | ** fossil stash diff ?STASHID? |
| 412 | ** fossil stash gdiff ?STASHID? |
| 413 | ** |
| 414 | ** Show diffs of the current working directory and what that |
| 415 | ** directory would be if STASHID were applied. |
| @@ -455,40 +448,71 @@ | |
| 455 | }else |
| 456 | if( memcmp(zCmd, "snapshot", nCmd)==0 ){ |
| 457 | stash_create(); |
| 458 | }else |
| 459 | if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){ |
| 460 | Stmt q; |
| 461 | int n = 0; |
| 462 | verify_all_options(); |
| 463 | db_prepare(&q, |
| 464 | "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid)," |
| 465 | " comment, datetime(ctime) FROM stash" |
| 466 | " ORDER BY ctime DESC" |
| 467 | ); |
| 468 | while( db_step(&q)==SQLITE_ROW ){ |
| 469 | const char *zCom; |
| 470 | n++; |
| 471 | fossil_print("%5d: [%.14s] on %s\n", |
| 472 | db_column_int(&q, 0), |
| 473 | db_column_text(&q, 1), |
| 474 | db_column_text(&q, 3) |
| 475 | ); |
| 476 | zCom = db_column_text(&q, 2); |
| 477 | if( zCom && zCom[0] ){ |
| 478 | fossil_print(" "); |
| 479 | comment_print(zCom, 7, 79); |
| 480 | } |
| 481 | } |
| 482 | db_finalize(&q); |
| 483 | if( n==0 ) fossil_print("empty stash\n"); |
| 484 | }else |
| 485 | if( memcmp(zCmd, "drop", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 ){ |
| 486 | int allFlag = find_option("all", 0, 0)!=0; |
| 487 | if( g.argc>4 ) usage("apply STASHID"); |
| 488 | if( allFlag ){ |
| 489 | db_multi_exec("DELETE FROM stash; DELETE FROM stashfile;"); |
| 490 | }else{ |
| 491 | stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); |
| 492 | undo_begin(); |
| 493 | undo_save_stash(stashid); |
| 494 | stash_drop(stashid); |
| @@ -537,11 +561,17 @@ | |
| 537 | const char *zDiffCmd = db_get("gdiff-command", 0); |
| 538 | int diffFlags = diff_options(); |
| 539 | if( g.argc>4 ) usage("diff STASHID"); |
| 540 | stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); |
| 541 | stash_diff(stashid, zDiffCmd, diffFlags); |
| 542 | }else |
| 543 | { |
| 544 | usage("SUBCOMMAND ARGS..."); |
| 545 | } |
| 546 | db_end_transaction(0); |
| 547 | } |
| 548 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -170,11 +170,11 @@ | |
| 170 | int i; |
| 171 | for(i=3; i<g.argc; i++){ |
| 172 | stash_add_file_or_dir(stashid, vid, g.argv[i]); |
| 173 | } |
| 174 | }else{ |
| 175 | stash_add_file_or_dir(stashid, vid, g.zLocalRoot); |
| 176 | } |
| 177 | return stashid; |
| 178 | } |
| 179 | |
| 180 | /* |
| @@ -258,12 +258,13 @@ | |
| 258 | file_delete(zOPath); |
| 259 | } |
| 260 | } |
| 261 | db_finalize(&q); |
| 262 | if( nConflict ){ |
| 263 | fossil_print( |
| 264 | "WARNING: %d merge conflicts - see messages above for details.\n", |
| 265 | nConflict); |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | /* |
| 270 | ** Show the diffs associate with a single stash. |
| @@ -363,35 +364,32 @@ | |
| 364 | ** |
| 365 | ** Usage: %fossil stash SUBCOMMAND ARGS... |
| 366 | ** |
| 367 | ** fossil stash |
| 368 | ** fossil stash save ?-m COMMENT? ?FILES...? |
| 369 | ** fossil stash snapshot ?-m COMMENT? ?FILES...? |
| 370 | ** |
| 371 | ** Save the current changes in the working tree as a new stash. |
| 372 | ** Then revert the changes back to the last check-in. If FILES |
| 373 | ** are listed, then only stash and revert the named files. The |
| 374 | ** "save" verb can be omitted if and only if there are no other |
| 375 | ** arguments. The "snapshot" verb works the same as "save" but |
| 376 | ** omits the revert, keeping the check-out unchanged. |
| 377 | ** |
| 378 | ** fossil stash list ?--detail? |
| 379 | ** fossil stash ls ?-l? |
| 380 | ** |
| 381 | ** List all changes sets currently stashed. Show information about |
| 382 | ** individual files in each changeset if --detail or -l is used. |
| 383 | ** |
| 384 | ** fossil stash pop |
| 385 | ** fossil stash apply ?STASHID? |
| 386 | ** |
| 387 | ** Apply STASHID or the most recently create stash to the current |
| 388 | ** working check-out. The "pop" command deletes that changeset from |
| 389 | ** the stash after applying it but the "apply" command retains the |
| 390 | ** changeset. |
| 391 | ** |
| 392 | ** fossil stash goto ?STASHID? |
| 393 | ** |
| 394 | ** Update to the baseline checkout for STASHID then apply the |
| 395 | ** changes of STASHID. Keep STASHID so that it can be reused |
| @@ -401,15 +399,10 @@ | |
| 399 | ** fossil stash rm ?STASHID? ?--all? |
| 400 | ** |
| 401 | ** Forget everything about STASHID. Forget the whole stash if the |
| 402 | ** --all flag is used. Individual drops are undoable but --all is not. |
| 403 | ** |
| 404 | ** fossil stash diff ?STASHID? |
| 405 | ** fossil stash gdiff ?STASHID? |
| 406 | ** |
| 407 | ** Show diffs of the current working directory and what that |
| 408 | ** directory would be if STASHID were applied. |
| @@ -455,40 +448,71 @@ | |
| 448 | }else |
| 449 | if( memcmp(zCmd, "snapshot", nCmd)==0 ){ |
| 450 | stash_create(); |
| 451 | }else |
| 452 | if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){ |
| 453 | Stmt q, q2; |
| 454 | int n = 0; |
| 455 | int fDetail = find_option("detail","l",0)!=0; |
| 456 | verify_all_options(); |
| 457 | db_prepare(&q, |
| 458 | "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid)," |
| 459 | " comment, datetime(ctime) FROM stash" |
| 460 | " ORDER BY ctime DESC" |
| 461 | ); |
| 462 | if( fDetail ){ |
| 463 | db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname" |
| 464 | " FROM stashfile WHERE stashid=$id"); |
| 465 | } |
| 466 | while( db_step(&q)==SQLITE_ROW ){ |
| 467 | int stashid = db_column_int(&q, 0); |
| 468 | const char *zCom; |
| 469 | n++; |
| 470 | fossil_print("%5d: [%.14s] on %s\n", |
| 471 | stashid, |
| 472 | db_column_text(&q, 1), |
| 473 | db_column_text(&q, 3) |
| 474 | ); |
| 475 | zCom = db_column_text(&q, 2); |
| 476 | if( zCom && zCom[0] ){ |
| 477 | fossil_print(" "); |
| 478 | comment_print(zCom, 7, 79); |
| 479 | } |
| 480 | if( fDetail ){ |
| 481 | db_bind_int(&q2, "$id", stashid); |
| 482 | while( db_step(&q2)==SQLITE_ROW ){ |
| 483 | int isAdded = db_column_int(&q2, 0); |
| 484 | int isRemoved = db_column_int(&q2, 1); |
| 485 | const char *zOrig = db_column_text(&q2, 2); |
| 486 | const char *zNew = db_column_text(&q2, 3); |
| 487 | if( isAdded ){ |
| 488 | fossil_print(" ADD %s\n", zNew); |
| 489 | }else if( isRemoved ){ |
| 490 | fossil_print(" REMOVE %s\n", zOrig); |
| 491 | }else if( fossil_strcmp(zOrig,zNew)!=0 ){ |
| 492 | fossil_print(" RENAME %s -> %s\n", zOrig, zNew); |
| 493 | }else{ |
| 494 | fossil_print(" EDIT %s\n", zOrig); |
| 495 | } |
| 496 | } |
| 497 | db_reset(&q2); |
| 498 | } |
| 499 | } |
| 500 | db_finalize(&q); |
| 501 | if( fDetail ) db_finalize(&q2); |
| 502 | if( n==0 ) fossil_print("empty stash\n"); |
| 503 | }else |
| 504 | if( memcmp(zCmd, "drop", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 ){ |
| 505 | int allFlag = find_option("all", 0, 0)!=0; |
| 506 | if( g.argc>4 ) usage("drop STASHID"); |
| 507 | if( allFlag ){ |
| 508 | Blob ans; |
| 509 | blob_zero(&ans); |
| 510 | prompt_user("This action is not undoable. Continue (y/N)? ", &ans); |
| 511 | if( blob_str(&ans)[0]=='y' ){ |
| 512 | db_multi_exec("DELETE FROM stash; DELETE FROM stashfile;"); |
| 513 | } |
| 514 | }else{ |
| 515 | stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); |
| 516 | undo_begin(); |
| 517 | undo_save_stash(stashid); |
| 518 | stash_drop(stashid); |
| @@ -537,11 +561,17 @@ | |
| 561 | const char *zDiffCmd = db_get("gdiff-command", 0); |
| 562 | int diffFlags = diff_options(); |
| 563 | if( g.argc>4 ) usage("diff STASHID"); |
| 564 | stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); |
| 565 | stash_diff(stashid, zDiffCmd, diffFlags); |
| 566 | }else |
| 567 | if( memcmp(zCmd, "help", nCmd)==0 ){ |
| 568 | g.argv[1] = "help"; |
| 569 | g.argv[2] = "stash"; |
| 570 | g.argc = 3; |
| 571 | help_cmd(); |
| 572 | }else |
| 573 | { |
| 574 | usage("SUBCOMMAND ARGS..."); |
| 575 | } |
| 576 | db_end_transaction(0); |
| 577 | } |
| 578 |
+1
-1
| --- src/tar.c | ||
| +++ src/tar.c | ||
| @@ -523,11 +523,11 @@ | ||
| 523 | 523 | blob_reset(&filename); |
| 524 | 524 | tar_finish(pTar); |
| 525 | 525 | } |
| 526 | 526 | |
| 527 | 527 | /* |
| 528 | -** COMMAND: tarball | |
| 528 | +** COMMAND: tarball* | |
| 529 | 529 | ** |
| 530 | 530 | ** Usage: %fossil tarball VERSION OUTPUTFILE [--name DIRECTORYNAME] |
| 531 | 531 | ** |
| 532 | 532 | ** Generate a compressed tarball for a specified version. If the --name |
| 533 | 533 | ** option is used, its argument becomes the name of the top-level directory |
| 534 | 534 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -523,11 +523,11 @@ | |
| 523 | blob_reset(&filename); |
| 524 | tar_finish(pTar); |
| 525 | } |
| 526 | |
| 527 | /* |
| 528 | ** COMMAND: tarball |
| 529 | ** |
| 530 | ** Usage: %fossil tarball VERSION OUTPUTFILE [--name DIRECTORYNAME] |
| 531 | ** |
| 532 | ** Generate a compressed tarball for a specified version. If the --name |
| 533 | ** option is used, its argument becomes the name of the top-level directory |
| 534 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -523,11 +523,11 @@ | |
| 523 | blob_reset(&filename); |
| 524 | tar_finish(pTar); |
| 525 | } |
| 526 | |
| 527 | /* |
| 528 | ** COMMAND: tarball* |
| 529 | ** |
| 530 | ** Usage: %fossil tarball VERSION OUTPUTFILE [--name DIRECTORYNAME] |
| 531 | ** |
| 532 | ** Generate a compressed tarball for a specified version. If the --name |
| 533 | ** option is used, its argument becomes the name of the top-level directory |
| 534 |
+1
-1
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -837,11 +837,11 @@ | ||
| 837 | 837 | } |
| 838 | 838 | @ </ol> |
| 839 | 839 | } |
| 840 | 840 | |
| 841 | 841 | /* |
| 842 | -** COMMAND: ticket | |
| 842 | +** COMMAND: ticket* | |
| 843 | 843 | ** Usage: %fossil ticket SUBCOMMAND ... |
| 844 | 844 | ** |
| 845 | 845 | ** Run various subcommands to control tickets |
| 846 | 846 | ** |
| 847 | 847 | ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? |
| 848 | 848 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -837,11 +837,11 @@ | |
| 837 | } |
| 838 | @ </ol> |
| 839 | } |
| 840 | |
| 841 | /* |
| 842 | ** COMMAND: ticket |
| 843 | ** Usage: %fossil ticket SUBCOMMAND ... |
| 844 | ** |
| 845 | ** Run various subcommands to control tickets |
| 846 | ** |
| 847 | ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? |
| 848 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -837,11 +837,11 @@ | |
| 837 | } |
| 838 | @ </ol> |
| 839 | } |
| 840 | |
| 841 | /* |
| 842 | ** COMMAND: ticket* |
| 843 | ** Usage: %fossil ticket SUBCOMMAND ... |
| 844 | ** |
| 845 | ** Run various subcommands to control tickets |
| 846 | ** |
| 847 | ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? |
| 848 |
+1
-1
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -837,11 +837,11 @@ | ||
| 837 | 837 | } |
| 838 | 838 | @ </ol> |
| 839 | 839 | } |
| 840 | 840 | |
| 841 | 841 | /* |
| 842 | -** COMMAND: ticket | |
| 842 | +** COMMAND: ticket* | |
| 843 | 843 | ** Usage: %fossil ticket SUBCOMMAND ... |
| 844 | 844 | ** |
| 845 | 845 | ** Run various subcommands to control tickets |
| 846 | 846 | ** |
| 847 | 847 | ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? |
| 848 | 848 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -837,11 +837,11 @@ | |
| 837 | } |
| 838 | @ </ol> |
| 839 | } |
| 840 | |
| 841 | /* |
| 842 | ** COMMAND: ticket |
| 843 | ** Usage: %fossil ticket SUBCOMMAND ... |
| 844 | ** |
| 845 | ** Run various subcommands to control tickets |
| 846 | ** |
| 847 | ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? |
| 848 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -837,11 +837,11 @@ | |
| 837 | } |
| 838 | @ </ol> |
| 839 | } |
| 840 | |
| 841 | /* |
| 842 | ** COMMAND: ticket* |
| 843 | ** Usage: %fossil ticket SUBCOMMAND ... |
| 844 | ** |
| 845 | ** Run various subcommands to control tickets |
| 846 | ** |
| 847 | ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? |
| 848 |
+1
-1
| --- src/undo.c | ||
| +++ src/undo.c | ||
| @@ -348,11 +348,11 @@ | ||
| 348 | 348 | undo_all_filesystem(0); |
| 349 | 349 | } |
| 350 | 350 | |
| 351 | 351 | /* |
| 352 | 352 | ** COMMAND: undo |
| 353 | -** COMMAND: redo | |
| 353 | +** COMMAND: redo* | |
| 354 | 354 | ** |
| 355 | 355 | ** Usage: %fossil undo ?--explain? ?FILENAME...? |
| 356 | 356 | ** or: %fossil redo ?--explain? ?FILENAME...? |
| 357 | 357 | ** |
| 358 | 358 | ** Undo the changes to the working checkout caused by the most recent |
| 359 | 359 |
| --- src/undo.c | |
| +++ src/undo.c | |
| @@ -348,11 +348,11 @@ | |
| 348 | undo_all_filesystem(0); |
| 349 | } |
| 350 | |
| 351 | /* |
| 352 | ** COMMAND: undo |
| 353 | ** COMMAND: redo |
| 354 | ** |
| 355 | ** Usage: %fossil undo ?--explain? ?FILENAME...? |
| 356 | ** or: %fossil redo ?--explain? ?FILENAME...? |
| 357 | ** |
| 358 | ** Undo the changes to the working checkout caused by the most recent |
| 359 |
| --- src/undo.c | |
| +++ src/undo.c | |
| @@ -348,11 +348,11 @@ | |
| 348 | undo_all_filesystem(0); |
| 349 | } |
| 350 | |
| 351 | /* |
| 352 | ** COMMAND: undo |
| 353 | ** COMMAND: redo* |
| 354 | ** |
| 355 | ** Usage: %fossil undo ?--explain? ?FILENAME...? |
| 356 | ** or: %fossil redo ?--explain? ?FILENAME...? |
| 357 | ** |
| 358 | ** Undo the changes to the working checkout caused by the most recent |
| 359 |
+1
-1
| --- src/user.c | ||
| +++ src/user.c | ||
| @@ -142,11 +142,11 @@ | ||
| 142 | 142 | } |
| 143 | 143 | } |
| 144 | 144 | |
| 145 | 145 | |
| 146 | 146 | /* |
| 147 | -** COMMAND: user | |
| 147 | +** COMMAND: user* | |
| 148 | 148 | ** |
| 149 | 149 | ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE? |
| 150 | 150 | ** |
| 151 | 151 | ** Run various subcommands on users of the open repository or of |
| 152 | 152 | ** the repository identified by the -R or --repository option. |
| 153 | 153 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -142,11 +142,11 @@ | |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | |
| 146 | /* |
| 147 | ** COMMAND: user |
| 148 | ** |
| 149 | ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE? |
| 150 | ** |
| 151 | ** Run various subcommands on users of the open repository or of |
| 152 | ** the repository identified by the -R or --repository option. |
| 153 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -142,11 +142,11 @@ | |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | |
| 146 | /* |
| 147 | ** COMMAND: user* |
| 148 | ** |
| 149 | ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE? |
| 150 | ** |
| 151 | ** Run various subcommands on users of the open repository or of |
| 152 | ** the repository identified by the -R or --repository option. |
| 153 |
+1
-1
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -839,11 +839,11 @@ | ||
| 839 | 839 | db_end_transaction(0); |
| 840 | 840 | return 1; |
| 841 | 841 | } |
| 842 | 842 | |
| 843 | 843 | /* |
| 844 | -** COMMAND: wiki | |
| 844 | +** COMMAND: wiki* | |
| 845 | 845 | ** |
| 846 | 846 | ** Usage: %fossil wiki (export|create|commit|list) WikiName |
| 847 | 847 | ** |
| 848 | 848 | ** Run various subcommands to work with wiki entries. |
| 849 | 849 | ** |
| 850 | 850 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -839,11 +839,11 @@ | |
| 839 | db_end_transaction(0); |
| 840 | return 1; |
| 841 | } |
| 842 | |
| 843 | /* |
| 844 | ** COMMAND: wiki |
| 845 | ** |
| 846 | ** Usage: %fossil wiki (export|create|commit|list) WikiName |
| 847 | ** |
| 848 | ** Run various subcommands to work with wiki entries. |
| 849 | ** |
| 850 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -839,11 +839,11 @@ | |
| 839 | db_end_transaction(0); |
| 840 | return 1; |
| 841 | } |
| 842 | |
| 843 | /* |
| 844 | ** COMMAND: wiki* |
| 845 | ** |
| 846 | ** Usage: %fossil wiki (export|create|commit|list) WikiName |
| 847 | ** |
| 848 | ** Run various subcommands to work with wiki entries. |
| 849 | ** |
| 850 |
+1
-1
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -839,11 +839,11 @@ | ||
| 839 | 839 | db_end_transaction(0); |
| 840 | 840 | return 1; |
| 841 | 841 | } |
| 842 | 842 | |
| 843 | 843 | /* |
| 844 | -** COMMAND: wiki | |
| 844 | +** COMMAND: wiki* | |
| 845 | 845 | ** |
| 846 | 846 | ** Usage: %fossil wiki (export|create|commit|list) WikiName |
| 847 | 847 | ** |
| 848 | 848 | ** Run various subcommands to work with wiki entries. |
| 849 | 849 | ** |
| 850 | 850 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -839,11 +839,11 @@ | |
| 839 | db_end_transaction(0); |
| 840 | return 1; |
| 841 | } |
| 842 | |
| 843 | /* |
| 844 | ** COMMAND: wiki |
| 845 | ** |
| 846 | ** Usage: %fossil wiki (export|create|commit|list) WikiName |
| 847 | ** |
| 848 | ** Run various subcommands to work with wiki entries. |
| 849 | ** |
| 850 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -839,11 +839,11 @@ | |
| 839 | db_end_transaction(0); |
| 840 | return 1; |
| 841 | } |
| 842 | |
| 843 | /* |
| 844 | ** COMMAND: wiki* |
| 845 | ** |
| 846 | ** Usage: %fossil wiki (export|create|commit|list) WikiName |
| 847 | ** |
| 848 | ** Run various subcommands to work with wiki entries. |
| 849 | ** |
| 850 |
+1
-1
| --- src/winhttp.c | ||
| +++ src/winhttp.c | ||
| @@ -432,11 +432,11 @@ | ||
| 432 | 432 | } |
| 433 | 433 | return 0; |
| 434 | 434 | } |
| 435 | 435 | |
| 436 | 436 | /* |
| 437 | -** COMMAND: winsrv | |
| 437 | +** COMMAND: winsrv* | |
| 438 | 438 | ** Usage: fossil winsrv METHOD ?SERVICE-NAME? ?OPTIONS? |
| 439 | 439 | ** |
| 440 | 440 | ** Where METHOD is one of: create delete show start stop. |
| 441 | 441 | ** |
| 442 | 442 | ** The winsrv command manages Fossil as a Windows service. This allows |
| 443 | 443 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -432,11 +432,11 @@ | |
| 432 | } |
| 433 | return 0; |
| 434 | } |
| 435 | |
| 436 | /* |
| 437 | ** COMMAND: winsrv |
| 438 | ** Usage: fossil winsrv METHOD ?SERVICE-NAME? ?OPTIONS? |
| 439 | ** |
| 440 | ** Where METHOD is one of: create delete show start stop. |
| 441 | ** |
| 442 | ** The winsrv command manages Fossil as a Windows service. This allows |
| 443 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -432,11 +432,11 @@ | |
| 432 | } |
| 433 | return 0; |
| 434 | } |
| 435 | |
| 436 | /* |
| 437 | ** COMMAND: winsrv* |
| 438 | ** Usage: fossil winsrv METHOD ?SERVICE-NAME? ?OPTIONS? |
| 439 | ** |
| 440 | ** Where METHOD is one of: create delete show start stop. |
| 441 | ** |
| 442 | ** The winsrv command manages Fossil as a Windows service. This allows |
| 443 |
+2
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -637,15 +637,17 @@ | ||
| 637 | 637 | Stmt q; |
| 638 | 638 | int nUncl; |
| 639 | 639 | int nRow = 0; |
| 640 | 640 | int rid; |
| 641 | 641 | |
| 642 | +#if 0 | |
| 642 | 643 | /* We should not ever get any private artifacts in the unclustered table. |
| 643 | 644 | ** But if we do (because of a bug) now is a good time to delete them. */ |
| 644 | 645 | db_multi_exec( |
| 645 | 646 | "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" |
| 646 | 647 | ); |
| 648 | +#endif | |
| 647 | 649 | |
| 648 | 650 | nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/" |
| 649 | 651 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 650 | 652 | " WHERE rid=unclustered.rid)"); |
| 651 | 653 | if( nUncl>=100 ){ |
| 652 | 654 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -637,15 +637,17 @@ | |
| 637 | Stmt q; |
| 638 | int nUncl; |
| 639 | int nRow = 0; |
| 640 | int rid; |
| 641 | |
| 642 | /* We should not ever get any private artifacts in the unclustered table. |
| 643 | ** But if we do (because of a bug) now is a good time to delete them. */ |
| 644 | db_multi_exec( |
| 645 | "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" |
| 646 | ); |
| 647 | |
| 648 | nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/" |
| 649 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 650 | " WHERE rid=unclustered.rid)"); |
| 651 | if( nUncl>=100 ){ |
| 652 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -637,15 +637,17 @@ | |
| 637 | Stmt q; |
| 638 | int nUncl; |
| 639 | int nRow = 0; |
| 640 | int rid; |
| 641 | |
| 642 | #if 0 |
| 643 | /* We should not ever get any private artifacts in the unclustered table. |
| 644 | ** But if we do (because of a bug) now is a good time to delete them. */ |
| 645 | db_multi_exec( |
| 646 | "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" |
| 647 | ); |
| 648 | #endif |
| 649 | |
| 650 | nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/" |
| 651 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 652 | " WHERE rid=unclustered.rid)"); |
| 653 | if( nUncl>=100 ){ |
| 654 |
+1
-1
| --- src/zip.c | ||
| +++ src/zip.c | ||
| @@ -377,11 +377,11 @@ | ||
| 377 | 377 | blob_reset(&filename); |
| 378 | 378 | zip_close(pZip); |
| 379 | 379 | } |
| 380 | 380 | |
| 381 | 381 | /* |
| 382 | -** COMMAND: zip | |
| 382 | +** COMMAND: zip* | |
| 383 | 383 | ** |
| 384 | 384 | ** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME] |
| 385 | 385 | ** |
| 386 | 386 | ** Generate a ZIP archive for a specified version. If the --name option is |
| 387 | 387 | ** used, it argument becomes the name of the top-level directory in the |
| 388 | 388 |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -377,11 +377,11 @@ | |
| 377 | blob_reset(&filename); |
| 378 | zip_close(pZip); |
| 379 | } |
| 380 | |
| 381 | /* |
| 382 | ** COMMAND: zip |
| 383 | ** |
| 384 | ** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME] |
| 385 | ** |
| 386 | ** Generate a ZIP archive for a specified version. If the --name option is |
| 387 | ** used, it argument becomes the name of the top-level directory in the |
| 388 |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -377,11 +377,11 @@ | |
| 377 | blob_reset(&filename); |
| 378 | zip_close(pZip); |
| 379 | } |
| 380 | |
| 381 | /* |
| 382 | ** COMMAND: zip* |
| 383 | ** |
| 384 | ** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME] |
| 385 | ** |
| 386 | ** Generate a ZIP archive for a specified version. If the --name option is |
| 387 | ** used, it argument becomes the name of the top-level directory in the |
| 388 |
+5
-4
| --- www/checkin_names.wiki | ||
| +++ www/checkin_names.wiki | ||
| @@ -126,23 +126,24 @@ | ||
| 126 | 126 | * <i>YYYY-MM-DD HH:MM</i> |
| 127 | 127 | * <i>YYYY-MM-DD HH:MM:SS</i> |
| 128 | 128 | |
| 129 | 129 | The space between the day and the year can optionally be |
| 130 | 130 | replaced by an uppercase <b>T</b> and the entire timestamp can |
| 131 | -optionally be followed by "<b>utc</b>". | |
| 131 | +optionally be followed by "<b>z</b>" or "<b>Z</b>". | |
| 132 | 132 | |
| 133 | 133 | In its default configuration, Fossil interprets and displays all dates |
| 134 | 134 | in Universal Coordinated Time (UTC). This tends to work the best for |
| 135 | 135 | distributed projects where participants are scattered around the globe. |
| 136 | 136 | But there is an option on the Admin/Timeline page of the web-interface to |
| 137 | -switch to local time. The "<b>utc</b>" suffix on an timestamp check-in | |
| 137 | +switch to local time. The "<b>Z</b>" suffix on an timestamp check-in | |
| 138 | 138 | name is meaningless if Fossil is in the default mode of using UTC for |
| 139 | 139 | everything, but if Fossil has been switched to localtime mode, then the |
| 140 | -"<b>utc</b>" suffix means to interpret that particular timestamp using | |
| 140 | +"<b>Z</b>" suffix means to interpret that particular timestamp using | |
| 141 | 141 | UTC instead localtime. |
| 142 | 142 | |
| 143 | -As an example, consider the homepage for the Fossil website itself: | |
| 143 | +For an example of how timestamps are useful, | |
| 144 | +consider the homepage for the Fossil website itself: | |
| 144 | 145 | |
| 145 | 146 | <blockquote> |
| 146 | 147 | http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/index.wiki |
| 147 | 148 | </blockquote> |
| 148 | 149 | |
| 149 | 150 |
| --- www/checkin_names.wiki | |
| +++ www/checkin_names.wiki | |
| @@ -126,23 +126,24 @@ | |
| 126 | * <i>YYYY-MM-DD HH:MM</i> |
| 127 | * <i>YYYY-MM-DD HH:MM:SS</i> |
| 128 | |
| 129 | The space between the day and the year can optionally be |
| 130 | replaced by an uppercase <b>T</b> and the entire timestamp can |
| 131 | optionally be followed by "<b>utc</b>". |
| 132 | |
| 133 | In its default configuration, Fossil interprets and displays all dates |
| 134 | in Universal Coordinated Time (UTC). This tends to work the best for |
| 135 | distributed projects where participants are scattered around the globe. |
| 136 | But there is an option on the Admin/Timeline page of the web-interface to |
| 137 | switch to local time. The "<b>utc</b>" suffix on an timestamp check-in |
| 138 | name is meaningless if Fossil is in the default mode of using UTC for |
| 139 | everything, but if Fossil has been switched to localtime mode, then the |
| 140 | "<b>utc</b>" suffix means to interpret that particular timestamp using |
| 141 | UTC instead localtime. |
| 142 | |
| 143 | As an example, consider the homepage for the Fossil website itself: |
| 144 | |
| 145 | <blockquote> |
| 146 | http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/index.wiki |
| 147 | </blockquote> |
| 148 | |
| 149 |
| --- www/checkin_names.wiki | |
| +++ www/checkin_names.wiki | |
| @@ -126,23 +126,24 @@ | |
| 126 | * <i>YYYY-MM-DD HH:MM</i> |
| 127 | * <i>YYYY-MM-DD HH:MM:SS</i> |
| 128 | |
| 129 | The space between the day and the year can optionally be |
| 130 | replaced by an uppercase <b>T</b> and the entire timestamp can |
| 131 | optionally be followed by "<b>z</b>" or "<b>Z</b>". |
| 132 | |
| 133 | In its default configuration, Fossil interprets and displays all dates |
| 134 | in Universal Coordinated Time (UTC). This tends to work the best for |
| 135 | distributed projects where participants are scattered around the globe. |
| 136 | But there is an option on the Admin/Timeline page of the web-interface to |
| 137 | switch to local time. The "<b>Z</b>" suffix on an timestamp check-in |
| 138 | name is meaningless if Fossil is in the default mode of using UTC for |
| 139 | everything, but if Fossil has been switched to localtime mode, then the |
| 140 | "<b>Z</b>" suffix means to interpret that particular timestamp using |
| 141 | UTC instead localtime. |
| 142 | |
| 143 | For an example of how timestamps are useful, |
| 144 | consider the homepage for the Fossil website itself: |
| 145 | |
| 146 | <blockquote> |
| 147 | http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/index.wiki |
| 148 | </blockquote> |
| 149 | |
| 150 |