Fossil SCM
Don't make it too complicated, too much risk to break other things.
Commit
de5a2ef66171ff07aa819d53c7e5632b0437619f
Parent
e1aed25eee903b3…
15 files changed
+26
-26
+1
-1
+7
-5
+4
-4
+138
+6
-16
+6
-16
+12
-1
+12
-1
+41
-38
+4
-1
+2
+165
-136
+75
-127
+4
-1
+26
-26
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -50,32 +50,19 @@ | ||
| 50 | 50 | /* |
| 51 | 51 | ** Build a string that contains all of the command-line options |
| 52 | 52 | ** specified as arguments. If the option name begins with "+" then |
| 53 | 53 | ** it takes an argument. Without the "+" it does not. |
| 54 | 54 | */ |
| 55 | -static const char *collect_arguments(const char *zArg, ...){ | |
| 56 | - va_list ap; | |
| 57 | - Blob res; | |
| 58 | - blob_zero(&res); | |
| 59 | - va_start(ap, zArg); | |
| 60 | - while( zArg!=0 ){ | |
| 61 | - if( zArg[0]=='+' ){ | |
| 62 | - const char *zValue = find_option(&zArg[1], 0, 1); | |
| 63 | - if( zValue ){ | |
| 64 | - blob_appendf(&res, " --%s %s", &zArg[1], zValue); | |
| 65 | - } | |
| 66 | - }else{ | |
| 67 | - if( find_option(zArg, 0, 0)!=0 ){ | |
| 68 | - blob_appendf(&res, " --%s", zArg); | |
| 69 | - } | |
| 70 | - } | |
| 71 | - zArg = va_arg(ap, const char*); | |
| 72 | - } | |
| 73 | - if( blob_size(&res)==0 ){ | |
| 74 | - return ""; | |
| 75 | - }else{ | |
| 76 | - return blob_str(&res); | |
| 55 | +static void collect_argument(Blob *pExtra, const char *zArg){ | |
| 56 | + if( find_option(zArg, 0, 0)!=0 ){ | |
| 57 | + blob_appendf(pExtra, " --%s", zArg); | |
| 58 | + } | |
| 59 | +} | |
| 60 | +static void collect_argument_value(Blob *pExtra, const char *zArg){ | |
| 61 | + const char *zValue = find_option(zArg, 0, 1); | |
| 62 | + if( zValue ){ | |
| 63 | + blob_appendf(pExtra, " --%s %s", zArg, zValue); | |
| 77 | 64 | } |
| 78 | 65 | } |
| 79 | 66 | |
| 80 | 67 | |
| 81 | 68 | /* |
| @@ -119,11 +106,11 @@ | ||
| 119 | 106 | Stmt q; |
| 120 | 107 | const char *zCmd; |
| 121 | 108 | char *zSyscmd; |
| 122 | 109 | char *zFossil; |
| 123 | 110 | char *zQFilename; |
| 124 | - const char *zExtra = ""; | |
| 111 | + Blob extra; | |
| 125 | 112 | int useCheckouts = 0; |
| 126 | 113 | int quiet = 0; |
| 127 | 114 | int testRun = 0; |
| 128 | 115 | int stopOnError = find_option("dontstop",0,0)==0; |
| 129 | 116 | int rc; |
| @@ -138,10 +125,11 @@ | ||
| 138 | 125 | if( g.argc<3 ){ |
| 139 | 126 | usage("changes|list|ls|pull|push|rebuild|sync"); |
| 140 | 127 | } |
| 141 | 128 | n = strlen(g.argv[2]); |
| 142 | 129 | db_open_config(1); |
| 130 | + blob_zero(&extra); | |
| 143 | 131 | zCmd = g.argv[2]; |
| 144 | 132 | if( strncmp(zCmd, "list", n)==0 || strncmp(zCmd,"ls",n)==0 ){ |
| 145 | 133 | zCmd = "list"; |
| 146 | 134 | useCheckouts = find_option("ckout","c",0)!=0; |
| 147 | 135 | }else if( strncmp(zCmd, "push", n)==0 ){ |
| @@ -148,16 +136,27 @@ | ||
| 148 | 136 | zCmd = "push -autourl -R"; |
| 149 | 137 | }else if( strncmp(zCmd, "pull", n)==0 ){ |
| 150 | 138 | zCmd = "pull -autourl -R"; |
| 151 | 139 | }else if( strncmp(zCmd, "rebuild", n)==0 ){ |
| 152 | 140 | zCmd = "rebuild"; |
| 153 | - zExtra = collect_arguments("cluster","compress","noverify","+pagesize", | |
| 154 | - "vacuum","deanalyze","wal","stats", 0); | |
| 141 | + collect_argument(&extra, "cluster"); | |
| 142 | + collect_argument(&extra, "compress"); | |
| 143 | + collect_argument(&extra, "noverify"); | |
| 144 | + collect_argument_value(&extra, "pagesize"); | |
| 145 | + collect_argument(&extra, "vacuum"); | |
| 146 | + collect_argument(&extra, "deanalyze"); | |
| 147 | + collect_argument(&extra, "wal"); | |
| 148 | + collect_argument(&extra, "stat"); | |
| 155 | 149 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 156 | 150 | zCmd = "sync -autourl -R"; |
| 157 | 151 | }else if( strncmp(zCmd, "test-integrity", n)==0 ){ |
| 158 | 152 | zCmd = "test-integrity"; |
| 153 | + }else if( strncmp(zCmd, "test-orphans", n)==0 ){ | |
| 154 | + zCmd = "test-orphans -R"; | |
| 155 | + }else if( strncmp(zCmd, "test-missing", n)==0 ){ | |
| 156 | + zCmd = "test-missing -q -R"; | |
| 157 | + collect_argument(&extra, "notshunned"); | |
| 159 | 158 | }else if( strncmp(zCmd, "changes", n)==0 ){ |
| 160 | 159 | zCmd = "changes --quiet --header --chdir"; |
| 161 | 160 | useCheckouts = 1; |
| 162 | 161 | stopOnError = 0; |
| 163 | 162 | quiet = 1; |
| @@ -213,11 +212,12 @@ | ||
| 213 | 212 | if( zCmd[0]=='l' ){ |
| 214 | 213 | fossil_print("%s\n", zFilename); |
| 215 | 214 | continue; |
| 216 | 215 | } |
| 217 | 216 | zQFilename = quoteFilename(zFilename); |
| 218 | - zSyscmd = mprintf("%s %s %s%s", zFossil, zCmd, zQFilename, zExtra); | |
| 217 | + zSyscmd = mprintf("%s %s %s%s", | |
| 218 | + zFossil, zCmd, zQFilename, blob_str(&extra)); | |
| 219 | 219 | if( !quiet || testRun ){ |
| 220 | 220 | fossil_print("%s\n", zSyscmd); |
| 221 | 221 | fflush(stdout); |
| 222 | 222 | } |
| 223 | 223 | rc = testRun ? 0 : fossil_system(zSyscmd); |
| 224 | 224 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -50,32 +50,19 @@ | |
| 50 | /* |
| 51 | ** Build a string that contains all of the command-line options |
| 52 | ** specified as arguments. If the option name begins with "+" then |
| 53 | ** it takes an argument. Without the "+" it does not. |
| 54 | */ |
| 55 | static const char *collect_arguments(const char *zArg, ...){ |
| 56 | va_list ap; |
| 57 | Blob res; |
| 58 | blob_zero(&res); |
| 59 | va_start(ap, zArg); |
| 60 | while( zArg!=0 ){ |
| 61 | if( zArg[0]=='+' ){ |
| 62 | const char *zValue = find_option(&zArg[1], 0, 1); |
| 63 | if( zValue ){ |
| 64 | blob_appendf(&res, " --%s %s", &zArg[1], zValue); |
| 65 | } |
| 66 | }else{ |
| 67 | if( find_option(zArg, 0, 0)!=0 ){ |
| 68 | blob_appendf(&res, " --%s", zArg); |
| 69 | } |
| 70 | } |
| 71 | zArg = va_arg(ap, const char*); |
| 72 | } |
| 73 | if( blob_size(&res)==0 ){ |
| 74 | return ""; |
| 75 | }else{ |
| 76 | return blob_str(&res); |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | |
| 81 | /* |
| @@ -119,11 +106,11 @@ | |
| 119 | Stmt q; |
| 120 | const char *zCmd; |
| 121 | char *zSyscmd; |
| 122 | char *zFossil; |
| 123 | char *zQFilename; |
| 124 | const char *zExtra = ""; |
| 125 | int useCheckouts = 0; |
| 126 | int quiet = 0; |
| 127 | int testRun = 0; |
| 128 | int stopOnError = find_option("dontstop",0,0)==0; |
| 129 | int rc; |
| @@ -138,10 +125,11 @@ | |
| 138 | if( g.argc<3 ){ |
| 139 | usage("changes|list|ls|pull|push|rebuild|sync"); |
| 140 | } |
| 141 | n = strlen(g.argv[2]); |
| 142 | db_open_config(1); |
| 143 | zCmd = g.argv[2]; |
| 144 | if( strncmp(zCmd, "list", n)==0 || strncmp(zCmd,"ls",n)==0 ){ |
| 145 | zCmd = "list"; |
| 146 | useCheckouts = find_option("ckout","c",0)!=0; |
| 147 | }else if( strncmp(zCmd, "push", n)==0 ){ |
| @@ -148,16 +136,27 @@ | |
| 148 | zCmd = "push -autourl -R"; |
| 149 | }else if( strncmp(zCmd, "pull", n)==0 ){ |
| 150 | zCmd = "pull -autourl -R"; |
| 151 | }else if( strncmp(zCmd, "rebuild", n)==0 ){ |
| 152 | zCmd = "rebuild"; |
| 153 | zExtra = collect_arguments("cluster","compress","noverify","+pagesize", |
| 154 | "vacuum","deanalyze","wal","stats", 0); |
| 155 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 156 | zCmd = "sync -autourl -R"; |
| 157 | }else if( strncmp(zCmd, "test-integrity", n)==0 ){ |
| 158 | zCmd = "test-integrity"; |
| 159 | }else if( strncmp(zCmd, "changes", n)==0 ){ |
| 160 | zCmd = "changes --quiet --header --chdir"; |
| 161 | useCheckouts = 1; |
| 162 | stopOnError = 0; |
| 163 | quiet = 1; |
| @@ -213,11 +212,12 @@ | |
| 213 | if( zCmd[0]=='l' ){ |
| 214 | fossil_print("%s\n", zFilename); |
| 215 | continue; |
| 216 | } |
| 217 | zQFilename = quoteFilename(zFilename); |
| 218 | zSyscmd = mprintf("%s %s %s%s", zFossil, zCmd, zQFilename, zExtra); |
| 219 | if( !quiet || testRun ){ |
| 220 | fossil_print("%s\n", zSyscmd); |
| 221 | fflush(stdout); |
| 222 | } |
| 223 | rc = testRun ? 0 : fossil_system(zSyscmd); |
| 224 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -50,32 +50,19 @@ | |
| 50 | /* |
| 51 | ** Build a string that contains all of the command-line options |
| 52 | ** specified as arguments. If the option name begins with "+" then |
| 53 | ** it takes an argument. Without the "+" it does not. |
| 54 | */ |
| 55 | static void collect_argument(Blob *pExtra, const char *zArg){ |
| 56 | if( find_option(zArg, 0, 0)!=0 ){ |
| 57 | blob_appendf(pExtra, " --%s", zArg); |
| 58 | } |
| 59 | } |
| 60 | static void collect_argument_value(Blob *pExtra, const char *zArg){ |
| 61 | const char *zValue = find_option(zArg, 0, 1); |
| 62 | if( zValue ){ |
| 63 | blob_appendf(pExtra, " --%s %s", zArg, zValue); |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | |
| 68 | /* |
| @@ -119,11 +106,11 @@ | |
| 106 | Stmt q; |
| 107 | const char *zCmd; |
| 108 | char *zSyscmd; |
| 109 | char *zFossil; |
| 110 | char *zQFilename; |
| 111 | Blob extra; |
| 112 | int useCheckouts = 0; |
| 113 | int quiet = 0; |
| 114 | int testRun = 0; |
| 115 | int stopOnError = find_option("dontstop",0,0)==0; |
| 116 | int rc; |
| @@ -138,10 +125,11 @@ | |
| 125 | if( g.argc<3 ){ |
| 126 | usage("changes|list|ls|pull|push|rebuild|sync"); |
| 127 | } |
| 128 | n = strlen(g.argv[2]); |
| 129 | db_open_config(1); |
| 130 | blob_zero(&extra); |
| 131 | zCmd = g.argv[2]; |
| 132 | if( strncmp(zCmd, "list", n)==0 || strncmp(zCmd,"ls",n)==0 ){ |
| 133 | zCmd = "list"; |
| 134 | useCheckouts = find_option("ckout","c",0)!=0; |
| 135 | }else if( strncmp(zCmd, "push", n)==0 ){ |
| @@ -148,16 +136,27 @@ | |
| 136 | zCmd = "push -autourl -R"; |
| 137 | }else if( strncmp(zCmd, "pull", n)==0 ){ |
| 138 | zCmd = "pull -autourl -R"; |
| 139 | }else if( strncmp(zCmd, "rebuild", n)==0 ){ |
| 140 | zCmd = "rebuild"; |
| 141 | collect_argument(&extra, "cluster"); |
| 142 | collect_argument(&extra, "compress"); |
| 143 | collect_argument(&extra, "noverify"); |
| 144 | collect_argument_value(&extra, "pagesize"); |
| 145 | collect_argument(&extra, "vacuum"); |
| 146 | collect_argument(&extra, "deanalyze"); |
| 147 | collect_argument(&extra, "wal"); |
| 148 | collect_argument(&extra, "stat"); |
| 149 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 150 | zCmd = "sync -autourl -R"; |
| 151 | }else if( strncmp(zCmd, "test-integrity", n)==0 ){ |
| 152 | zCmd = "test-integrity"; |
| 153 | }else if( strncmp(zCmd, "test-orphans", n)==0 ){ |
| 154 | zCmd = "test-orphans -R"; |
| 155 | }else if( strncmp(zCmd, "test-missing", n)==0 ){ |
| 156 | zCmd = "test-missing -q -R"; |
| 157 | collect_argument(&extra, "notshunned"); |
| 158 | }else if( strncmp(zCmd, "changes", n)==0 ){ |
| 159 | zCmd = "changes --quiet --header --chdir"; |
| 160 | useCheckouts = 1; |
| 161 | stopOnError = 0; |
| 162 | quiet = 1; |
| @@ -213,11 +212,12 @@ | |
| 212 | if( zCmd[0]=='l' ){ |
| 213 | fossil_print("%s\n", zFilename); |
| 214 | continue; |
| 215 | } |
| 216 | zQFilename = quoteFilename(zFilename); |
| 217 | zSyscmd = mprintf("%s %s %s%s", |
| 218 | zFossil, zCmd, zQFilename, blob_str(&extra)); |
| 219 | if( !quiet || testRun ){ |
| 220 | fossil_print("%s\n", zSyscmd); |
| 221 | fflush(stdout); |
| 222 | } |
| 223 | rc = testRun ? 0 : fossil_system(zSyscmd); |
| 224 |
+1
-1
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -243,11 +243,11 @@ | ||
| 243 | 243 | int addCompress = 0; |
| 244 | 244 | Manifest *pManifest; |
| 245 | 245 | |
| 246 | 246 | db_begin_transaction(); |
| 247 | 247 | blob_init(&content, aContent, szContent); |
| 248 | - pManifest = manifest_parse(&content, 0); | |
| 248 | + pManifest = manifest_parse(&content, 0, 0); | |
| 249 | 249 | manifest_destroy(pManifest); |
| 250 | 250 | blob_init(&content, aContent, szContent); |
| 251 | 251 | if( pManifest ){ |
| 252 | 252 | blob_compress(&content, &content); |
| 253 | 253 | addCompress = 1; |
| 254 | 254 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -243,11 +243,11 @@ | |
| 243 | int addCompress = 0; |
| 244 | Manifest *pManifest; |
| 245 | |
| 246 | db_begin_transaction(); |
| 247 | blob_init(&content, aContent, szContent); |
| 248 | pManifest = manifest_parse(&content, 0); |
| 249 | manifest_destroy(pManifest); |
| 250 | blob_init(&content, aContent, szContent); |
| 251 | if( pManifest ){ |
| 252 | blob_compress(&content, &content); |
| 253 | addCompress = 1; |
| 254 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -243,11 +243,11 @@ | |
| 243 | int addCompress = 0; |
| 244 | Manifest *pManifest; |
| 245 | |
| 246 | db_begin_transaction(); |
| 247 | blob_init(&content, aContent, szContent); |
| 248 | pManifest = manifest_parse(&content, 0, 0); |
| 249 | manifest_destroy(pManifest); |
| 250 | blob_init(&content, aContent, szContent); |
| 251 | if( pManifest ){ |
| 252 | blob_compress(&content, &content); |
| 253 | addCompress = 1; |
| 254 |
+7
-5
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -659,17 +659,19 @@ | ||
| 659 | 659 | |
| 660 | 660 | /* |
| 661 | 661 | ** Do printf-style string rendering and append the results to a blob. |
| 662 | 662 | */ |
| 663 | 663 | void blob_appendf(Blob *pBlob, const char *zFormat, ...){ |
| 664 | - va_list ap; | |
| 665 | - va_start(ap, zFormat); | |
| 666 | - vxprintf(pBlob, zFormat, ap); | |
| 667 | - va_end(ap); | |
| 664 | + if( pBlob ){ | |
| 665 | + va_list ap; | |
| 666 | + va_start(ap, zFormat); | |
| 667 | + vxprintf(pBlob, zFormat, ap); | |
| 668 | + va_end(ap); | |
| 669 | + } | |
| 668 | 670 | } |
| 669 | 671 | void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){ |
| 670 | - vxprintf(pBlob, zFormat, ap); | |
| 672 | + if( pBlob ) vxprintf(pBlob, zFormat, ap); | |
| 671 | 673 | } |
| 672 | 674 | |
| 673 | 675 | /* |
| 674 | 676 | ** Initalize a blob to the data on an input channel. Return |
| 675 | 677 | ** the number of bytes read into the blob. Any prior content |
| 676 | 678 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -659,17 +659,19 @@ | |
| 659 | |
| 660 | /* |
| 661 | ** Do printf-style string rendering and append the results to a blob. |
| 662 | */ |
| 663 | void blob_appendf(Blob *pBlob, const char *zFormat, ...){ |
| 664 | va_list ap; |
| 665 | va_start(ap, zFormat); |
| 666 | vxprintf(pBlob, zFormat, ap); |
| 667 | va_end(ap); |
| 668 | } |
| 669 | void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){ |
| 670 | vxprintf(pBlob, zFormat, ap); |
| 671 | } |
| 672 | |
| 673 | /* |
| 674 | ** Initalize a blob to the data on an input channel. Return |
| 675 | ** the number of bytes read into the blob. Any prior content |
| 676 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -659,17 +659,19 @@ | |
| 659 | |
| 660 | /* |
| 661 | ** Do printf-style string rendering and append the results to a blob. |
| 662 | */ |
| 663 | void blob_appendf(Blob *pBlob, const char *zFormat, ...){ |
| 664 | if( pBlob ){ |
| 665 | va_list ap; |
| 666 | va_start(ap, zFormat); |
| 667 | vxprintf(pBlob, zFormat, ap); |
| 668 | va_end(ap); |
| 669 | } |
| 670 | } |
| 671 | void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){ |
| 672 | if( pBlob ) vxprintf(pBlob, zFormat, ap); |
| 673 | } |
| 674 | |
| 675 | /* |
| 676 | ** Initalize a blob to the data on an input channel. Return |
| 677 | ** the number of bytes read into the blob. Any prior content |
| 678 |
+4
-4
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -900,20 +900,20 @@ | ||
| 900 | 900 | Blob ans; |
| 901 | 901 | char cReply; |
| 902 | 902 | |
| 903 | 903 | if( eType&1 ){ |
| 904 | 904 | if( crnlOk ){ |
| 905 | - return; /* We don't want CrLf warnings for this file. */ | |
| 905 | + return; /* We don't want CR/NL warnings for this file. */ | |
| 906 | 906 | } |
| 907 | - zWarning = "CR/NL line endings"; | |
| 907 | + zWarning = "CR/NL line endings"; | |
| 908 | 908 | }else{ |
| 909 | - zWarning = "Unicode"; | |
| 909 | + zWarning = "Unicode"; | |
| 910 | 910 | } |
| 911 | 911 | file_relative_name(zFilename, &fname, 0); |
| 912 | 912 | blob_zero(&ans); |
| 913 | 913 | zMsg = mprintf( |
| 914 | - "%s contains %s. commit anyhow (a=all/y/N)? ", | |
| 914 | + "%s contains %s. commit anyhow (a=all/y/N)? ", | |
| 915 | 915 | blob_str(&fname), zWarning ); |
| 916 | 916 | prompt_user(zMsg, &ans); |
| 917 | 917 | fossil_free(zMsg); |
| 918 | 918 | cReply = blob_str(&ans)[0]; |
| 919 | 919 | if( cReply=='a' || cReply=='A' ){ |
| 920 | 920 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -900,20 +900,20 @@ | |
| 900 | Blob ans; |
| 901 | char cReply; |
| 902 | |
| 903 | if( eType&1 ){ |
| 904 | if( crnlOk ){ |
| 905 | return; /* We don't want CrLf warnings for this file. */ |
| 906 | } |
| 907 | zWarning = "CR/NL line endings"; |
| 908 | }else{ |
| 909 | zWarning = "Unicode"; |
| 910 | } |
| 911 | file_relative_name(zFilename, &fname, 0); |
| 912 | blob_zero(&ans); |
| 913 | zMsg = mprintf( |
| 914 | "%s contains %s. commit anyhow (a=all/y/N)? ", |
| 915 | blob_str(&fname), zWarning ); |
| 916 | prompt_user(zMsg, &ans); |
| 917 | fossil_free(zMsg); |
| 918 | cReply = blob_str(&ans)[0]; |
| 919 | if( cReply=='a' || cReply=='A' ){ |
| 920 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -900,20 +900,20 @@ | |
| 900 | Blob ans; |
| 901 | char cReply; |
| 902 | |
| 903 | if( eType&1 ){ |
| 904 | if( crnlOk ){ |
| 905 | return; /* We don't want CR/NL warnings for this file. */ |
| 906 | } |
| 907 | zWarning = "CR/NL line endings"; |
| 908 | }else{ |
| 909 | zWarning = "Unicode"; |
| 910 | } |
| 911 | file_relative_name(zFilename, &fname, 0); |
| 912 | blob_zero(&ans); |
| 913 | zMsg = mprintf( |
| 914 | "%s contains %s. commit anyhow (a=all/y/N)? ", |
| 915 | blob_str(&fname), zWarning ); |
| 916 | prompt_user(zMsg, &ans); |
| 917 | fossil_free(zMsg); |
| 918 | cReply = blob_str(&ans)[0]; |
| 919 | if( cReply=='a' || cReply=='A' ){ |
| 920 |
+138
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -926,5 +926,143 @@ | ||
| 926 | 926 | cnt++; |
| 927 | 927 | } |
| 928 | 928 | db_finalize(&q); |
| 929 | 929 | fossil_print("%d orphans\n", cnt); |
| 930 | 930 | } |
| 931 | + | |
| 932 | +/* Allowed flags for check_exists */ | |
| 933 | +#define MISSING_SHUNNED 0x0001 /* Do not report shunned artifacts */ | |
| 934 | + | |
| 935 | +/* This is a helper routine for test-artifacts. | |
| 936 | +** | |
| 937 | +** Check to see that artifact zUuid exists in the repository. If it does, | |
| 938 | +** return 0. If it does not, generate an error message and return 1. | |
| 939 | +*/ | |
| 940 | +static int check_exists( | |
| 941 | + const char *zUuid, /* The artifact we are checking for */ | |
| 942 | + unsigned flags, /* Flags */ | |
| 943 | + Manifest *p, /* The control artifact that references zUuid */ | |
| 944 | + const char *zRole, /* Role of zUuid in p */ | |
| 945 | + const char *zDetail /* Additional information, such as a filename */ | |
| 946 | +){ | |
| 947 | + static Stmt q; | |
| 948 | + int rc = 0; | |
| 949 | + | |
| 950 | + db_static_prepare(&q, "SELECT size FROM blob WHERE uuid=:uuid"); | |
| 951 | + if( zUuid==0 || zUuid[0]==0 ) return 0; | |
| 952 | + db_bind_text(&q, ":uuid", zUuid); | |
| 953 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 954 | + int size = db_column_int(&q, 0); | |
| 955 | + if( size<0 ) rc = 2; | |
| 956 | + }else{ | |
| 957 | + rc = 1; | |
| 958 | + } | |
| 959 | + db_reset(&q); | |
| 960 | + if( rc ){ | |
| 961 | + const char *zCFType = "control artifact"; | |
| 962 | + char *zSrc; | |
| 963 | + char *zDate; | |
| 964 | + char *zErrType = "MISSING"; | |
| 965 | + if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ | |
| 966 | + if( flags & MISSING_SHUNNED ) return 0; | |
| 967 | + zErrType = "SHUNNED"; | |
| 968 | + } | |
| 969 | + switch( p->type ){ | |
| 970 | + case CFTYPE_MANIFEST: zCFType = "check-in"; break; | |
| 971 | + case CFTYPE_CLUSTER: zCFType = "cluster"; break; | |
| 972 | + case CFTYPE_CONTROL: zCFType = "tag"; break; | |
| 973 | + case CFTYPE_WIKI: zCFType = "wiki"; break; | |
| 974 | + case CFTYPE_TICKET: zCFType = "ticket"; break; | |
| 975 | + case CFTYPE_ATTACHMENT: zCFType = "attachment"; break; | |
| 976 | + case CFTYPE_EVENT: zCFType = "event"; break; | |
| 977 | + } | |
| 978 | + zSrc = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid); | |
| 979 | + if( p->rDate>0.0 ){ | |
| 980 | + zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate); | |
| 981 | + }else{ | |
| 982 | + zDate = db_text(0, | |
| 983 | + "SELECT datetime(rcvfrom.mtime)" | |
| 984 | + " FROM blob, rcvfrom" | |
| 985 | + " WHERE blob.rcvid=rcvfrom.rcvid" | |
| 986 | + " AND blob.rid=%d", p->rid); | |
| 987 | + } | |
| 988 | + fossil_print("%s: %s\n %s %s %S (%d) %s\n", | |
| 989 | + zErrType, zUuid, zRole, zCFType, zSrc, p->rid, zDate); | |
| 990 | + if( zDetail && zDetail[0] ){ | |
| 991 | + fossil_print(" %s\n", zDetail); | |
| 992 | + } | |
| 993 | + fossil_free(zSrc); | |
| 994 | + fossil_free(zDate); | |
| 995 | + rc = 1; | |
| 996 | + } | |
| 997 | + return rc; | |
| 998 | +} | |
| 999 | + | |
| 1000 | +/* | |
| 1001 | +** COMMAND: test-missing | |
| 1002 | +** | |
| 1003 | +** Usage: %fossil test-missing | |
| 1004 | +** | |
| 1005 | +** Look at every artifact in the repository and verify that | |
| 1006 | +** all references are satisfied. Report any referenced artifacts | |
| 1007 | +** that are missing or shunned. | |
| 1008 | +** | |
| 1009 | +** Options: | |
| 1010 | +** | |
| 1011 | +** --notshunned Do not report shunned artifacts | |
| 1012 | +** --quiet Only show output if there are errors | |
| 1013 | +*/ | |
| 1014 | +void test_missing(void){ | |
| 1015 | + Stmt q; | |
| 1016 | + Blob content; | |
| 1017 | + int nErr = 0; | |
| 1018 | + int nArtifact = 0; | |
| 1019 | + int i; | |
| 1020 | + Manifest *p; | |
| 1021 | + unsigned flags = 0; | |
| 1022 | + int quietFlag; | |
| 1023 | + | |
| 1024 | + if( find_option("notshunned", 0, 0)!=0 ) flags |= MISSING_SHUNNED; | |
| 1025 | + quietFlag = find_option("quiet","q",0)!=0; | |
| 1026 | + db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); | |
| 1027 | + db_prepare(&q, | |
| 1028 | + "SELECT mid FROM mlink UNION " | |
| 1029 | + "SELECT srcid FROM tagxref WHERE srcid>0 UNION " | |
| 1030 | + "SELECT rid FROM tagxref UNION " | |
| 1031 | + "SELECT rid FROM attachment JOIN blob ON src=uuid UNION " | |
| 1032 | + "SELECT objid FROM event"); | |
| 1033 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 1034 | + int rid = db_column_int(&q, 0); | |
| 1035 | + content_get(rid, &content); | |
| 1036 | + p = manifest_parse(&content, rid, 0); | |
| 1037 | + if( p ){ | |
| 1038 | + nArtifact++; | |
| 1039 | + nErr += check_exists(p->zBaseline, flags, p, "baseline of", 0); | |
| 1040 | + nErr += check_exists(p->zAttachSrc, flags, p, "file of", 0); | |
| 1041 | + for(i=0; i<p->nFile; i++){ | |
| 1042 | + nErr += check_exists(p->aFile[i].zUuid, flags, p, "file of", | |
| 1043 | + p->aFile[i].zName); | |
| 1044 | + } | |
| 1045 | + for(i=0; i<p->nParent; i++){ | |
| 1046 | + nErr += check_exists(p->azParent[i], flags, p, "parent of", 0); | |
| 1047 | + } | |
| 1048 | + for(i=0; i<p->nCherrypick; i++){ | |
| 1049 | + nErr += check_exists(p->aCherrypick[i].zCPTarget+1, flags, p, | |
| 1050 | + "cherry-pick target of", 0); | |
| 1051 | + nErr += check_exists(p->aCherrypick[i].zCPBase, flags, p, | |
| 1052 | + "cherry-pick baseline of", 0); | |
| 1053 | + } | |
| 1054 | + for(i=0; i<p->nCChild; i++){ | |
| 1055 | + nErr += check_exists(p->azCChild[i], flags, p, "in", 0); | |
| 1056 | + } | |
| 1057 | + for(i=0; i<p->nTag; i++){ | |
| 1058 | + nErr += check_exists(p->aTag[i].zUuid, flags, p, "target of", 0); | |
| 1059 | + } | |
| 1060 | + manifest_destroy(p); | |
| 1061 | + } | |
| 1062 | + } | |
| 1063 | + db_finalize(&q); | |
| 1064 | + if( nErr>0 || quietFlag==0 ){ | |
| 1065 | + fossil_print("%d missing or shunned references in %d control artifacts\n", | |
| 1066 | + nErr, nArtifact); | |
| 1067 | + } | |
| 1068 | +} | |
| 931 | 1069 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -926,5 +926,143 @@ | |
| 926 | cnt++; |
| 927 | } |
| 928 | db_finalize(&q); |
| 929 | fossil_print("%d orphans\n", cnt); |
| 930 | } |
| 931 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -926,5 +926,143 @@ | |
| 926 | cnt++; |
| 927 | } |
| 928 | db_finalize(&q); |
| 929 | fossil_print("%d orphans\n", cnt); |
| 930 | } |
| 931 | |
| 932 | /* Allowed flags for check_exists */ |
| 933 | #define MISSING_SHUNNED 0x0001 /* Do not report shunned artifacts */ |
| 934 | |
| 935 | /* This is a helper routine for test-artifacts. |
| 936 | ** |
| 937 | ** Check to see that artifact zUuid exists in the repository. If it does, |
| 938 | ** return 0. If it does not, generate an error message and return 1. |
| 939 | */ |
| 940 | static int check_exists( |
| 941 | const char *zUuid, /* The artifact we are checking for */ |
| 942 | unsigned flags, /* Flags */ |
| 943 | Manifest *p, /* The control artifact that references zUuid */ |
| 944 | const char *zRole, /* Role of zUuid in p */ |
| 945 | const char *zDetail /* Additional information, such as a filename */ |
| 946 | ){ |
| 947 | static Stmt q; |
| 948 | int rc = 0; |
| 949 | |
| 950 | db_static_prepare(&q, "SELECT size FROM blob WHERE uuid=:uuid"); |
| 951 | if( zUuid==0 || zUuid[0]==0 ) return 0; |
| 952 | db_bind_text(&q, ":uuid", zUuid); |
| 953 | if( db_step(&q)==SQLITE_ROW ){ |
| 954 | int size = db_column_int(&q, 0); |
| 955 | if( size<0 ) rc = 2; |
| 956 | }else{ |
| 957 | rc = 1; |
| 958 | } |
| 959 | db_reset(&q); |
| 960 | if( rc ){ |
| 961 | const char *zCFType = "control artifact"; |
| 962 | char *zSrc; |
| 963 | char *zDate; |
| 964 | char *zErrType = "MISSING"; |
| 965 | if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ |
| 966 | if( flags & MISSING_SHUNNED ) return 0; |
| 967 | zErrType = "SHUNNED"; |
| 968 | } |
| 969 | switch( p->type ){ |
| 970 | case CFTYPE_MANIFEST: zCFType = "check-in"; break; |
| 971 | case CFTYPE_CLUSTER: zCFType = "cluster"; break; |
| 972 | case CFTYPE_CONTROL: zCFType = "tag"; break; |
| 973 | case CFTYPE_WIKI: zCFType = "wiki"; break; |
| 974 | case CFTYPE_TICKET: zCFType = "ticket"; break; |
| 975 | case CFTYPE_ATTACHMENT: zCFType = "attachment"; break; |
| 976 | case CFTYPE_EVENT: zCFType = "event"; break; |
| 977 | } |
| 978 | zSrc = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid); |
| 979 | if( p->rDate>0.0 ){ |
| 980 | zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate); |
| 981 | }else{ |
| 982 | zDate = db_text(0, |
| 983 | "SELECT datetime(rcvfrom.mtime)" |
| 984 | " FROM blob, rcvfrom" |
| 985 | " WHERE blob.rcvid=rcvfrom.rcvid" |
| 986 | " AND blob.rid=%d", p->rid); |
| 987 | } |
| 988 | fossil_print("%s: %s\n %s %s %S (%d) %s\n", |
| 989 | zErrType, zUuid, zRole, zCFType, zSrc, p->rid, zDate); |
| 990 | if( zDetail && zDetail[0] ){ |
| 991 | fossil_print(" %s\n", zDetail); |
| 992 | } |
| 993 | fossil_free(zSrc); |
| 994 | fossil_free(zDate); |
| 995 | rc = 1; |
| 996 | } |
| 997 | return rc; |
| 998 | } |
| 999 | |
| 1000 | /* |
| 1001 | ** COMMAND: test-missing |
| 1002 | ** |
| 1003 | ** Usage: %fossil test-missing |
| 1004 | ** |
| 1005 | ** Look at every artifact in the repository and verify that |
| 1006 | ** all references are satisfied. Report any referenced artifacts |
| 1007 | ** that are missing or shunned. |
| 1008 | ** |
| 1009 | ** Options: |
| 1010 | ** |
| 1011 | ** --notshunned Do not report shunned artifacts |
| 1012 | ** --quiet Only show output if there are errors |
| 1013 | */ |
| 1014 | void test_missing(void){ |
| 1015 | Stmt q; |
| 1016 | Blob content; |
| 1017 | int nErr = 0; |
| 1018 | int nArtifact = 0; |
| 1019 | int i; |
| 1020 | Manifest *p; |
| 1021 | unsigned flags = 0; |
| 1022 | int quietFlag; |
| 1023 | |
| 1024 | if( find_option("notshunned", 0, 0)!=0 ) flags |= MISSING_SHUNNED; |
| 1025 | quietFlag = find_option("quiet","q",0)!=0; |
| 1026 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1027 | db_prepare(&q, |
| 1028 | "SELECT mid FROM mlink UNION " |
| 1029 | "SELECT srcid FROM tagxref WHERE srcid>0 UNION " |
| 1030 | "SELECT rid FROM tagxref UNION " |
| 1031 | "SELECT rid FROM attachment JOIN blob ON src=uuid UNION " |
| 1032 | "SELECT objid FROM event"); |
| 1033 | while( db_step(&q)==SQLITE_ROW ){ |
| 1034 | int rid = db_column_int(&q, 0); |
| 1035 | content_get(rid, &content); |
| 1036 | p = manifest_parse(&content, rid, 0); |
| 1037 | if( p ){ |
| 1038 | nArtifact++; |
| 1039 | nErr += check_exists(p->zBaseline, flags, p, "baseline of", 0); |
| 1040 | nErr += check_exists(p->zAttachSrc, flags, p, "file of", 0); |
| 1041 | for(i=0; i<p->nFile; i++){ |
| 1042 | nErr += check_exists(p->aFile[i].zUuid, flags, p, "file of", |
| 1043 | p->aFile[i].zName); |
| 1044 | } |
| 1045 | for(i=0; i<p->nParent; i++){ |
| 1046 | nErr += check_exists(p->azParent[i], flags, p, "parent of", 0); |
| 1047 | } |
| 1048 | for(i=0; i<p->nCherrypick; i++){ |
| 1049 | nErr += check_exists(p->aCherrypick[i].zCPTarget+1, flags, p, |
| 1050 | "cherry-pick target of", 0); |
| 1051 | nErr += check_exists(p->aCherrypick[i].zCPBase, flags, p, |
| 1052 | "cherry-pick baseline of", 0); |
| 1053 | } |
| 1054 | for(i=0; i<p->nCChild; i++){ |
| 1055 | nErr += check_exists(p->azCChild[i], flags, p, "in", 0); |
| 1056 | } |
| 1057 | for(i=0; i<p->nTag; i++){ |
| 1058 | nErr += check_exists(p->aTag[i].zUuid, flags, p, "target of", 0); |
| 1059 | } |
| 1060 | manifest_destroy(p); |
| 1061 | } |
| 1062 | } |
| 1063 | db_finalize(&q); |
| 1064 | if( nErr>0 || quietFlag==0 ){ |
| 1065 | fossil_print("%d missing or shunned references in %d control artifacts\n", |
| 1066 | nErr, nArtifact); |
| 1067 | } |
| 1068 | } |
| 1069 |
+6
-16
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -195,35 +195,28 @@ | ||
| 195 | 195 | unsigned char *z = (unsigned char *) blob_buffer(pContent); |
| 196 | 196 | unsigned int n = blob_size(pContent); |
| 197 | 197 | int j; |
| 198 | 198 | unsigned char c; |
| 199 | 199 | int result = 1; /* Assume text with no CR/NL */ |
| 200 | - static const char isBinary[256] = { | |
| 201 | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, | |
| 202 | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 | |
| 203 | - }; | |
| 204 | 200 | |
| 205 | - | |
| 206 | - /* Check individual lines. | |
| 201 | + /* | |
| 202 | + ** Check individual lines. | |
| 207 | 203 | */ |
| 208 | 204 | if( n==0 ) return result; /* Empty file -> text */ |
| 209 | 205 | c = *z; |
| 210 | - if( isBinary[c] ) return 0; /* non-text byte in a file -> binary */ | |
| 206 | + if( c==0 ) return 0; /* \000 byte in a file -> binary */ | |
| 211 | 207 | if ( (n&1)==0 ){ /* UTF-16 must have an even blob length */ |
| 212 | 208 | if ( (c==0xff) && (z[1]==0xfe) ){ /* UTF-16 LE BOM */ |
| 213 | 209 | result = -2; |
| 214 | 210 | j = LENGTH_MASK/3; |
| 215 | 211 | while( (n-=2)>0 ){ |
| 216 | 212 | c = *(z+=2); |
| 217 | 213 | if( z[1]==0 ){ /* High-byte must be 0 for further checks */ |
| 218 | - if( isBinary[c] ) return 0; /* non-text char in a file -> binary */ | |
| 214 | + if( c==0 ) return 0; /* \000 char in a file -> binary */ | |
| 219 | 215 | if( c=='\n' ){ |
| 220 | 216 | j = LENGTH_MASK/3; |
| 221 | 217 | } |
| 222 | - }else if( (c+z[1])>0x1fc ){ | |
| 223 | - /* FEFF, FFFE and FFFF are invalid UTF-16 here. */ | |
| 224 | - return 0; | |
| 225 | 218 | } |
| 226 | 219 | if( --j==0 ){ |
| 227 | 220 | return 0; /* Very long line -> binary */ |
| 228 | 221 | } |
| 229 | 222 | } |
| @@ -232,17 +225,14 @@ | ||
| 232 | 225 | result = -2; |
| 233 | 226 | ++z; j = LENGTH_MASK/3; |
| 234 | 227 | while( (n-=2)>0 ){ |
| 235 | 228 | c = *(z+=2); |
| 236 | 229 | if ( z[-1]==0 ){ /* High-byte must be 0 for further checks */ |
| 237 | - if( isBinary[c] ) return 0; /* non-text char in a file -> binary */ | |
| 230 | + if( c==0 ) return 0; /* \000 char in a file -> binary */ | |
| 238 | 231 | if( c=='\n' ){ |
| 239 | 232 | j = LENGTH_MASK/3; |
| 240 | 233 | } |
| 241 | - }else if( (c+z[-1])>0x1fc ){ | |
| 242 | - /* FEFF, FFFE and FFFF are invalid UTF-16 here. */ | |
| 243 | - return 0; | |
| 244 | 234 | } |
| 245 | 235 | if( --j==0 ){ |
| 246 | 236 | return 0; /* Very long line -> binary */ |
| 247 | 237 | } |
| 248 | 238 | } |
| @@ -250,11 +240,11 @@ | ||
| 250 | 240 | } |
| 251 | 241 | } |
| 252 | 242 | j = LENGTH_MASK - (c!='\n'); |
| 253 | 243 | while( --n>0 ){ |
| 254 | 244 | c = *++z; |
| 255 | - if( isBinary[c] ) return 0; /* non-text byte in a file -> binary */ | |
| 245 | + if( c==0 ) return 0; /* \000 byte in a file -> binary */ | |
| 256 | 246 | if( c=='\n' ){ |
| 257 | 247 | if( z[-1]=='\r' ){ |
| 258 | 248 | result = -1; /* Contains CR/NL, continue */ |
| 259 | 249 | } |
| 260 | 250 | j = LENGTH_MASK; |
| 261 | 251 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -195,35 +195,28 @@ | |
| 195 | unsigned char *z = (unsigned char *) blob_buffer(pContent); |
| 196 | unsigned int n = blob_size(pContent); |
| 197 | int j; |
| 198 | unsigned char c; |
| 199 | int result = 1; /* Assume text with no CR/NL */ |
| 200 | static const char isBinary[256] = { |
| 201 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, |
| 202 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 |
| 203 | }; |
| 204 | |
| 205 | |
| 206 | /* Check individual lines. |
| 207 | */ |
| 208 | if( n==0 ) return result; /* Empty file -> text */ |
| 209 | c = *z; |
| 210 | if( isBinary[c] ) return 0; /* non-text byte in a file -> binary */ |
| 211 | if ( (n&1)==0 ){ /* UTF-16 must have an even blob length */ |
| 212 | if ( (c==0xff) && (z[1]==0xfe) ){ /* UTF-16 LE BOM */ |
| 213 | result = -2; |
| 214 | j = LENGTH_MASK/3; |
| 215 | while( (n-=2)>0 ){ |
| 216 | c = *(z+=2); |
| 217 | if( z[1]==0 ){ /* High-byte must be 0 for further checks */ |
| 218 | if( isBinary[c] ) return 0; /* non-text char in a file -> binary */ |
| 219 | if( c=='\n' ){ |
| 220 | j = LENGTH_MASK/3; |
| 221 | } |
| 222 | }else if( (c+z[1])>0x1fc ){ |
| 223 | /* FEFF, FFFE and FFFF are invalid UTF-16 here. */ |
| 224 | return 0; |
| 225 | } |
| 226 | if( --j==0 ){ |
| 227 | return 0; /* Very long line -> binary */ |
| 228 | } |
| 229 | } |
| @@ -232,17 +225,14 @@ | |
| 232 | result = -2; |
| 233 | ++z; j = LENGTH_MASK/3; |
| 234 | while( (n-=2)>0 ){ |
| 235 | c = *(z+=2); |
| 236 | if ( z[-1]==0 ){ /* High-byte must be 0 for further checks */ |
| 237 | if( isBinary[c] ) return 0; /* non-text char in a file -> binary */ |
| 238 | if( c=='\n' ){ |
| 239 | j = LENGTH_MASK/3; |
| 240 | } |
| 241 | }else if( (c+z[-1])>0x1fc ){ |
| 242 | /* FEFF, FFFE and FFFF are invalid UTF-16 here. */ |
| 243 | return 0; |
| 244 | } |
| 245 | if( --j==0 ){ |
| 246 | return 0; /* Very long line -> binary */ |
| 247 | } |
| 248 | } |
| @@ -250,11 +240,11 @@ | |
| 250 | } |
| 251 | } |
| 252 | j = LENGTH_MASK - (c!='\n'); |
| 253 | while( --n>0 ){ |
| 254 | c = *++z; |
| 255 | if( isBinary[c] ) return 0; /* non-text byte in a file -> binary */ |
| 256 | if( c=='\n' ){ |
| 257 | if( z[-1]=='\r' ){ |
| 258 | result = -1; /* Contains CR/NL, continue */ |
| 259 | } |
| 260 | j = LENGTH_MASK; |
| 261 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -195,35 +195,28 @@ | |
| 195 | unsigned char *z = (unsigned char *) blob_buffer(pContent); |
| 196 | unsigned int n = blob_size(pContent); |
| 197 | int j; |
| 198 | unsigned char c; |
| 199 | int result = 1; /* Assume text with no CR/NL */ |
| 200 | |
| 201 | /* |
| 202 | ** Check individual lines. |
| 203 | */ |
| 204 | if( n==0 ) return result; /* Empty file -> text */ |
| 205 | c = *z; |
| 206 | if( c==0 ) return 0; /* \000 byte in a file -> binary */ |
| 207 | if ( (n&1)==0 ){ /* UTF-16 must have an even blob length */ |
| 208 | if ( (c==0xff) && (z[1]==0xfe) ){ /* UTF-16 LE BOM */ |
| 209 | result = -2; |
| 210 | j = LENGTH_MASK/3; |
| 211 | while( (n-=2)>0 ){ |
| 212 | c = *(z+=2); |
| 213 | if( z[1]==0 ){ /* High-byte must be 0 for further checks */ |
| 214 | if( c==0 ) return 0; /* \000 char in a file -> binary */ |
| 215 | if( c=='\n' ){ |
| 216 | j = LENGTH_MASK/3; |
| 217 | } |
| 218 | } |
| 219 | if( --j==0 ){ |
| 220 | return 0; /* Very long line -> binary */ |
| 221 | } |
| 222 | } |
| @@ -232,17 +225,14 @@ | |
| 225 | result = -2; |
| 226 | ++z; j = LENGTH_MASK/3; |
| 227 | while( (n-=2)>0 ){ |
| 228 | c = *(z+=2); |
| 229 | if ( z[-1]==0 ){ /* High-byte must be 0 for further checks */ |
| 230 | if( c==0 ) return 0; /* \000 char in a file -> binary */ |
| 231 | if( c=='\n' ){ |
| 232 | j = LENGTH_MASK/3; |
| 233 | } |
| 234 | } |
| 235 | if( --j==0 ){ |
| 236 | return 0; /* Very long line -> binary */ |
| 237 | } |
| 238 | } |
| @@ -250,11 +240,11 @@ | |
| 240 | } |
| 241 | } |
| 242 | j = LENGTH_MASK - (c!='\n'); |
| 243 | while( --n>0 ){ |
| 244 | c = *++z; |
| 245 | if( c==0 ) return 0; /* \000 byte in a file -> binary */ |
| 246 | if( c=='\n' ){ |
| 247 | if( z[-1]=='\r' ){ |
| 248 | result = -1; /* Contains CR/NL, continue */ |
| 249 | } |
| 250 | j = LENGTH_MASK; |
| 251 |
+6
-16
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -195,35 +195,28 @@ | ||
| 195 | 195 | unsigned char *z = (unsigned char *) blob_buffer(pContent); |
| 196 | 196 | unsigned int n = blob_size(pContent); |
| 197 | 197 | int j; |
| 198 | 198 | unsigned char c; |
| 199 | 199 | int result = 1; /* Assume text with no CR/NL */ |
| 200 | - static const char isBinary[256] = { | |
| 201 | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, | |
| 202 | - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 | |
| 203 | - }; | |
| 204 | 200 | |
| 205 | - | |
| 206 | - /* Check individual lines. | |
| 201 | + /* | |
| 202 | + ** Check individual lines. | |
| 207 | 203 | */ |
| 208 | 204 | if( n==0 ) return result; /* Empty file -> text */ |
| 209 | 205 | c = *z; |
| 210 | - if( isBinary[c] ) return 0; /* non-text byte in a file -> binary */ | |
| 206 | + if( c==0 ) return 0; /* \000 byte in a file -> binary */ | |
| 211 | 207 | if ( (n&1)==0 ){ /* UTF-16 must have an even blob length */ |
| 212 | 208 | if ( (c==0xff) && (z[1]==0xfe) ){ /* UTF-16 LE BOM */ |
| 213 | 209 | result = -2; |
| 214 | 210 | j = LENGTH_MASK/3; |
| 215 | 211 | while( (n-=2)>0 ){ |
| 216 | 212 | c = *(z+=2); |
| 217 | 213 | if( z[1]==0 ){ /* High-byte must be 0 for further checks */ |
| 218 | - if( isBinary[c] ) return 0; /* non-text char in a file -> binary */ | |
| 214 | + if( c==0 ) return 0; /* \000 char in a file -> binary */ | |
| 219 | 215 | if( c=='\n' ){ |
| 220 | 216 | j = LENGTH_MASK/3; |
| 221 | 217 | } |
| 222 | - }else if( (c+z[1])>0x1fc ){ | |
| 223 | - /* FEFF, FFFE and FFFF are invalid UTF-16 here. */ | |
| 224 | - return 0; | |
| 225 | 218 | } |
| 226 | 219 | if( --j==0 ){ |
| 227 | 220 | return 0; /* Very long line -> binary */ |
| 228 | 221 | } |
| 229 | 222 | } |
| @@ -232,17 +225,14 @@ | ||
| 232 | 225 | result = -2; |
| 233 | 226 | ++z; j = LENGTH_MASK/3; |
| 234 | 227 | while( (n-=2)>0 ){ |
| 235 | 228 | c = *(z+=2); |
| 236 | 229 | if ( z[-1]==0 ){ /* High-byte must be 0 for further checks */ |
| 237 | - if( isBinary[c] ) return 0; /* non-text char in a file -> binary */ | |
| 230 | + if( c==0 ) return 0; /* \000 char in a file -> binary */ | |
| 238 | 231 | if( c=='\n' ){ |
| 239 | 232 | j = LENGTH_MASK/3; |
| 240 | 233 | } |
| 241 | - }else if( (c+z[-1])>0x1fc ){ | |
| 242 | - /* FEFF, FFFE and FFFF are invalid UTF-16 here. */ | |
| 243 | - return 0; | |
| 244 | 234 | } |
| 245 | 235 | if( --j==0 ){ |
| 246 | 236 | return 0; /* Very long line -> binary */ |
| 247 | 237 | } |
| 248 | 238 | } |
| @@ -250,11 +240,11 @@ | ||
| 250 | 240 | } |
| 251 | 241 | } |
| 252 | 242 | j = LENGTH_MASK - (c!='\n'); |
| 253 | 243 | while( --n>0 ){ |
| 254 | 244 | c = *++z; |
| 255 | - if( isBinary[c] ) return 0; /* non-text byte in a file -> binary */ | |
| 245 | + if( c==0 ) return 0; /* \000 byte in a file -> binary */ | |
| 256 | 246 | if( c=='\n' ){ |
| 257 | 247 | if( z[-1]=='\r' ){ |
| 258 | 248 | result = -1; /* Contains CR/NL, continue */ |
| 259 | 249 | } |
| 260 | 250 | j = LENGTH_MASK; |
| 261 | 251 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -195,35 +195,28 @@ | |
| 195 | unsigned char *z = (unsigned char *) blob_buffer(pContent); |
| 196 | unsigned int n = blob_size(pContent); |
| 197 | int j; |
| 198 | unsigned char c; |
| 199 | int result = 1; /* Assume text with no CR/NL */ |
| 200 | static const char isBinary[256] = { |
| 201 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, |
| 202 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 |
| 203 | }; |
| 204 | |
| 205 | |
| 206 | /* Check individual lines. |
| 207 | */ |
| 208 | if( n==0 ) return result; /* Empty file -> text */ |
| 209 | c = *z; |
| 210 | if( isBinary[c] ) return 0; /* non-text byte in a file -> binary */ |
| 211 | if ( (n&1)==0 ){ /* UTF-16 must have an even blob length */ |
| 212 | if ( (c==0xff) && (z[1]==0xfe) ){ /* UTF-16 LE BOM */ |
| 213 | result = -2; |
| 214 | j = LENGTH_MASK/3; |
| 215 | while( (n-=2)>0 ){ |
| 216 | c = *(z+=2); |
| 217 | if( z[1]==0 ){ /* High-byte must be 0 for further checks */ |
| 218 | if( isBinary[c] ) return 0; /* non-text char in a file -> binary */ |
| 219 | if( c=='\n' ){ |
| 220 | j = LENGTH_MASK/3; |
| 221 | } |
| 222 | }else if( (c+z[1])>0x1fc ){ |
| 223 | /* FEFF, FFFE and FFFF are invalid UTF-16 here. */ |
| 224 | return 0; |
| 225 | } |
| 226 | if( --j==0 ){ |
| 227 | return 0; /* Very long line -> binary */ |
| 228 | } |
| 229 | } |
| @@ -232,17 +225,14 @@ | |
| 232 | result = -2; |
| 233 | ++z; j = LENGTH_MASK/3; |
| 234 | while( (n-=2)>0 ){ |
| 235 | c = *(z+=2); |
| 236 | if ( z[-1]==0 ){ /* High-byte must be 0 for further checks */ |
| 237 | if( isBinary[c] ) return 0; /* non-text char in a file -> binary */ |
| 238 | if( c=='\n' ){ |
| 239 | j = LENGTH_MASK/3; |
| 240 | } |
| 241 | }else if( (c+z[-1])>0x1fc ){ |
| 242 | /* FEFF, FFFE and FFFF are invalid UTF-16 here. */ |
| 243 | return 0; |
| 244 | } |
| 245 | if( --j==0 ){ |
| 246 | return 0; /* Very long line -> binary */ |
| 247 | } |
| 248 | } |
| @@ -250,11 +240,11 @@ | |
| 250 | } |
| 251 | } |
| 252 | j = LENGTH_MASK - (c!='\n'); |
| 253 | while( --n>0 ){ |
| 254 | c = *++z; |
| 255 | if( isBinary[c] ) return 0; /* non-text byte in a file -> binary */ |
| 256 | if( c=='\n' ){ |
| 257 | if( z[-1]=='\r' ){ |
| 258 | result = -1; /* Contains CR/NL, continue */ |
| 259 | } |
| 260 | j = LENGTH_MASK; |
| 261 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -195,35 +195,28 @@ | |
| 195 | unsigned char *z = (unsigned char *) blob_buffer(pContent); |
| 196 | unsigned int n = blob_size(pContent); |
| 197 | int j; |
| 198 | unsigned char c; |
| 199 | int result = 1; /* Assume text with no CR/NL */ |
| 200 | |
| 201 | /* |
| 202 | ** Check individual lines. |
| 203 | */ |
| 204 | if( n==0 ) return result; /* Empty file -> text */ |
| 205 | c = *z; |
| 206 | if( c==0 ) return 0; /* \000 byte in a file -> binary */ |
| 207 | if ( (n&1)==0 ){ /* UTF-16 must have an even blob length */ |
| 208 | if ( (c==0xff) && (z[1]==0xfe) ){ /* UTF-16 LE BOM */ |
| 209 | result = -2; |
| 210 | j = LENGTH_MASK/3; |
| 211 | while( (n-=2)>0 ){ |
| 212 | c = *(z+=2); |
| 213 | if( z[1]==0 ){ /* High-byte must be 0 for further checks */ |
| 214 | if( c==0 ) return 0; /* \000 char in a file -> binary */ |
| 215 | if( c=='\n' ){ |
| 216 | j = LENGTH_MASK/3; |
| 217 | } |
| 218 | } |
| 219 | if( --j==0 ){ |
| 220 | return 0; /* Very long line -> binary */ |
| 221 | } |
| 222 | } |
| @@ -232,17 +225,14 @@ | |
| 225 | result = -2; |
| 226 | ++z; j = LENGTH_MASK/3; |
| 227 | while( (n-=2)>0 ){ |
| 228 | c = *(z+=2); |
| 229 | if ( z[-1]==0 ){ /* High-byte must be 0 for further checks */ |
| 230 | if( c==0 ) return 0; /* \000 char in a file -> binary */ |
| 231 | if( c=='\n' ){ |
| 232 | j = LENGTH_MASK/3; |
| 233 | } |
| 234 | } |
| 235 | if( --j==0 ){ |
| 236 | return 0; /* Very long line -> binary */ |
| 237 | } |
| 238 | } |
| @@ -250,11 +240,11 @@ | |
| 240 | } |
| 241 | } |
| 242 | j = LENGTH_MASK - (c!='\n'); |
| 243 | while( --n>0 ){ |
| 244 | c = *++z; |
| 245 | if( c==0 ) return 0; /* \000 byte in a file -> binary */ |
| 246 | if( c=='\n' ){ |
| 247 | if( z[-1]=='\r' ){ |
| 248 | result = -1; /* Contains CR/NL, continue */ |
| 249 | } |
| 250 | j = LENGTH_MASK; |
| 251 |
+12
-1
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -34,10 +34,15 @@ | ||
| 34 | 34 | */ |
| 35 | 35 | const char *mimetype_from_content(Blob *pBlob){ |
| 36 | 36 | int i; |
| 37 | 37 | int n; |
| 38 | 38 | const unsigned char *x; |
| 39 | + | |
| 40 | + static const char isBinary[256] = { | |
| 41 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, | |
| 42 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, | |
| 43 | + }; | |
| 39 | 44 | |
| 40 | 45 | /* A table of mimetypes based on file content prefixes |
| 41 | 46 | */ |
| 42 | 47 | static const struct { |
| 43 | 48 | const char *zPrefix; /* The file prefix */ |
| @@ -51,11 +56,17 @@ | ||
| 51 | 56 | { "\377\330\377", 3, "image/jpeg" }, |
| 52 | 57 | }; |
| 53 | 58 | |
| 54 | 59 | x = (const unsigned char*)blob_buffer(pBlob); |
| 55 | 60 | n = blob_size(pBlob); |
| 56 | - if( looks_like_text(pBlob) ){ | |
| 61 | + for(i=0; i<n; i++){ | |
| 62 | + unsigned char c = x[i]; | |
| 63 | + if( isBinary[c] ){ | |
| 64 | + break; | |
| 65 | + } | |
| 66 | + } | |
| 67 | + if( i>=n ){ | |
| 57 | 68 | return 0; /* Plain text */ |
| 58 | 69 | } |
| 59 | 70 | for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){ |
| 60 | 71 | if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){ |
| 61 | 72 | return aMime[i].zMimetype; |
| 62 | 73 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -34,10 +34,15 @@ | |
| 34 | */ |
| 35 | const char *mimetype_from_content(Blob *pBlob){ |
| 36 | int i; |
| 37 | int n; |
| 38 | const unsigned char *x; |
| 39 | |
| 40 | /* A table of mimetypes based on file content prefixes |
| 41 | */ |
| 42 | static const struct { |
| 43 | const char *zPrefix; /* The file prefix */ |
| @@ -51,11 +56,17 @@ | |
| 51 | { "\377\330\377", 3, "image/jpeg" }, |
| 52 | }; |
| 53 | |
| 54 | x = (const unsigned char*)blob_buffer(pBlob); |
| 55 | n = blob_size(pBlob); |
| 56 | if( looks_like_text(pBlob) ){ |
| 57 | return 0; /* Plain text */ |
| 58 | } |
| 59 | for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){ |
| 60 | if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){ |
| 61 | return aMime[i].zMimetype; |
| 62 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -34,10 +34,15 @@ | |
| 34 | */ |
| 35 | const char *mimetype_from_content(Blob *pBlob){ |
| 36 | int i; |
| 37 | int n; |
| 38 | const unsigned char *x; |
| 39 | |
| 40 | static const char isBinary[256] = { |
| 41 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, |
| 42 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, |
| 43 | }; |
| 44 | |
| 45 | /* A table of mimetypes based on file content prefixes |
| 46 | */ |
| 47 | static const struct { |
| 48 | const char *zPrefix; /* The file prefix */ |
| @@ -51,11 +56,17 @@ | |
| 56 | { "\377\330\377", 3, "image/jpeg" }, |
| 57 | }; |
| 58 | |
| 59 | x = (const unsigned char*)blob_buffer(pBlob); |
| 60 | n = blob_size(pBlob); |
| 61 | for(i=0; i<n; i++){ |
| 62 | unsigned char c = x[i]; |
| 63 | if( isBinary[c] ){ |
| 64 | break; |
| 65 | } |
| 66 | } |
| 67 | if( i>=n ){ |
| 68 | return 0; /* Plain text */ |
| 69 | } |
| 70 | for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){ |
| 71 | if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){ |
| 72 | return aMime[i].zMimetype; |
| 73 |
+12
-1
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -34,10 +34,15 @@ | ||
| 34 | 34 | */ |
| 35 | 35 | const char *mimetype_from_content(Blob *pBlob){ |
| 36 | 36 | int i; |
| 37 | 37 | int n; |
| 38 | 38 | const unsigned char *x; |
| 39 | + | |
| 40 | + static const char isBinary[256] = { | |
| 41 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, | |
| 42 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, | |
| 43 | + }; | |
| 39 | 44 | |
| 40 | 45 | /* A table of mimetypes based on file content prefixes |
| 41 | 46 | */ |
| 42 | 47 | static const struct { |
| 43 | 48 | const char *zPrefix; /* The file prefix */ |
| @@ -51,11 +56,17 @@ | ||
| 51 | 56 | { "\377\330\377", 3, "image/jpeg" }, |
| 52 | 57 | }; |
| 53 | 58 | |
| 54 | 59 | x = (const unsigned char*)blob_buffer(pBlob); |
| 55 | 60 | n = blob_size(pBlob); |
| 56 | - if( looks_like_text(pBlob) ){ | |
| 61 | + for(i=0; i<n; i++){ | |
| 62 | + unsigned char c = x[i]; | |
| 63 | + if( isBinary[c] ){ | |
| 64 | + break; | |
| 65 | + } | |
| 66 | + } | |
| 67 | + if( i>=n ){ | |
| 57 | 68 | return 0; /* Plain text */ |
| 58 | 69 | } |
| 59 | 70 | for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){ |
| 60 | 71 | if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){ |
| 61 | 72 | return aMime[i].zMimetype; |
| 62 | 73 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -34,10 +34,15 @@ | |
| 34 | */ |
| 35 | const char *mimetype_from_content(Blob *pBlob){ |
| 36 | int i; |
| 37 | int n; |
| 38 | const unsigned char *x; |
| 39 | |
| 40 | /* A table of mimetypes based on file content prefixes |
| 41 | */ |
| 42 | static const struct { |
| 43 | const char *zPrefix; /* The file prefix */ |
| @@ -51,11 +56,17 @@ | |
| 51 | { "\377\330\377", 3, "image/jpeg" }, |
| 52 | }; |
| 53 | |
| 54 | x = (const unsigned char*)blob_buffer(pBlob); |
| 55 | n = blob_size(pBlob); |
| 56 | if( looks_like_text(pBlob) ){ |
| 57 | return 0; /* Plain text */ |
| 58 | } |
| 59 | for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){ |
| 60 | if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){ |
| 61 | return aMime[i].zMimetype; |
| 62 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -34,10 +34,15 @@ | |
| 34 | */ |
| 35 | const char *mimetype_from_content(Blob *pBlob){ |
| 36 | int i; |
| 37 | int n; |
| 38 | const unsigned char *x; |
| 39 | |
| 40 | static const char isBinary[256] = { |
| 41 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, |
| 42 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, |
| 43 | }; |
| 44 | |
| 45 | /* A table of mimetypes based on file content prefixes |
| 46 | */ |
| 47 | static const struct { |
| 48 | const char *zPrefix; /* The file prefix */ |
| @@ -51,11 +56,17 @@ | |
| 56 | { "\377\330\377", 3, "image/jpeg" }, |
| 57 | }; |
| 58 | |
| 59 | x = (const unsigned char*)blob_buffer(pBlob); |
| 60 | n = blob_size(pBlob); |
| 61 | for(i=0; i<n; i++){ |
| 62 | unsigned char c = x[i]; |
| 63 | if( isBinary[c] ){ |
| 64 | break; |
| 65 | } |
| 66 | } |
| 67 | if( i>=n ){ |
| 68 | return 0; /* Plain text */ |
| 69 | } |
| 70 | for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){ |
| 71 | if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){ |
| 72 | return aMime[i].zMimetype; |
| 73 |
+41
-38
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -22,12 +22,12 @@ | ||
| 22 | 22 | #include "config.h" |
| 23 | 23 | #include "info.h" |
| 24 | 24 | #include <assert.h> |
| 25 | 25 | |
| 26 | 26 | /* |
| 27 | -** Return a string (in memory obtained from malloc) holding a | |
| 28 | -** comma-separated list of tags that apply to check-in with | |
| 27 | +** Return a string (in memory obtained from malloc) holding a | |
| 28 | +** comma-separated list of tags that apply to check-in with | |
| 29 | 29 | ** record-id rid. If the "propagatingOnly" flag is true, then only |
| 30 | 30 | ** show branch tags (tags that propagate to children). |
| 31 | 31 | ** |
| 32 | 32 | ** Return NULL if there are no such tags. |
| 33 | 33 | */ |
| @@ -62,21 +62,21 @@ | ||
| 62 | 62 | char *zTags; |
| 63 | 63 | char *zDate; |
| 64 | 64 | char *zUuid; |
| 65 | 65 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 66 | 66 | if( zUuid ){ |
| 67 | - zDate = db_text(0, | |
| 67 | + zDate = db_text(0, | |
| 68 | 68 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", |
| 69 | 69 | rid |
| 70 | 70 | ); |
| 71 | 71 | /* 01234567890123 */ |
| 72 | 72 | fossil_print("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : ""); |
| 73 | 73 | free(zUuid); |
| 74 | 74 | free(zDate); |
| 75 | 75 | } |
| 76 | 76 | if( zUuid && showComment ){ |
| 77 | - zComment = db_text(0, | |
| 77 | + zComment = db_text(0, | |
| 78 | 78 | "SELECT coalesce(ecomment,comment) || " |
| 79 | 79 | " ' (user: ' || coalesce(euser,user,'?') || ')' " |
| 80 | 80 | " FROM event WHERE objid=%d", |
| 81 | 81 | rid |
| 82 | 82 | ); |
| @@ -86,11 +86,11 @@ | ||
| 86 | 86 | " WHERE cid=%d" |
| 87 | 87 | " ORDER BY isprim DESC, mtime DESC /*sort*/", rid); |
| 88 | 88 | while( db_step(&q)==SQLITE_ROW ){ |
| 89 | 89 | const char *zUuid = db_column_text(&q, 0); |
| 90 | 90 | const char *zType = db_column_int(&q, 2) ? "parent:" : "merged-from:"; |
| 91 | - zDate = db_text("", | |
| 91 | + zDate = db_text("", | |
| 92 | 92 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", |
| 93 | 93 | db_column_int(&q, 1) |
| 94 | 94 | ); |
| 95 | 95 | fossil_print("%-13s %s %s\n", zType, zUuid, zDate); |
| 96 | 96 | free(zDate); |
| @@ -100,11 +100,11 @@ | ||
| 100 | 100 | " WHERE pid=%d" |
| 101 | 101 | " ORDER BY isprim DESC, mtime DESC /*sort*/", rid); |
| 102 | 102 | while( db_step(&q)==SQLITE_ROW ){ |
| 103 | 103 | const char *zUuid = db_column_text(&q, 0); |
| 104 | 104 | const char *zType = db_column_int(&q, 2) ? "child:" : "merged-into:"; |
| 105 | - zDate = db_text("", | |
| 105 | + zDate = db_text("", | |
| 106 | 106 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", |
| 107 | 107 | db_column_int(&q, 1) |
| 108 | 108 | ); |
| 109 | 109 | fossil_print("%-13s %s %s\n", zType, zUuid, zDate); |
| 110 | 110 | free(zDate); |
| @@ -289,11 +289,11 @@ | ||
| 289 | 289 | |
| 290 | 290 | |
| 291 | 291 | /* |
| 292 | 292 | ** Append the difference between two RIDs to the output |
| 293 | 293 | */ |
| 294 | -static void append_diff(const char *zFrom, const char *zTo, u64 diffFlags){ | |
| 294 | +static void append_diff(const char *zFrom, const char *zTo, u64 diffFlags){ | |
| 295 | 295 | int fromid; |
| 296 | 296 | int toid; |
| 297 | 297 | Blob from, to, out; |
| 298 | 298 | if( zFrom ){ |
| 299 | 299 | fromid = uuid_to_rid(zFrom, 0); |
| @@ -319,16 +319,16 @@ | ||
| 319 | 319 | @ %s(blob_str(&out)) |
| 320 | 320 | @ </div> |
| 321 | 321 | } |
| 322 | 322 | blob_reset(&from); |
| 323 | 323 | blob_reset(&to); |
| 324 | - blob_reset(&out); | |
| 324 | + blob_reset(&out); | |
| 325 | 325 | } |
| 326 | 326 | |
| 327 | 327 | |
| 328 | 328 | /* |
| 329 | -** Write a line of web-page output that shows changes that have occurred | |
| 329 | +** Write a line of web-page output that shows changes that have occurred | |
| 330 | 330 | ** to a file between two check-ins. |
| 331 | 331 | */ |
| 332 | 332 | static void append_file_change_line( |
| 333 | 333 | const char *zName, /* Name of the file that has changed */ |
| 334 | 334 | const char *zOld, /* blob.uuid before change. NULL for added files */ |
| @@ -424,11 +424,11 @@ | ||
| 424 | 424 | /* |
| 425 | 425 | ** WEBPAGE: vinfo |
| 426 | 426 | ** WEBPAGE: ci |
| 427 | 427 | ** URL: /ci?name=RID|ARTIFACTID |
| 428 | 428 | ** |
| 429 | -** Display information about a particular check-in. | |
| 429 | +** Display information about a particular check-in. | |
| 430 | 430 | ** |
| 431 | 431 | ** We also jump here from /info if the name is a version. |
| 432 | 432 | ** |
| 433 | 433 | ** If the /ci page is used (instead of /vinfo or /info) then the |
| 434 | 434 | ** default behavior is to show unified diffs of all file changes. |
| @@ -462,11 +462,11 @@ | ||
| 462 | 462 | "SELECT uuid FROM plink, blob" |
| 463 | 463 | " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim", |
| 464 | 464 | rid |
| 465 | 465 | ); |
| 466 | 466 | isLeaf = is_a_leaf(rid); |
| 467 | - db_prepare(&q, | |
| 467 | + db_prepare(&q, | |
| 468 | 468 | "SELECT uuid, datetime(mtime, 'localtime'), user, comment," |
| 469 | 469 | " datetime(omtime, 'localtime'), mtime" |
| 470 | 470 | " FROM blob, event" |
| 471 | 471 | " WHERE blob.rid=%d" |
| 472 | 472 | " AND event.objid=%d", |
| @@ -482,18 +482,18 @@ | ||
| 482 | 482 | const char *zDate; |
| 483 | 483 | const char *zOrigDate; |
| 484 | 484 | char *zThisBranch; |
| 485 | 485 | double thisMtime; |
| 486 | 486 | int seenDiffTitle = 0; |
| 487 | - | |
| 487 | + | |
| 488 | 488 | style_header(zTitle); |
| 489 | 489 | login_anonymous_available(); |
| 490 | 490 | free(zTitle); |
| 491 | 491 | zEUser = db_text(0, |
| 492 | 492 | "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", |
| 493 | 493 | TAG_USER, rid); |
| 494 | - zEComment = db_text(0, | |
| 494 | + zEComment = db_text(0, | |
| 495 | 495 | "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", |
| 496 | 496 | TAG_COMMENT, rid); |
| 497 | 497 | zUser = db_column_text(&q, 2); |
| 498 | 498 | zComment = db_column_text(&q, 3); |
| 499 | 499 | zDate = db_column_text(&q,1); |
| @@ -526,11 +526,11 @@ | ||
| 526 | 526 | @ <tr><th>Original Comment:</th><td>%w(zComment)</td></tr> |
| 527 | 527 | }else{ |
| 528 | 528 | @ <tr><th>Comment:</th><td>%w(zComment)</td></tr> |
| 529 | 529 | } |
| 530 | 530 | if( g.perm.Admin ){ |
| 531 | - db_prepare(&q, | |
| 531 | + db_prepare(&q, | |
| 532 | 532 | "SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)" |
| 533 | 533 | " FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)" |
| 534 | 534 | " WHERE blob.rid=%d", |
| 535 | 535 | rid |
| 536 | 536 | ); |
| @@ -730,11 +730,11 @@ | ||
| 730 | 730 | style_header("Wiki Page Information Error"); |
| 731 | 731 | @ No such object: %h(g.argv[2]) |
| 732 | 732 | style_footer(); |
| 733 | 733 | return; |
| 734 | 734 | } |
| 735 | - db_prepare(&q, | |
| 735 | + db_prepare(&q, | |
| 736 | 736 | "SELECT substr(tagname, 6, 1000), uuid," |
| 737 | 737 | " datetime(event.mtime, 'localtime'), user" |
| 738 | 738 | " FROM tagxref, tag, blob, event" |
| 739 | 739 | " WHERE tagxref.rid=%d" |
| 740 | 740 | " AND tag.tagid=tagxref.tagid" |
| @@ -901,11 +901,11 @@ | ||
| 901 | 901 | ** branch=TAG |
| 902 | 902 | ** detail=BOOLEAN |
| 903 | 903 | ** sbs=BOOLEAN |
| 904 | 904 | ** |
| 905 | 905 | ** |
| 906 | -** Show all differences between two checkins. | |
| 906 | +** Show all differences between two checkins. | |
| 907 | 907 | */ |
| 908 | 908 | void vdiff_page(void){ |
| 909 | 909 | int ridFrom, ridTo; |
| 910 | 910 | int showDetail = 0; |
| 911 | 911 | int sideBySide = 0; |
| @@ -966,24 +966,24 @@ | ||
| 966 | 966 | cmp = -1; |
| 967 | 967 | }else{ |
| 968 | 968 | cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); |
| 969 | 969 | } |
| 970 | 970 | if( cmp<0 ){ |
| 971 | - append_file_change_line(pFileFrom->zName, | |
| 971 | + append_file_change_line(pFileFrom->zName, | |
| 972 | 972 | pFileFrom->zUuid, 0, 0, diffFlags, 0); |
| 973 | 973 | pFileFrom = manifest_file_next(pFrom, 0); |
| 974 | 974 | }else if( cmp>0 ){ |
| 975 | - append_file_change_line(pFileTo->zName, | |
| 975 | + append_file_change_line(pFileTo->zName, | |
| 976 | 976 | 0, pFileTo->zUuid, 0, diffFlags, |
| 977 | 977 | manifest_file_mperm(pFileTo)); |
| 978 | 978 | pFileTo = manifest_file_next(pTo, 0); |
| 979 | 979 | }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ |
| 980 | 980 | /* No changes */ |
| 981 | 981 | pFileFrom = manifest_file_next(pFrom, 0); |
| 982 | 982 | pFileTo = manifest_file_next(pTo, 0); |
| 983 | 983 | }else{ |
| 984 | - append_file_change_line(pFileFrom->zName, | |
| 984 | + append_file_change_line(pFileFrom->zName, | |
| 985 | 985 | pFileFrom->zUuid, |
| 986 | 986 | pFileTo->zUuid, 0, diffFlags, |
| 987 | 987 | manifest_file_mperm(pFileTo)); |
| 988 | 988 | pFileFrom = manifest_file_next(pFrom, 0); |
| 989 | 989 | pFileTo = manifest_file_next(pTo, 0); |
| @@ -1054,11 +1054,11 @@ | ||
| 1054 | 1054 | if( mPerm==PERM_LNK ){ |
| 1055 | 1055 | @ <li>Symbolic link |
| 1056 | 1056 | }else if( mPerm==PERM_EXE ){ |
| 1057 | 1057 | @ <li>Executable file |
| 1058 | 1058 | }else{ |
| 1059 | - @ <li>File | |
| 1059 | + @ <li>File | |
| 1060 | 1060 | } |
| 1061 | 1061 | @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a> |
| 1062 | 1062 | @ <ul> |
| 1063 | 1063 | prevName = fossil_strdup(zName); |
| 1064 | 1064 | } |
| @@ -1081,16 +1081,16 @@ | ||
| 1081 | 1081 | } |
| 1082 | 1082 | } |
| 1083 | 1083 | @ </ul></ul> |
| 1084 | 1084 | free(prevName); |
| 1085 | 1085 | db_finalize(&q); |
| 1086 | - db_prepare(&q, | |
| 1086 | + db_prepare(&q, | |
| 1087 | 1087 | "SELECT substr(tagname, 6, 10000), datetime(event.mtime)," |
| 1088 | 1088 | " coalesce(event.euser, event.user)" |
| 1089 | 1089 | " FROM tagxref, tag, event" |
| 1090 | 1090 | " WHERE tagxref.rid=%d" |
| 1091 | - " AND tag.tagid=tagxref.tagid" | |
| 1091 | + " AND tag.tagid=tagxref.tagid" | |
| 1092 | 1092 | " AND tag.tagname LIKE 'wiki-%%'" |
| 1093 | 1093 | " AND event.objid=tagxref.rid", |
| 1094 | 1094 | rid |
| 1095 | 1095 | ); |
| 1096 | 1096 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -1152,11 +1152,11 @@ | ||
| 1152 | 1152 | } |
| 1153 | 1153 | cnt++; |
| 1154 | 1154 | } |
| 1155 | 1155 | db_finalize(&q); |
| 1156 | 1156 | } |
| 1157 | - db_prepare(&q, | |
| 1157 | + db_prepare(&q, | |
| 1158 | 1158 | "SELECT target, filename, datetime(mtime), user, src" |
| 1159 | 1159 | " FROM attachment" |
| 1160 | 1160 | " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)" |
| 1161 | 1161 | " ORDER BY mtime DESC /*sort*/", |
| 1162 | 1162 | rid |
| @@ -1287,11 +1287,11 @@ | ||
| 1287 | 1287 | } |
| 1288 | 1288 | |
| 1289 | 1289 | /* |
| 1290 | 1290 | ** WEBPAGE: raw |
| 1291 | 1291 | ** URL: /raw?name=ARTIFACTID&m=TYPE |
| 1292 | -** | |
| 1292 | +** | |
| 1293 | 1293 | ** Return the uninterpreted content of an artifact. Used primarily |
| 1294 | 1294 | ** to view artifacts that are images. |
| 1295 | 1295 | */ |
| 1296 | 1296 | void rawartifact_page(void){ |
| 1297 | 1297 | int rid; |
| @@ -1373,11 +1373,11 @@ | ||
| 1373 | 1373 | } |
| 1374 | 1374 | |
| 1375 | 1375 | /* |
| 1376 | 1376 | ** WEBPAGE: hexdump |
| 1377 | 1377 | ** URL: /hexdump?name=ARTIFACTID |
| 1378 | -** | |
| 1378 | +** | |
| 1379 | 1379 | ** Show the complete content of a file identified by ARTIFACTID |
| 1380 | 1380 | ** as preformatted text. |
| 1381 | 1381 | */ |
| 1382 | 1382 | void hexdump_page(void){ |
| 1383 | 1383 | int rid; |
| @@ -1402,11 +1402,11 @@ | ||
| 1402 | 1402 | style_header("Hex Artifact Content"); |
| 1403 | 1403 | zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1404 | 1404 | @ <h2>Artifact %s(zUuid):</h2> |
| 1405 | 1405 | blob_zero(&downloadName); |
| 1406 | 1406 | object_description(rid, 0, &downloadName); |
| 1407 | - style_submenu_element("Download", "Download", | |
| 1407 | + style_submenu_element("Download", "Download", | |
| 1408 | 1408 | "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid); |
| 1409 | 1409 | @ <hr /> |
| 1410 | 1410 | content_get(rid, &content); |
| 1411 | 1411 | @ <blockquote><pre> |
| 1412 | 1412 | hexdump(&content); |
| @@ -1508,11 +1508,11 @@ | ||
| 1508 | 1508 | ** Additional query parameters: |
| 1509 | 1509 | ** |
| 1510 | 1510 | ** ln - show line numbers |
| 1511 | 1511 | ** ln=N - highlight line number N |
| 1512 | 1512 | ** ln=M-N - highlight lines M through N inclusive |
| 1513 | -** | |
| 1513 | +** | |
| 1514 | 1514 | ** Show the complete content of a file identified by ARTIFACTID |
| 1515 | 1515 | ** as preformatted text. |
| 1516 | 1516 | */ |
| 1517 | 1517 | void artifact_page(void){ |
| 1518 | 1518 | int rid = 0; |
| @@ -1545,11 +1545,11 @@ | ||
| 1545 | 1545 | style_header("Artifact Content"); |
| 1546 | 1546 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1547 | 1547 | @ <h2>Artifact %s(zUuid)</h2> |
| 1548 | 1548 | blob_zero(&downloadName); |
| 1549 | 1549 | object_description(rid, 0, &downloadName); |
| 1550 | - style_submenu_element("Download", "Download", | |
| 1550 | + style_submenu_element("Download", "Download", | |
| 1551 | 1551 | "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid); |
| 1552 | 1552 | zMime = mimetype_from_name(blob_str(&downloadName)); |
| 1553 | 1553 | if( zMime ){ |
| 1554 | 1554 | if( fossil_strcmp(zMime, "text/html")==0 ){ |
| 1555 | 1555 | if( P("txt") ){ |
| @@ -1575,19 +1575,22 @@ | ||
| 1575 | 1575 | content_get(rid, &content); |
| 1576 | 1576 | if( renderAsWiki ){ |
| 1577 | 1577 | wiki_convert(&content, 0, 0); |
| 1578 | 1578 | }else if( renderAsHtml ){ |
| 1579 | 1579 | @ <div> |
| 1580 | + blob_strip_bom(&content, 0); | |
| 1580 | 1581 | cgi_append_content(blob_buffer(&content), blob_size(&content)); |
| 1581 | 1582 | @ </div> |
| 1582 | 1583 | }else{ |
| 1583 | 1584 | style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid); |
| 1584 | 1585 | zMime = mimetype_from_content(&content); |
| 1585 | 1586 | @ <blockquote> |
| 1586 | 1587 | if( zMime==0 ){ |
| 1587 | 1588 | const char *zLn = P("ln"); |
| 1588 | - const char *z = blob_str(&content); | |
| 1589 | + const char *z; | |
| 1590 | + blob_strip_bom(&content, 0); | |
| 1591 | + z = blob_str(&content); | |
| 1589 | 1592 | if( zLn ){ |
| 1590 | 1593 | output_text_with_line_numbers(z, zLn); |
| 1591 | 1594 | }else{ |
| 1592 | 1595 | @ <pre> |
| 1593 | 1596 | @ %h(z) |
| @@ -1599,11 +1602,11 @@ | ||
| 1599 | 1602 | @ <i>(file is %d(blob_size(&content)) bytes of binary data)</i> |
| 1600 | 1603 | } |
| 1601 | 1604 | @ </blockquote> |
| 1602 | 1605 | } |
| 1603 | 1606 | style_footer(); |
| 1604 | -} | |
| 1607 | +} | |
| 1605 | 1608 | |
| 1606 | 1609 | /* |
| 1607 | 1610 | ** WEBPAGE: tinfo |
| 1608 | 1611 | ** URL: /tinfo?name=ARTIFACTID |
| 1609 | 1612 | ** |
| @@ -1663,20 +1666,20 @@ | ||
| 1663 | 1666 | /* |
| 1664 | 1667 | ** WEBPAGE: info |
| 1665 | 1668 | ** URL: info/ARTIFACTID |
| 1666 | 1669 | ** |
| 1667 | 1670 | ** The argument is a artifact ID which might be a baseline or a file or |
| 1668 | -** a ticket changes or a wiki edit or something else. | |
| 1671 | +** a ticket changes or a wiki edit or something else. | |
| 1669 | 1672 | ** |
| 1670 | 1673 | ** Figure out what the artifact ID is and jump to it. |
| 1671 | 1674 | */ |
| 1672 | 1675 | void info_page(void){ |
| 1673 | 1676 | const char *zName; |
| 1674 | 1677 | Blob uuid; |
| 1675 | 1678 | int rid; |
| 1676 | 1679 | int rc; |
| 1677 | - | |
| 1680 | + | |
| 1678 | 1681 | zName = P("name"); |
| 1679 | 1682 | if( zName==0 ) fossil_redirect_home(); |
| 1680 | 1683 | if( validate16(zName, strlen(zName)) ){ |
| 1681 | 1684 | if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ |
| 1682 | 1685 | tktview_page(); |
| @@ -1769,28 +1772,28 @@ | ||
| 1769 | 1772 | { "#d3a8bc", 0 }, |
| 1770 | 1773 | { "#d3b5a8", 0 }, |
| 1771 | 1774 | { "#d1d3a8", 0 }, |
| 1772 | 1775 | { "#b1d3a8", 0 }, |
| 1773 | 1776 | |
| 1774 | - { "#8eb2a1", 0 }, | |
| 1777 | + { "#8eb2a1", 0 }, | |
| 1775 | 1778 | { "#8ea7b2", 0 }, |
| 1776 | 1779 | { "#8f8eb2", 0 }, |
| 1777 | 1780 | { "#ab8eb2", 0 }, |
| 1778 | 1781 | { "#b28e9e", 0 }, |
| 1779 | 1782 | { "#b2988e", 0 }, |
| 1780 | 1783 | { "#b0b28e", 0 }, |
| 1781 | 1784 | { "#95b28e", 0 }, |
| 1782 | 1785 | |
| 1783 | - { "#80d6b0", 0 }, | |
| 1786 | + { "#80d6b0", 0 }, | |
| 1784 | 1787 | { "#80bbd6", 0 }, |
| 1785 | 1788 | { "#8680d6", 0 }, |
| 1786 | 1789 | { "#c680d6", 0 }, |
| 1787 | 1790 | { "#d680a6", 0 }, |
| 1788 | 1791 | { "#d69b80", 0 }, |
| 1789 | 1792 | { "#d1d680", 0 }, |
| 1790 | 1793 | { "#91d680", 0 }, |
| 1791 | - | |
| 1794 | + | |
| 1792 | 1795 | |
| 1793 | 1796 | { "custom", "##" }, |
| 1794 | 1797 | }; |
| 1795 | 1798 | int nColor = sizeof(aColor)/sizeof(aColor[0])-1; |
| 1796 | 1799 | int stdClrFound = 0; |
| @@ -1893,11 +1896,11 @@ | ||
| 1893 | 1896 | const char *zNewComment; /* Revised check-in comment */ |
| 1894 | 1897 | const char *zUser; /* Current user for the check-in */ |
| 1895 | 1898 | const char *zNewUser; /* Revised user */ |
| 1896 | 1899 | const char *zDate; /* Current date of the check-in */ |
| 1897 | 1900 | const char *zNewDate; /* Revised check-in date */ |
| 1898 | - const char *zColor; | |
| 1901 | + const char *zColor; | |
| 1899 | 1902 | const char *zNewColor; |
| 1900 | 1903 | const char *zNewTagFlag; |
| 1901 | 1904 | const char *zNewTag; |
| 1902 | 1905 | const char *zNewBrFlag; |
| 1903 | 1906 | const char *zNewBranch; |
| @@ -1906,11 +1909,11 @@ | ||
| 1906 | 1909 | int fNewPropagateColor; /* True if color propagates after edit */ |
| 1907 | 1910 | const char *zChngTime = 0; /* Value of chngtime= query param, if any */ |
| 1908 | 1911 | char *zUuid; |
| 1909 | 1912 | Blob comment; |
| 1910 | 1913 | Stmt q; |
| 1911 | - | |
| 1914 | + | |
| 1912 | 1915 | login_check_credentials(); |
| 1913 | 1916 | if( !g.perm.Write ){ login_needed(); return; } |
| 1914 | 1917 | rid = name_to_typed_rid(P("r"), "ci"); |
| 1915 | 1918 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1916 | 1919 | zComment = db_text(0, "SELECT coalesce(ecomment,comment)" |
| @@ -1953,11 +1956,11 @@ | ||
| 1953 | 1956 | blob_zero(&ctrl); |
| 1954 | 1957 | zNow = date_in_standard_format(zChngTime ? zChngTime : "now"); |
| 1955 | 1958 | blob_appendf(&ctrl, "D %s\n", zNow); |
| 1956 | 1959 | db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)"); |
| 1957 | 1960 | if( zNewColor[0] |
| 1958 | - && (fPropagateColor!=fNewPropagateColor | |
| 1961 | + && (fPropagateColor!=fNewPropagateColor | |
| 1959 | 1962 | || fossil_strcmp(zColor,zNewColor)!=0) |
| 1960 | 1963 | ){ |
| 1961 | 1964 | char *zPrefix = "+"; |
| 1962 | 1965 | if( fNewPropagateColor ){ |
| 1963 | 1966 | zPrefix = "*"; |
| 1964 | 1967 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -22,12 +22,12 @@ | |
| 22 | #include "config.h" |
| 23 | #include "info.h" |
| 24 | #include <assert.h> |
| 25 | |
| 26 | /* |
| 27 | ** Return a string (in memory obtained from malloc) holding a |
| 28 | ** comma-separated list of tags that apply to check-in with |
| 29 | ** record-id rid. If the "propagatingOnly" flag is true, then only |
| 30 | ** show branch tags (tags that propagate to children). |
| 31 | ** |
| 32 | ** Return NULL if there are no such tags. |
| 33 | */ |
| @@ -62,21 +62,21 @@ | |
| 62 | char *zTags; |
| 63 | char *zDate; |
| 64 | char *zUuid; |
| 65 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 66 | if( zUuid ){ |
| 67 | zDate = db_text(0, |
| 68 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", |
| 69 | rid |
| 70 | ); |
| 71 | /* 01234567890123 */ |
| 72 | fossil_print("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : ""); |
| 73 | free(zUuid); |
| 74 | free(zDate); |
| 75 | } |
| 76 | if( zUuid && showComment ){ |
| 77 | zComment = db_text(0, |
| 78 | "SELECT coalesce(ecomment,comment) || " |
| 79 | " ' (user: ' || coalesce(euser,user,'?') || ')' " |
| 80 | " FROM event WHERE objid=%d", |
| 81 | rid |
| 82 | ); |
| @@ -86,11 +86,11 @@ | |
| 86 | " WHERE cid=%d" |
| 87 | " ORDER BY isprim DESC, mtime DESC /*sort*/", rid); |
| 88 | while( db_step(&q)==SQLITE_ROW ){ |
| 89 | const char *zUuid = db_column_text(&q, 0); |
| 90 | const char *zType = db_column_int(&q, 2) ? "parent:" : "merged-from:"; |
| 91 | zDate = db_text("", |
| 92 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", |
| 93 | db_column_int(&q, 1) |
| 94 | ); |
| 95 | fossil_print("%-13s %s %s\n", zType, zUuid, zDate); |
| 96 | free(zDate); |
| @@ -100,11 +100,11 @@ | |
| 100 | " WHERE pid=%d" |
| 101 | " ORDER BY isprim DESC, mtime DESC /*sort*/", rid); |
| 102 | while( db_step(&q)==SQLITE_ROW ){ |
| 103 | const char *zUuid = db_column_text(&q, 0); |
| 104 | const char *zType = db_column_int(&q, 2) ? "child:" : "merged-into:"; |
| 105 | zDate = db_text("", |
| 106 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", |
| 107 | db_column_int(&q, 1) |
| 108 | ); |
| 109 | fossil_print("%-13s %s %s\n", zType, zUuid, zDate); |
| 110 | free(zDate); |
| @@ -289,11 +289,11 @@ | |
| 289 | |
| 290 | |
| 291 | /* |
| 292 | ** Append the difference between two RIDs to the output |
| 293 | */ |
| 294 | static void append_diff(const char *zFrom, const char *zTo, u64 diffFlags){ |
| 295 | int fromid; |
| 296 | int toid; |
| 297 | Blob from, to, out; |
| 298 | if( zFrom ){ |
| 299 | fromid = uuid_to_rid(zFrom, 0); |
| @@ -319,16 +319,16 @@ | |
| 319 | @ %s(blob_str(&out)) |
| 320 | @ </div> |
| 321 | } |
| 322 | blob_reset(&from); |
| 323 | blob_reset(&to); |
| 324 | blob_reset(&out); |
| 325 | } |
| 326 | |
| 327 | |
| 328 | /* |
| 329 | ** Write a line of web-page output that shows changes that have occurred |
| 330 | ** to a file between two check-ins. |
| 331 | */ |
| 332 | static void append_file_change_line( |
| 333 | const char *zName, /* Name of the file that has changed */ |
| 334 | const char *zOld, /* blob.uuid before change. NULL for added files */ |
| @@ -424,11 +424,11 @@ | |
| 424 | /* |
| 425 | ** WEBPAGE: vinfo |
| 426 | ** WEBPAGE: ci |
| 427 | ** URL: /ci?name=RID|ARTIFACTID |
| 428 | ** |
| 429 | ** Display information about a particular check-in. |
| 430 | ** |
| 431 | ** We also jump here from /info if the name is a version. |
| 432 | ** |
| 433 | ** If the /ci page is used (instead of /vinfo or /info) then the |
| 434 | ** default behavior is to show unified diffs of all file changes. |
| @@ -462,11 +462,11 @@ | |
| 462 | "SELECT uuid FROM plink, blob" |
| 463 | " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim", |
| 464 | rid |
| 465 | ); |
| 466 | isLeaf = is_a_leaf(rid); |
| 467 | db_prepare(&q, |
| 468 | "SELECT uuid, datetime(mtime, 'localtime'), user, comment," |
| 469 | " datetime(omtime, 'localtime'), mtime" |
| 470 | " FROM blob, event" |
| 471 | " WHERE blob.rid=%d" |
| 472 | " AND event.objid=%d", |
| @@ -482,18 +482,18 @@ | |
| 482 | const char *zDate; |
| 483 | const char *zOrigDate; |
| 484 | char *zThisBranch; |
| 485 | double thisMtime; |
| 486 | int seenDiffTitle = 0; |
| 487 | |
| 488 | style_header(zTitle); |
| 489 | login_anonymous_available(); |
| 490 | free(zTitle); |
| 491 | zEUser = db_text(0, |
| 492 | "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", |
| 493 | TAG_USER, rid); |
| 494 | zEComment = db_text(0, |
| 495 | "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", |
| 496 | TAG_COMMENT, rid); |
| 497 | zUser = db_column_text(&q, 2); |
| 498 | zComment = db_column_text(&q, 3); |
| 499 | zDate = db_column_text(&q,1); |
| @@ -526,11 +526,11 @@ | |
| 526 | @ <tr><th>Original Comment:</th><td>%w(zComment)</td></tr> |
| 527 | }else{ |
| 528 | @ <tr><th>Comment:</th><td>%w(zComment)</td></tr> |
| 529 | } |
| 530 | if( g.perm.Admin ){ |
| 531 | db_prepare(&q, |
| 532 | "SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)" |
| 533 | " FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)" |
| 534 | " WHERE blob.rid=%d", |
| 535 | rid |
| 536 | ); |
| @@ -730,11 +730,11 @@ | |
| 730 | style_header("Wiki Page Information Error"); |
| 731 | @ No such object: %h(g.argv[2]) |
| 732 | style_footer(); |
| 733 | return; |
| 734 | } |
| 735 | db_prepare(&q, |
| 736 | "SELECT substr(tagname, 6, 1000), uuid," |
| 737 | " datetime(event.mtime, 'localtime'), user" |
| 738 | " FROM tagxref, tag, blob, event" |
| 739 | " WHERE tagxref.rid=%d" |
| 740 | " AND tag.tagid=tagxref.tagid" |
| @@ -901,11 +901,11 @@ | |
| 901 | ** branch=TAG |
| 902 | ** detail=BOOLEAN |
| 903 | ** sbs=BOOLEAN |
| 904 | ** |
| 905 | ** |
| 906 | ** Show all differences between two checkins. |
| 907 | */ |
| 908 | void vdiff_page(void){ |
| 909 | int ridFrom, ridTo; |
| 910 | int showDetail = 0; |
| 911 | int sideBySide = 0; |
| @@ -966,24 +966,24 @@ | |
| 966 | cmp = -1; |
| 967 | }else{ |
| 968 | cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); |
| 969 | } |
| 970 | if( cmp<0 ){ |
| 971 | append_file_change_line(pFileFrom->zName, |
| 972 | pFileFrom->zUuid, 0, 0, diffFlags, 0); |
| 973 | pFileFrom = manifest_file_next(pFrom, 0); |
| 974 | }else if( cmp>0 ){ |
| 975 | append_file_change_line(pFileTo->zName, |
| 976 | 0, pFileTo->zUuid, 0, diffFlags, |
| 977 | manifest_file_mperm(pFileTo)); |
| 978 | pFileTo = manifest_file_next(pTo, 0); |
| 979 | }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ |
| 980 | /* No changes */ |
| 981 | pFileFrom = manifest_file_next(pFrom, 0); |
| 982 | pFileTo = manifest_file_next(pTo, 0); |
| 983 | }else{ |
| 984 | append_file_change_line(pFileFrom->zName, |
| 985 | pFileFrom->zUuid, |
| 986 | pFileTo->zUuid, 0, diffFlags, |
| 987 | manifest_file_mperm(pFileTo)); |
| 988 | pFileFrom = manifest_file_next(pFrom, 0); |
| 989 | pFileTo = manifest_file_next(pTo, 0); |
| @@ -1054,11 +1054,11 @@ | |
| 1054 | if( mPerm==PERM_LNK ){ |
| 1055 | @ <li>Symbolic link |
| 1056 | }else if( mPerm==PERM_EXE ){ |
| 1057 | @ <li>Executable file |
| 1058 | }else{ |
| 1059 | @ <li>File |
| 1060 | } |
| 1061 | @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a> |
| 1062 | @ <ul> |
| 1063 | prevName = fossil_strdup(zName); |
| 1064 | } |
| @@ -1081,16 +1081,16 @@ | |
| 1081 | } |
| 1082 | } |
| 1083 | @ </ul></ul> |
| 1084 | free(prevName); |
| 1085 | db_finalize(&q); |
| 1086 | db_prepare(&q, |
| 1087 | "SELECT substr(tagname, 6, 10000), datetime(event.mtime)," |
| 1088 | " coalesce(event.euser, event.user)" |
| 1089 | " FROM tagxref, tag, event" |
| 1090 | " WHERE tagxref.rid=%d" |
| 1091 | " AND tag.tagid=tagxref.tagid" |
| 1092 | " AND tag.tagname LIKE 'wiki-%%'" |
| 1093 | " AND event.objid=tagxref.rid", |
| 1094 | rid |
| 1095 | ); |
| 1096 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -1152,11 +1152,11 @@ | |
| 1152 | } |
| 1153 | cnt++; |
| 1154 | } |
| 1155 | db_finalize(&q); |
| 1156 | } |
| 1157 | db_prepare(&q, |
| 1158 | "SELECT target, filename, datetime(mtime), user, src" |
| 1159 | " FROM attachment" |
| 1160 | " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)" |
| 1161 | " ORDER BY mtime DESC /*sort*/", |
| 1162 | rid |
| @@ -1287,11 +1287,11 @@ | |
| 1287 | } |
| 1288 | |
| 1289 | /* |
| 1290 | ** WEBPAGE: raw |
| 1291 | ** URL: /raw?name=ARTIFACTID&m=TYPE |
| 1292 | ** |
| 1293 | ** Return the uninterpreted content of an artifact. Used primarily |
| 1294 | ** to view artifacts that are images. |
| 1295 | */ |
| 1296 | void rawartifact_page(void){ |
| 1297 | int rid; |
| @@ -1373,11 +1373,11 @@ | |
| 1373 | } |
| 1374 | |
| 1375 | /* |
| 1376 | ** WEBPAGE: hexdump |
| 1377 | ** URL: /hexdump?name=ARTIFACTID |
| 1378 | ** |
| 1379 | ** Show the complete content of a file identified by ARTIFACTID |
| 1380 | ** as preformatted text. |
| 1381 | */ |
| 1382 | void hexdump_page(void){ |
| 1383 | int rid; |
| @@ -1402,11 +1402,11 @@ | |
| 1402 | style_header("Hex Artifact Content"); |
| 1403 | zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1404 | @ <h2>Artifact %s(zUuid):</h2> |
| 1405 | blob_zero(&downloadName); |
| 1406 | object_description(rid, 0, &downloadName); |
| 1407 | style_submenu_element("Download", "Download", |
| 1408 | "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid); |
| 1409 | @ <hr /> |
| 1410 | content_get(rid, &content); |
| 1411 | @ <blockquote><pre> |
| 1412 | hexdump(&content); |
| @@ -1508,11 +1508,11 @@ | |
| 1508 | ** Additional query parameters: |
| 1509 | ** |
| 1510 | ** ln - show line numbers |
| 1511 | ** ln=N - highlight line number N |
| 1512 | ** ln=M-N - highlight lines M through N inclusive |
| 1513 | ** |
| 1514 | ** Show the complete content of a file identified by ARTIFACTID |
| 1515 | ** as preformatted text. |
| 1516 | */ |
| 1517 | void artifact_page(void){ |
| 1518 | int rid = 0; |
| @@ -1545,11 +1545,11 @@ | |
| 1545 | style_header("Artifact Content"); |
| 1546 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1547 | @ <h2>Artifact %s(zUuid)</h2> |
| 1548 | blob_zero(&downloadName); |
| 1549 | object_description(rid, 0, &downloadName); |
| 1550 | style_submenu_element("Download", "Download", |
| 1551 | "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid); |
| 1552 | zMime = mimetype_from_name(blob_str(&downloadName)); |
| 1553 | if( zMime ){ |
| 1554 | if( fossil_strcmp(zMime, "text/html")==0 ){ |
| 1555 | if( P("txt") ){ |
| @@ -1575,19 +1575,22 @@ | |
| 1575 | content_get(rid, &content); |
| 1576 | if( renderAsWiki ){ |
| 1577 | wiki_convert(&content, 0, 0); |
| 1578 | }else if( renderAsHtml ){ |
| 1579 | @ <div> |
| 1580 | cgi_append_content(blob_buffer(&content), blob_size(&content)); |
| 1581 | @ </div> |
| 1582 | }else{ |
| 1583 | style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid); |
| 1584 | zMime = mimetype_from_content(&content); |
| 1585 | @ <blockquote> |
| 1586 | if( zMime==0 ){ |
| 1587 | const char *zLn = P("ln"); |
| 1588 | const char *z = blob_str(&content); |
| 1589 | if( zLn ){ |
| 1590 | output_text_with_line_numbers(z, zLn); |
| 1591 | }else{ |
| 1592 | @ <pre> |
| 1593 | @ %h(z) |
| @@ -1599,11 +1602,11 @@ | |
| 1599 | @ <i>(file is %d(blob_size(&content)) bytes of binary data)</i> |
| 1600 | } |
| 1601 | @ </blockquote> |
| 1602 | } |
| 1603 | style_footer(); |
| 1604 | } |
| 1605 | |
| 1606 | /* |
| 1607 | ** WEBPAGE: tinfo |
| 1608 | ** URL: /tinfo?name=ARTIFACTID |
| 1609 | ** |
| @@ -1663,20 +1666,20 @@ | |
| 1663 | /* |
| 1664 | ** WEBPAGE: info |
| 1665 | ** URL: info/ARTIFACTID |
| 1666 | ** |
| 1667 | ** The argument is a artifact ID which might be a baseline or a file or |
| 1668 | ** a ticket changes or a wiki edit or something else. |
| 1669 | ** |
| 1670 | ** Figure out what the artifact ID is and jump to it. |
| 1671 | */ |
| 1672 | void info_page(void){ |
| 1673 | const char *zName; |
| 1674 | Blob uuid; |
| 1675 | int rid; |
| 1676 | int rc; |
| 1677 | |
| 1678 | zName = P("name"); |
| 1679 | if( zName==0 ) fossil_redirect_home(); |
| 1680 | if( validate16(zName, strlen(zName)) ){ |
| 1681 | if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ |
| 1682 | tktview_page(); |
| @@ -1769,28 +1772,28 @@ | |
| 1769 | { "#d3a8bc", 0 }, |
| 1770 | { "#d3b5a8", 0 }, |
| 1771 | { "#d1d3a8", 0 }, |
| 1772 | { "#b1d3a8", 0 }, |
| 1773 | |
| 1774 | { "#8eb2a1", 0 }, |
| 1775 | { "#8ea7b2", 0 }, |
| 1776 | { "#8f8eb2", 0 }, |
| 1777 | { "#ab8eb2", 0 }, |
| 1778 | { "#b28e9e", 0 }, |
| 1779 | { "#b2988e", 0 }, |
| 1780 | { "#b0b28e", 0 }, |
| 1781 | { "#95b28e", 0 }, |
| 1782 | |
| 1783 | { "#80d6b0", 0 }, |
| 1784 | { "#80bbd6", 0 }, |
| 1785 | { "#8680d6", 0 }, |
| 1786 | { "#c680d6", 0 }, |
| 1787 | { "#d680a6", 0 }, |
| 1788 | { "#d69b80", 0 }, |
| 1789 | { "#d1d680", 0 }, |
| 1790 | { "#91d680", 0 }, |
| 1791 | |
| 1792 | |
| 1793 | { "custom", "##" }, |
| 1794 | }; |
| 1795 | int nColor = sizeof(aColor)/sizeof(aColor[0])-1; |
| 1796 | int stdClrFound = 0; |
| @@ -1893,11 +1896,11 @@ | |
| 1893 | const char *zNewComment; /* Revised check-in comment */ |
| 1894 | const char *zUser; /* Current user for the check-in */ |
| 1895 | const char *zNewUser; /* Revised user */ |
| 1896 | const char *zDate; /* Current date of the check-in */ |
| 1897 | const char *zNewDate; /* Revised check-in date */ |
| 1898 | const char *zColor; |
| 1899 | const char *zNewColor; |
| 1900 | const char *zNewTagFlag; |
| 1901 | const char *zNewTag; |
| 1902 | const char *zNewBrFlag; |
| 1903 | const char *zNewBranch; |
| @@ -1906,11 +1909,11 @@ | |
| 1906 | int fNewPropagateColor; /* True if color propagates after edit */ |
| 1907 | const char *zChngTime = 0; /* Value of chngtime= query param, if any */ |
| 1908 | char *zUuid; |
| 1909 | Blob comment; |
| 1910 | Stmt q; |
| 1911 | |
| 1912 | login_check_credentials(); |
| 1913 | if( !g.perm.Write ){ login_needed(); return; } |
| 1914 | rid = name_to_typed_rid(P("r"), "ci"); |
| 1915 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1916 | zComment = db_text(0, "SELECT coalesce(ecomment,comment)" |
| @@ -1953,11 +1956,11 @@ | |
| 1953 | blob_zero(&ctrl); |
| 1954 | zNow = date_in_standard_format(zChngTime ? zChngTime : "now"); |
| 1955 | blob_appendf(&ctrl, "D %s\n", zNow); |
| 1956 | db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)"); |
| 1957 | if( zNewColor[0] |
| 1958 | && (fPropagateColor!=fNewPropagateColor |
| 1959 | || fossil_strcmp(zColor,zNewColor)!=0) |
| 1960 | ){ |
| 1961 | char *zPrefix = "+"; |
| 1962 | if( fNewPropagateColor ){ |
| 1963 | zPrefix = "*"; |
| 1964 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -22,12 +22,12 @@ | |
| 22 | #include "config.h" |
| 23 | #include "info.h" |
| 24 | #include <assert.h> |
| 25 | |
| 26 | /* |
| 27 | ** Return a string (in memory obtained from malloc) holding a |
| 28 | ** comma-separated list of tags that apply to check-in with |
| 29 | ** record-id rid. If the "propagatingOnly" flag is true, then only |
| 30 | ** show branch tags (tags that propagate to children). |
| 31 | ** |
| 32 | ** Return NULL if there are no such tags. |
| 33 | */ |
| @@ -62,21 +62,21 @@ | |
| 62 | char *zTags; |
| 63 | char *zDate; |
| 64 | char *zUuid; |
| 65 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 66 | if( zUuid ){ |
| 67 | zDate = db_text(0, |
| 68 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", |
| 69 | rid |
| 70 | ); |
| 71 | /* 01234567890123 */ |
| 72 | fossil_print("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : ""); |
| 73 | free(zUuid); |
| 74 | free(zDate); |
| 75 | } |
| 76 | if( zUuid && showComment ){ |
| 77 | zComment = db_text(0, |
| 78 | "SELECT coalesce(ecomment,comment) || " |
| 79 | " ' (user: ' || coalesce(euser,user,'?') || ')' " |
| 80 | " FROM event WHERE objid=%d", |
| 81 | rid |
| 82 | ); |
| @@ -86,11 +86,11 @@ | |
| 86 | " WHERE cid=%d" |
| 87 | " ORDER BY isprim DESC, mtime DESC /*sort*/", rid); |
| 88 | while( db_step(&q)==SQLITE_ROW ){ |
| 89 | const char *zUuid = db_column_text(&q, 0); |
| 90 | const char *zType = db_column_int(&q, 2) ? "parent:" : "merged-from:"; |
| 91 | zDate = db_text("", |
| 92 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", |
| 93 | db_column_int(&q, 1) |
| 94 | ); |
| 95 | fossil_print("%-13s %s %s\n", zType, zUuid, zDate); |
| 96 | free(zDate); |
| @@ -100,11 +100,11 @@ | |
| 100 | " WHERE pid=%d" |
| 101 | " ORDER BY isprim DESC, mtime DESC /*sort*/", rid); |
| 102 | while( db_step(&q)==SQLITE_ROW ){ |
| 103 | const char *zUuid = db_column_text(&q, 0); |
| 104 | const char *zType = db_column_int(&q, 2) ? "child:" : "merged-into:"; |
| 105 | zDate = db_text("", |
| 106 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", |
| 107 | db_column_int(&q, 1) |
| 108 | ); |
| 109 | fossil_print("%-13s %s %s\n", zType, zUuid, zDate); |
| 110 | free(zDate); |
| @@ -289,11 +289,11 @@ | |
| 289 | |
| 290 | |
| 291 | /* |
| 292 | ** Append the difference between two RIDs to the output |
| 293 | */ |
| 294 | static void append_diff(const char *zFrom, const char *zTo, u64 diffFlags){ |
| 295 | int fromid; |
| 296 | int toid; |
| 297 | Blob from, to, out; |
| 298 | if( zFrom ){ |
| 299 | fromid = uuid_to_rid(zFrom, 0); |
| @@ -319,16 +319,16 @@ | |
| 319 | @ %s(blob_str(&out)) |
| 320 | @ </div> |
| 321 | } |
| 322 | blob_reset(&from); |
| 323 | blob_reset(&to); |
| 324 | blob_reset(&out); |
| 325 | } |
| 326 | |
| 327 | |
| 328 | /* |
| 329 | ** Write a line of web-page output that shows changes that have occurred |
| 330 | ** to a file between two check-ins. |
| 331 | */ |
| 332 | static void append_file_change_line( |
| 333 | const char *zName, /* Name of the file that has changed */ |
| 334 | const char *zOld, /* blob.uuid before change. NULL for added files */ |
| @@ -424,11 +424,11 @@ | |
| 424 | /* |
| 425 | ** WEBPAGE: vinfo |
| 426 | ** WEBPAGE: ci |
| 427 | ** URL: /ci?name=RID|ARTIFACTID |
| 428 | ** |
| 429 | ** Display information about a particular check-in. |
| 430 | ** |
| 431 | ** We also jump here from /info if the name is a version. |
| 432 | ** |
| 433 | ** If the /ci page is used (instead of /vinfo or /info) then the |
| 434 | ** default behavior is to show unified diffs of all file changes. |
| @@ -462,11 +462,11 @@ | |
| 462 | "SELECT uuid FROM plink, blob" |
| 463 | " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim", |
| 464 | rid |
| 465 | ); |
| 466 | isLeaf = is_a_leaf(rid); |
| 467 | db_prepare(&q, |
| 468 | "SELECT uuid, datetime(mtime, 'localtime'), user, comment," |
| 469 | " datetime(omtime, 'localtime'), mtime" |
| 470 | " FROM blob, event" |
| 471 | " WHERE blob.rid=%d" |
| 472 | " AND event.objid=%d", |
| @@ -482,18 +482,18 @@ | |
| 482 | const char *zDate; |
| 483 | const char *zOrigDate; |
| 484 | char *zThisBranch; |
| 485 | double thisMtime; |
| 486 | int seenDiffTitle = 0; |
| 487 | |
| 488 | style_header(zTitle); |
| 489 | login_anonymous_available(); |
| 490 | free(zTitle); |
| 491 | zEUser = db_text(0, |
| 492 | "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", |
| 493 | TAG_USER, rid); |
| 494 | zEComment = db_text(0, |
| 495 | "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", |
| 496 | TAG_COMMENT, rid); |
| 497 | zUser = db_column_text(&q, 2); |
| 498 | zComment = db_column_text(&q, 3); |
| 499 | zDate = db_column_text(&q,1); |
| @@ -526,11 +526,11 @@ | |
| 526 | @ <tr><th>Original Comment:</th><td>%w(zComment)</td></tr> |
| 527 | }else{ |
| 528 | @ <tr><th>Comment:</th><td>%w(zComment)</td></tr> |
| 529 | } |
| 530 | if( g.perm.Admin ){ |
| 531 | db_prepare(&q, |
| 532 | "SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)" |
| 533 | " FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)" |
| 534 | " WHERE blob.rid=%d", |
| 535 | rid |
| 536 | ); |
| @@ -730,11 +730,11 @@ | |
| 730 | style_header("Wiki Page Information Error"); |
| 731 | @ No such object: %h(g.argv[2]) |
| 732 | style_footer(); |
| 733 | return; |
| 734 | } |
| 735 | db_prepare(&q, |
| 736 | "SELECT substr(tagname, 6, 1000), uuid," |
| 737 | " datetime(event.mtime, 'localtime'), user" |
| 738 | " FROM tagxref, tag, blob, event" |
| 739 | " WHERE tagxref.rid=%d" |
| 740 | " AND tag.tagid=tagxref.tagid" |
| @@ -901,11 +901,11 @@ | |
| 901 | ** branch=TAG |
| 902 | ** detail=BOOLEAN |
| 903 | ** sbs=BOOLEAN |
| 904 | ** |
| 905 | ** |
| 906 | ** Show all differences between two checkins. |
| 907 | */ |
| 908 | void vdiff_page(void){ |
| 909 | int ridFrom, ridTo; |
| 910 | int showDetail = 0; |
| 911 | int sideBySide = 0; |
| @@ -966,24 +966,24 @@ | |
| 966 | cmp = -1; |
| 967 | }else{ |
| 968 | cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); |
| 969 | } |
| 970 | if( cmp<0 ){ |
| 971 | append_file_change_line(pFileFrom->zName, |
| 972 | pFileFrom->zUuid, 0, 0, diffFlags, 0); |
| 973 | pFileFrom = manifest_file_next(pFrom, 0); |
| 974 | }else if( cmp>0 ){ |
| 975 | append_file_change_line(pFileTo->zName, |
| 976 | 0, pFileTo->zUuid, 0, diffFlags, |
| 977 | manifest_file_mperm(pFileTo)); |
| 978 | pFileTo = manifest_file_next(pTo, 0); |
| 979 | }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ |
| 980 | /* No changes */ |
| 981 | pFileFrom = manifest_file_next(pFrom, 0); |
| 982 | pFileTo = manifest_file_next(pTo, 0); |
| 983 | }else{ |
| 984 | append_file_change_line(pFileFrom->zName, |
| 985 | pFileFrom->zUuid, |
| 986 | pFileTo->zUuid, 0, diffFlags, |
| 987 | manifest_file_mperm(pFileTo)); |
| 988 | pFileFrom = manifest_file_next(pFrom, 0); |
| 989 | pFileTo = manifest_file_next(pTo, 0); |
| @@ -1054,11 +1054,11 @@ | |
| 1054 | if( mPerm==PERM_LNK ){ |
| 1055 | @ <li>Symbolic link |
| 1056 | }else if( mPerm==PERM_EXE ){ |
| 1057 | @ <li>Executable file |
| 1058 | }else{ |
| 1059 | @ <li>File |
| 1060 | } |
| 1061 | @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a> |
| 1062 | @ <ul> |
| 1063 | prevName = fossil_strdup(zName); |
| 1064 | } |
| @@ -1081,16 +1081,16 @@ | |
| 1081 | } |
| 1082 | } |
| 1083 | @ </ul></ul> |
| 1084 | free(prevName); |
| 1085 | db_finalize(&q); |
| 1086 | db_prepare(&q, |
| 1087 | "SELECT substr(tagname, 6, 10000), datetime(event.mtime)," |
| 1088 | " coalesce(event.euser, event.user)" |
| 1089 | " FROM tagxref, tag, event" |
| 1090 | " WHERE tagxref.rid=%d" |
| 1091 | " AND tag.tagid=tagxref.tagid" |
| 1092 | " AND tag.tagname LIKE 'wiki-%%'" |
| 1093 | " AND event.objid=tagxref.rid", |
| 1094 | rid |
| 1095 | ); |
| 1096 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -1152,11 +1152,11 @@ | |
| 1152 | } |
| 1153 | cnt++; |
| 1154 | } |
| 1155 | db_finalize(&q); |
| 1156 | } |
| 1157 | db_prepare(&q, |
| 1158 | "SELECT target, filename, datetime(mtime), user, src" |
| 1159 | " FROM attachment" |
| 1160 | " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)" |
| 1161 | " ORDER BY mtime DESC /*sort*/", |
| 1162 | rid |
| @@ -1287,11 +1287,11 @@ | |
| 1287 | } |
| 1288 | |
| 1289 | /* |
| 1290 | ** WEBPAGE: raw |
| 1291 | ** URL: /raw?name=ARTIFACTID&m=TYPE |
| 1292 | ** |
| 1293 | ** Return the uninterpreted content of an artifact. Used primarily |
| 1294 | ** to view artifacts that are images. |
| 1295 | */ |
| 1296 | void rawartifact_page(void){ |
| 1297 | int rid; |
| @@ -1373,11 +1373,11 @@ | |
| 1373 | } |
| 1374 | |
| 1375 | /* |
| 1376 | ** WEBPAGE: hexdump |
| 1377 | ** URL: /hexdump?name=ARTIFACTID |
| 1378 | ** |
| 1379 | ** Show the complete content of a file identified by ARTIFACTID |
| 1380 | ** as preformatted text. |
| 1381 | */ |
| 1382 | void hexdump_page(void){ |
| 1383 | int rid; |
| @@ -1402,11 +1402,11 @@ | |
| 1402 | style_header("Hex Artifact Content"); |
| 1403 | zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1404 | @ <h2>Artifact %s(zUuid):</h2> |
| 1405 | blob_zero(&downloadName); |
| 1406 | object_description(rid, 0, &downloadName); |
| 1407 | style_submenu_element("Download", "Download", |
| 1408 | "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid); |
| 1409 | @ <hr /> |
| 1410 | content_get(rid, &content); |
| 1411 | @ <blockquote><pre> |
| 1412 | hexdump(&content); |
| @@ -1508,11 +1508,11 @@ | |
| 1508 | ** Additional query parameters: |
| 1509 | ** |
| 1510 | ** ln - show line numbers |
| 1511 | ** ln=N - highlight line number N |
| 1512 | ** ln=M-N - highlight lines M through N inclusive |
| 1513 | ** |
| 1514 | ** Show the complete content of a file identified by ARTIFACTID |
| 1515 | ** as preformatted text. |
| 1516 | */ |
| 1517 | void artifact_page(void){ |
| 1518 | int rid = 0; |
| @@ -1545,11 +1545,11 @@ | |
| 1545 | style_header("Artifact Content"); |
| 1546 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1547 | @ <h2>Artifact %s(zUuid)</h2> |
| 1548 | blob_zero(&downloadName); |
| 1549 | object_description(rid, 0, &downloadName); |
| 1550 | style_submenu_element("Download", "Download", |
| 1551 | "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid); |
| 1552 | zMime = mimetype_from_name(blob_str(&downloadName)); |
| 1553 | if( zMime ){ |
| 1554 | if( fossil_strcmp(zMime, "text/html")==0 ){ |
| 1555 | if( P("txt") ){ |
| @@ -1575,19 +1575,22 @@ | |
| 1575 | content_get(rid, &content); |
| 1576 | if( renderAsWiki ){ |
| 1577 | wiki_convert(&content, 0, 0); |
| 1578 | }else if( renderAsHtml ){ |
| 1579 | @ <div> |
| 1580 | blob_strip_bom(&content, 0); |
| 1581 | cgi_append_content(blob_buffer(&content), blob_size(&content)); |
| 1582 | @ </div> |
| 1583 | }else{ |
| 1584 | style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid); |
| 1585 | zMime = mimetype_from_content(&content); |
| 1586 | @ <blockquote> |
| 1587 | if( zMime==0 ){ |
| 1588 | const char *zLn = P("ln"); |
| 1589 | const char *z; |
| 1590 | blob_strip_bom(&content, 0); |
| 1591 | z = blob_str(&content); |
| 1592 | if( zLn ){ |
| 1593 | output_text_with_line_numbers(z, zLn); |
| 1594 | }else{ |
| 1595 | @ <pre> |
| 1596 | @ %h(z) |
| @@ -1599,11 +1602,11 @@ | |
| 1602 | @ <i>(file is %d(blob_size(&content)) bytes of binary data)</i> |
| 1603 | } |
| 1604 | @ </blockquote> |
| 1605 | } |
| 1606 | style_footer(); |
| 1607 | } |
| 1608 | |
| 1609 | /* |
| 1610 | ** WEBPAGE: tinfo |
| 1611 | ** URL: /tinfo?name=ARTIFACTID |
| 1612 | ** |
| @@ -1663,20 +1666,20 @@ | |
| 1666 | /* |
| 1667 | ** WEBPAGE: info |
| 1668 | ** URL: info/ARTIFACTID |
| 1669 | ** |
| 1670 | ** The argument is a artifact ID which might be a baseline or a file or |
| 1671 | ** a ticket changes or a wiki edit or something else. |
| 1672 | ** |
| 1673 | ** Figure out what the artifact ID is and jump to it. |
| 1674 | */ |
| 1675 | void info_page(void){ |
| 1676 | const char *zName; |
| 1677 | Blob uuid; |
| 1678 | int rid; |
| 1679 | int rc; |
| 1680 | |
| 1681 | zName = P("name"); |
| 1682 | if( zName==0 ) fossil_redirect_home(); |
| 1683 | if( validate16(zName, strlen(zName)) ){ |
| 1684 | if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ |
| 1685 | tktview_page(); |
| @@ -1769,28 +1772,28 @@ | |
| 1772 | { "#d3a8bc", 0 }, |
| 1773 | { "#d3b5a8", 0 }, |
| 1774 | { "#d1d3a8", 0 }, |
| 1775 | { "#b1d3a8", 0 }, |
| 1776 | |
| 1777 | { "#8eb2a1", 0 }, |
| 1778 | { "#8ea7b2", 0 }, |
| 1779 | { "#8f8eb2", 0 }, |
| 1780 | { "#ab8eb2", 0 }, |
| 1781 | { "#b28e9e", 0 }, |
| 1782 | { "#b2988e", 0 }, |
| 1783 | { "#b0b28e", 0 }, |
| 1784 | { "#95b28e", 0 }, |
| 1785 | |
| 1786 | { "#80d6b0", 0 }, |
| 1787 | { "#80bbd6", 0 }, |
| 1788 | { "#8680d6", 0 }, |
| 1789 | { "#c680d6", 0 }, |
| 1790 | { "#d680a6", 0 }, |
| 1791 | { "#d69b80", 0 }, |
| 1792 | { "#d1d680", 0 }, |
| 1793 | { "#91d680", 0 }, |
| 1794 | |
| 1795 | |
| 1796 | { "custom", "##" }, |
| 1797 | }; |
| 1798 | int nColor = sizeof(aColor)/sizeof(aColor[0])-1; |
| 1799 | int stdClrFound = 0; |
| @@ -1893,11 +1896,11 @@ | |
| 1896 | const char *zNewComment; /* Revised check-in comment */ |
| 1897 | const char *zUser; /* Current user for the check-in */ |
| 1898 | const char *zNewUser; /* Revised user */ |
| 1899 | const char *zDate; /* Current date of the check-in */ |
| 1900 | const char *zNewDate; /* Revised check-in date */ |
| 1901 | const char *zColor; |
| 1902 | const char *zNewColor; |
| 1903 | const char *zNewTagFlag; |
| 1904 | const char *zNewTag; |
| 1905 | const char *zNewBrFlag; |
| 1906 | const char *zNewBranch; |
| @@ -1906,11 +1909,11 @@ | |
| 1909 | int fNewPropagateColor; /* True if color propagates after edit */ |
| 1910 | const char *zChngTime = 0; /* Value of chngtime= query param, if any */ |
| 1911 | char *zUuid; |
| 1912 | Blob comment; |
| 1913 | Stmt q; |
| 1914 | |
| 1915 | login_check_credentials(); |
| 1916 | if( !g.perm.Write ){ login_needed(); return; } |
| 1917 | rid = name_to_typed_rid(P("r"), "ci"); |
| 1918 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1919 | zComment = db_text(0, "SELECT coalesce(ecomment,comment)" |
| @@ -1953,11 +1956,11 @@ | |
| 1956 | blob_zero(&ctrl); |
| 1957 | zNow = date_in_standard_format(zChngTime ? zChngTime : "now"); |
| 1958 | blob_appendf(&ctrl, "D %s\n", zNow); |
| 1959 | db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)"); |
| 1960 | if( zNewColor[0] |
| 1961 | && (fPropagateColor!=fNewPropagateColor |
| 1962 | || fossil_strcmp(zColor,zNewColor)!=0) |
| 1963 | ){ |
| 1964 | char *zPrefix = "+"; |
| 1965 | if( fNewPropagateColor ){ |
| 1966 | zPrefix = "*"; |
| 1967 |
+4
-1
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -992,17 +992,19 @@ | ||
| 992 | 992 | |
| 993 | 993 | case 'j': g.perm.RdWiki = 1; break; |
| 994 | 994 | case 'k': g.perm.WrWiki = g.perm.RdWiki = g.perm.ApndWiki =1; break; |
| 995 | 995 | case 'm': g.perm.ApndWiki = 1; break; |
| 996 | 996 | case 'f': g.perm.NewWiki = 1; break; |
| 997 | + case 'l': g.perm.ModWiki = 1; break; | |
| 997 | 998 | |
| 998 | 999 | case 'e': g.perm.RdAddr = 1; break; |
| 999 | 1000 | case 'r': g.perm.RdTkt = 1; break; |
| 1000 | 1001 | case 'n': g.perm.NewTkt = 1; break; |
| 1001 | 1002 | case 'w': g.perm.WrTkt = g.perm.RdTkt = g.perm.NewTkt = |
| 1002 | 1003 | g.perm.ApndTkt = 1; break; |
| 1003 | 1004 | case 'c': g.perm.ApndTkt = 1; break; |
| 1005 | + case 'q': g.perm.ModTkt = 1; break; | |
| 1004 | 1006 | case 't': g.perm.TktFmt = 1; break; |
| 1005 | 1007 | case 'b': g.perm.Attach = 1; break; |
| 1006 | 1008 | case 'x': g.perm.Private = 1; break; |
| 1007 | 1009 | |
| 1008 | 1010 | /* The "u" privileges is a little different. It recursively |
| @@ -1058,15 +1060,16 @@ | ||
| 1058 | 1060 | case 'g': rc = g.perm.Clone; break; |
| 1059 | 1061 | case 'h': rc = g.perm.Hyperlink; break; |
| 1060 | 1062 | case 'i': rc = g.perm.Write; break; |
| 1061 | 1063 | case 'j': rc = g.perm.RdWiki; break; |
| 1062 | 1064 | case 'k': rc = g.perm.WrWiki; break; |
| 1065 | + case 'l': rc = g.perm.ModWiki; break; | |
| 1063 | 1066 | case 'm': rc = g.perm.ApndWiki; break; |
| 1064 | 1067 | case 'n': rc = g.perm.NewTkt; break; |
| 1065 | 1068 | case 'o': rc = g.perm.Read; break; |
| 1066 | 1069 | case 'p': rc = g.perm.Password; break; |
| 1067 | - /* case 'q': */ | |
| 1070 | + case 'q': rc = g.perm.ModTkt; break; | |
| 1068 | 1071 | case 'r': rc = g.perm.RdTkt; break; |
| 1069 | 1072 | case 's': rc = g.perm.Setup; break; |
| 1070 | 1073 | case 't': rc = g.perm.TktFmt; break; |
| 1071 | 1074 | /* case 'u': READER */ |
| 1072 | 1075 | /* case 'v': DEVELOPER */ |
| 1073 | 1076 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -992,17 +992,19 @@ | |
| 992 | |
| 993 | case 'j': g.perm.RdWiki = 1; break; |
| 994 | case 'k': g.perm.WrWiki = g.perm.RdWiki = g.perm.ApndWiki =1; break; |
| 995 | case 'm': g.perm.ApndWiki = 1; break; |
| 996 | case 'f': g.perm.NewWiki = 1; break; |
| 997 | |
| 998 | case 'e': g.perm.RdAddr = 1; break; |
| 999 | case 'r': g.perm.RdTkt = 1; break; |
| 1000 | case 'n': g.perm.NewTkt = 1; break; |
| 1001 | case 'w': g.perm.WrTkt = g.perm.RdTkt = g.perm.NewTkt = |
| 1002 | g.perm.ApndTkt = 1; break; |
| 1003 | case 'c': g.perm.ApndTkt = 1; break; |
| 1004 | case 't': g.perm.TktFmt = 1; break; |
| 1005 | case 'b': g.perm.Attach = 1; break; |
| 1006 | case 'x': g.perm.Private = 1; break; |
| 1007 | |
| 1008 | /* The "u" privileges is a little different. It recursively |
| @@ -1058,15 +1060,16 @@ | |
| 1058 | case 'g': rc = g.perm.Clone; break; |
| 1059 | case 'h': rc = g.perm.Hyperlink; break; |
| 1060 | case 'i': rc = g.perm.Write; break; |
| 1061 | case 'j': rc = g.perm.RdWiki; break; |
| 1062 | case 'k': rc = g.perm.WrWiki; break; |
| 1063 | case 'm': rc = g.perm.ApndWiki; break; |
| 1064 | case 'n': rc = g.perm.NewTkt; break; |
| 1065 | case 'o': rc = g.perm.Read; break; |
| 1066 | case 'p': rc = g.perm.Password; break; |
| 1067 | /* case 'q': */ |
| 1068 | case 'r': rc = g.perm.RdTkt; break; |
| 1069 | case 's': rc = g.perm.Setup; break; |
| 1070 | case 't': rc = g.perm.TktFmt; break; |
| 1071 | /* case 'u': READER */ |
| 1072 | /* case 'v': DEVELOPER */ |
| 1073 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -992,17 +992,19 @@ | |
| 992 | |
| 993 | case 'j': g.perm.RdWiki = 1; break; |
| 994 | case 'k': g.perm.WrWiki = g.perm.RdWiki = g.perm.ApndWiki =1; break; |
| 995 | case 'm': g.perm.ApndWiki = 1; break; |
| 996 | case 'f': g.perm.NewWiki = 1; break; |
| 997 | case 'l': g.perm.ModWiki = 1; break; |
| 998 | |
| 999 | case 'e': g.perm.RdAddr = 1; break; |
| 1000 | case 'r': g.perm.RdTkt = 1; break; |
| 1001 | case 'n': g.perm.NewTkt = 1; break; |
| 1002 | case 'w': g.perm.WrTkt = g.perm.RdTkt = g.perm.NewTkt = |
| 1003 | g.perm.ApndTkt = 1; break; |
| 1004 | case 'c': g.perm.ApndTkt = 1; break; |
| 1005 | case 'q': g.perm.ModTkt = 1; break; |
| 1006 | case 't': g.perm.TktFmt = 1; break; |
| 1007 | case 'b': g.perm.Attach = 1; break; |
| 1008 | case 'x': g.perm.Private = 1; break; |
| 1009 | |
| 1010 | /* The "u" privileges is a little different. It recursively |
| @@ -1058,15 +1060,16 @@ | |
| 1060 | case 'g': rc = g.perm.Clone; break; |
| 1061 | case 'h': rc = g.perm.Hyperlink; break; |
| 1062 | case 'i': rc = g.perm.Write; break; |
| 1063 | case 'j': rc = g.perm.RdWiki; break; |
| 1064 | case 'k': rc = g.perm.WrWiki; break; |
| 1065 | case 'l': rc = g.perm.ModWiki; break; |
| 1066 | case 'm': rc = g.perm.ApndWiki; break; |
| 1067 | case 'n': rc = g.perm.NewTkt; break; |
| 1068 | case 'o': rc = g.perm.Read; break; |
| 1069 | case 'p': rc = g.perm.Password; break; |
| 1070 | case 'q': rc = g.perm.ModTkt; break; |
| 1071 | case 'r': rc = g.perm.RdTkt; break; |
| 1072 | case 's': rc = g.perm.Setup; break; |
| 1073 | case 't': rc = g.perm.TktFmt; break; |
| 1074 | /* case 'u': READER */ |
| 1075 | /* case 'v': DEVELOPER */ |
| 1076 |
+2
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -70,14 +70,16 @@ | ||
| 70 | 70 | char Clone; /* g: clone */ |
| 71 | 71 | char RdWiki; /* j: view wiki via web */ |
| 72 | 72 | char NewWiki; /* f: create new wiki via web */ |
| 73 | 73 | char ApndWiki; /* m: append to wiki via web */ |
| 74 | 74 | char WrWiki; /* k: edit wiki via web */ |
| 75 | + char ModWiki; /* l: approve and publish wiki content (Moderator) */ | |
| 75 | 76 | char RdTkt; /* r: view tickets via web */ |
| 76 | 77 | char NewTkt; /* n: create new tickets */ |
| 77 | 78 | char ApndTkt; /* c: append to tickets via the web */ |
| 78 | 79 | char WrTkt; /* w: make changes to tickets via web */ |
| 80 | + char ModTkt; /* q: approve and publish ticket changes (Moderator) */ | |
| 79 | 81 | char Attach; /* b: add attachments */ |
| 80 | 82 | char TktFmt; /* t: create new ticket report formats */ |
| 81 | 83 | char RdAddr; /* e: read email addresses or other private data */ |
| 82 | 84 | char Zip; /* z: download zipped artifact via /zip URL */ |
| 83 | 85 | char Private; /* x: can send and receive private content */ |
| 84 | 86 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -70,14 +70,16 @@ | |
| 70 | char Clone; /* g: clone */ |
| 71 | char RdWiki; /* j: view wiki via web */ |
| 72 | char NewWiki; /* f: create new wiki via web */ |
| 73 | char ApndWiki; /* m: append to wiki via web */ |
| 74 | char WrWiki; /* k: edit wiki via web */ |
| 75 | char RdTkt; /* r: view tickets via web */ |
| 76 | char NewTkt; /* n: create new tickets */ |
| 77 | char ApndTkt; /* c: append to tickets via the web */ |
| 78 | char WrTkt; /* w: make changes to tickets via web */ |
| 79 | char Attach; /* b: add attachments */ |
| 80 | char TktFmt; /* t: create new ticket report formats */ |
| 81 | char RdAddr; /* e: read email addresses or other private data */ |
| 82 | char Zip; /* z: download zipped artifact via /zip URL */ |
| 83 | char Private; /* x: can send and receive private content */ |
| 84 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -70,14 +70,16 @@ | |
| 70 | char Clone; /* g: clone */ |
| 71 | char RdWiki; /* j: view wiki via web */ |
| 72 | char NewWiki; /* f: create new wiki via web */ |
| 73 | char ApndWiki; /* m: append to wiki via web */ |
| 74 | char WrWiki; /* k: edit wiki via web */ |
| 75 | char ModWiki; /* l: approve and publish wiki content (Moderator) */ |
| 76 | char RdTkt; /* r: view tickets via web */ |
| 77 | char NewTkt; /* n: create new tickets */ |
| 78 | char ApndTkt; /* c: append to tickets via the web */ |
| 79 | char WrTkt; /* w: make changes to tickets via web */ |
| 80 | char ModTkt; /* q: approve and publish ticket changes (Moderator) */ |
| 81 | char Attach; /* b: add attachments */ |
| 82 | char TktFmt; /* t: create new ticket report formats */ |
| 83 | char RdAddr; /* e: read email addresses or other private data */ |
| 84 | char Zip; /* z: download zipped artifact via /zip URL */ |
| 85 | char Private; /* x: can send and receive private content */ |
| 86 |
+165
-136
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -307,10 +307,15 @@ | ||
| 307 | 307 | c = 0; |
| 308 | 308 | } |
| 309 | 309 | return c; |
| 310 | 310 | } |
| 311 | 311 | |
| 312 | +/* | |
| 313 | +** Shorthand for a control-artifact parsing error | |
| 314 | +*/ | |
| 315 | +#define SYNTAX(T) {zErr=(T); goto manifest_syntax_error;} | |
| 316 | + | |
| 312 | 317 | /* |
| 313 | 318 | ** Parse a blob into a Manifest object. The Manifest object |
| 314 | 319 | ** takes over the input blob and will free it when the |
| 315 | 320 | ** Manifest object is freed. Zeros are inserted into the blob |
| 316 | 321 | ** as string terminators so that blob should not be used again. |
| @@ -334,11 +339,11 @@ | ||
| 334 | 339 | ** Each card is divided into tokens by a single space character. |
| 335 | 340 | ** The first token is a single upper-case letter which is the card type. |
| 336 | 341 | ** The card type determines the other parameters to the card. |
| 337 | 342 | ** Cards must occur in lexicographical order. |
| 338 | 343 | */ |
| 339 | -Manifest *manifest_parse(Blob *pContent, int rid){ | |
| 344 | +Manifest *manifest_parse(Blob *pContent, int rid, Blob *pErr){ | |
| 340 | 345 | Manifest *p; |
| 341 | 346 | int seenZ = 0; |
| 342 | 347 | int i, lineNo=0; |
| 343 | 348 | ManifestText x; |
| 344 | 349 | char cPrevType = 0; |
| @@ -347,10 +352,11 @@ | ||
| 347 | 352 | int n; |
| 348 | 353 | char *zUuid; |
| 349 | 354 | int sz = 0; |
| 350 | 355 | int isRepeat; |
| 351 | 356 | static Bag seen; |
| 357 | + const char *zErr = 0; | |
| 352 | 358 | |
| 353 | 359 | if( rid==0 ){ |
| 354 | 360 | isRepeat = 1; |
| 355 | 361 | }else if( bag_find(&seen, rid) ){ |
| 356 | 362 | isRepeat = 1; |
| @@ -365,27 +371,30 @@ | ||
| 365 | 371 | if( !isRepeat ) g.parseCnt[0]++; |
| 366 | 372 | z = blob_materialize(pContent); |
| 367 | 373 | n = blob_size(pContent); |
| 368 | 374 | if( n<=0 || z[n-1]!='\n' ){ |
| 369 | 375 | blob_reset(pContent); |
| 376 | + blob_appendf(pErr, n ? "not terminated with \\n" : "zero-length"); | |
| 370 | 377 | return 0; |
| 371 | 378 | } |
| 372 | 379 | |
| 373 | 380 | /* Strip off the PGP signature if there is one. Then verify the |
| 374 | 381 | ** Z-card. |
| 375 | 382 | */ |
| 376 | 383 | remove_pgp_signature(&z, &n); |
| 377 | - if( verify_z_card(z, n)==0 ){ | |
| 384 | + if( verify_z_card(z, n)==2 ){ | |
| 378 | 385 | blob_reset(pContent); |
| 386 | + blob_appendf(pErr, "incorrect Z-card cksum"); | |
| 379 | 387 | return 0; |
| 380 | 388 | } |
| 381 | 389 | |
| 382 | 390 | /* Verify that the first few characters of the artifact look like |
| 383 | 391 | ** a control artifact. |
| 384 | 392 | */ |
| 385 | 393 | if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){ |
| 386 | 394 | blob_reset(pContent); |
| 395 | + blob_appendf(pErr, "line 1 not recognized"); | |
| 387 | 396 | return 0; |
| 388 | 397 | } |
| 389 | 398 | |
| 390 | 399 | /* Allocate a Manifest object to hold the parsed control artifact. |
| 391 | 400 | */ |
| @@ -420,19 +429,19 @@ | ||
| 420 | 429 | zSrc = next_token(&x, &nSrc); |
| 421 | 430 | if( zName==0 || zTarget==0 ) goto manifest_syntax_error; |
| 422 | 431 | if( p->zAttachName!=0 ) goto manifest_syntax_error; |
| 423 | 432 | defossilize(zName); |
| 424 | 433 | if( !file_is_simple_pathname(zName) ){ |
| 425 | - goto manifest_syntax_error; | |
| 434 | + SYNTAX("invalid filename on A-card"); | |
| 426 | 435 | } |
| 427 | 436 | defossilize(zTarget); |
| 428 | 437 | if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE)) |
| 429 | 438 | && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| 430 | - goto manifest_syntax_error; | |
| 439 | + SYNTAX("invalid target on A-card"); | |
| 431 | 440 | } |
| 432 | 441 | if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){ |
| 433 | - goto manifest_syntax_error; | |
| 442 | + SYNTAX("invalid source on A-card"); | |
| 434 | 443 | } |
| 435 | 444 | p->zAttachName = (char*)file_tail(zName); |
| 436 | 445 | p->zAttachSrc = zSrc; |
| 437 | 446 | p->zAttachTarget = zTarget; |
| 438 | 447 | break; |
| @@ -442,15 +451,16 @@ | ||
| 442 | 451 | ** B <uuid> |
| 443 | 452 | ** |
| 444 | 453 | ** A B-line gives the UUID for the baselinen of a delta-manifest. |
| 445 | 454 | */ |
| 446 | 455 | case 'B': { |
| 447 | - if( p->zBaseline ) goto manifest_syntax_error; | |
| 456 | + if( p->zBaseline ) SYNTAX("more than one B-card"); | |
| 448 | 457 | p->zBaseline = next_token(&x, &sz); |
| 449 | - if( p->zBaseline==0 ) goto manifest_syntax_error; | |
| 450 | - if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 451 | - if( !validate16(p->zBaseline, UUID_SIZE) ) goto manifest_syntax_error; | |
| 458 | + if( p->zBaseline==0 ) SYNTAX("missing UUID on B-card"); | |
| 459 | + if( sz!=UUID_SIZE || !validate16(p->zBaseline, UUID_SIZE) ){ | |
| 460 | + SYNTAX("invalid UUID on B-card"); | |
| 461 | + } | |
| 452 | 462 | break; |
| 453 | 463 | } |
| 454 | 464 | |
| 455 | 465 | |
| 456 | 466 | /* |
| @@ -459,13 +469,13 @@ | ||
| 459 | 469 | ** Comment text is fossil-encoded. There may be no more than |
| 460 | 470 | ** one C line. C lines are required for manifests and are |
| 461 | 471 | ** disallowed on all other control files. |
| 462 | 472 | */ |
| 463 | 473 | case 'C': { |
| 464 | - if( p->zComment!=0 ) goto manifest_syntax_error; | |
| 474 | + if( p->zComment!=0 ) SYNTAX("more than one C-card"); | |
| 465 | 475 | p->zComment = next_token(&x, 0); |
| 466 | - if( p->zComment==0 ) goto manifest_syntax_error; | |
| 476 | + if( p->zComment==0 ) SYNTAX("missing comment text on C-card"); | |
| 467 | 477 | defossilize(p->zComment); |
| 468 | 478 | break; |
| 469 | 479 | } |
| 470 | 480 | |
| 471 | 481 | /* |
| @@ -474,13 +484,13 @@ | ||
| 474 | 484 | ** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS |
| 475 | 485 | ** There can be no more than 1 D line. D lines are required |
| 476 | 486 | ** for all control files except for clusters. |
| 477 | 487 | */ |
| 478 | 488 | case 'D': { |
| 479 | - if( p->rDate>0.0 ) goto manifest_syntax_error; | |
| 489 | + if( p->rDate>0.0 ) SYNTAX("more than one D-card"); | |
| 480 | 490 | p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0)); |
| 481 | - if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 491 | + if( p->rDate<=0.0 ) SYNTAX("cannot parse date on D-card"); | |
| 482 | 492 | break; |
| 483 | 493 | } |
| 484 | 494 | |
| 485 | 495 | /* |
| 486 | 496 | ** E <timestamp> <uuid> |
| @@ -490,16 +500,17 @@ | ||
| 490 | 500 | ** The event timestamp is distinct from the D timestamp. The D |
| 491 | 501 | ** timestamp is when the artifact was created whereas the E timestamp |
| 492 | 502 | ** is when the specific event is said to occur. |
| 493 | 503 | */ |
| 494 | 504 | case 'E': { |
| 495 | - if( p->rEventDate>0.0 ) goto manifest_syntax_error; | |
| 505 | + if( p->rEventDate>0.0 ) SYNTAX("more than one E-card"); | |
| 496 | 506 | p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); |
| 497 | - if( p->rEventDate<=0.0 ) goto manifest_syntax_error; | |
| 507 | + if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card"); | |
| 498 | 508 | p->zEventId = next_token(&x, &sz); |
| 499 | - if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 500 | - if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error; | |
| 509 | + if( sz!=UUID_SIZE || !validate16(p->zEventId, UUID_SIZE) ){ | |
| 510 | + SYNTAX("malformed UUID on E-card"); | |
| 511 | + } | |
| 501 | 512 | break; |
| 502 | 513 | } |
| 503 | 514 | |
| 504 | 515 | /* |
| 505 | 516 | ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>? |
| @@ -509,26 +520,26 @@ | ||
| 509 | 520 | ** other control file. The filename and old-name are fossil-encoded. |
| 510 | 521 | */ |
| 511 | 522 | case 'F': { |
| 512 | 523 | char *zName, *zPerm, *zPriorName; |
| 513 | 524 | zName = next_token(&x,0); |
| 514 | - if( zName==0 ) goto manifest_syntax_error; | |
| 525 | + if( zName==0 ) SYNTAX("missing filename on F-card"); | |
| 515 | 526 | defossilize(zName); |
| 516 | 527 | if( !file_is_simple_pathname(zName) ){ |
| 517 | - goto manifest_syntax_error; | |
| 528 | + SYNTAX("F-card filename is not a simple path"); | |
| 518 | 529 | } |
| 519 | 530 | zUuid = next_token(&x, &sz); |
| 520 | 531 | if( p->zBaseline==0 || zUuid!=0 ){ |
| 521 | - if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 522 | - if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; | |
| 532 | + if( sz!=UUID_SIZE ) SYNTAX("F-card UUID is the wrong size"); | |
| 533 | + if( !validate16(zUuid, UUID_SIZE) ) SYNTAX("F-card UUID invalid"); | |
| 523 | 534 | } |
| 524 | 535 | zPerm = next_token(&x,0); |
| 525 | 536 | zPriorName = next_token(&x,0); |
| 526 | 537 | if( zPriorName ){ |
| 527 | 538 | defossilize(zPriorName); |
| 528 | 539 | if( !file_is_simple_pathname(zPriorName) ){ |
| 529 | - goto manifest_syntax_error; | |
| 540 | + SYNTAX("F-card old filename is not a simple path"); | |
| 530 | 541 | } |
| 531 | 542 | } |
| 532 | 543 | if( p->nFile>=p->nFileAlloc ){ |
| 533 | 544 | p->nFileAlloc = p->nFileAlloc*2 + 10; |
| 534 | 545 | p->aFile = fossil_realloc(p->aFile, |
| @@ -538,11 +549,11 @@ | ||
| 538 | 549 | p->aFile[i].zName = zName; |
| 539 | 550 | p->aFile[i].zUuid = zUuid; |
| 540 | 551 | p->aFile[i].zPerm = zPerm; |
| 541 | 552 | p->aFile[i].zPrior = zPriorName; |
| 542 | 553 | if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){ |
| 543 | - goto manifest_syntax_error; | |
| 554 | + SYNTAX("incorrect F-card sort order"); | |
| 544 | 555 | } |
| 545 | 556 | break; |
| 546 | 557 | } |
| 547 | 558 | |
| 548 | 559 | /* |
| @@ -555,11 +566,11 @@ | ||
| 555 | 566 | */ |
| 556 | 567 | case 'J': { |
| 557 | 568 | char *zName, *zValue; |
| 558 | 569 | zName = next_token(&x,0); |
| 559 | 570 | zValue = next_token(&x,0); |
| 560 | - if( zName==0 ) goto manifest_syntax_error; | |
| 571 | + if( zName==0 ) SYNTAX("name missing from J-card"); | |
| 561 | 572 | if( zValue==0 ) zValue = ""; |
| 562 | 573 | defossilize(zValue); |
| 563 | 574 | if( p->nField>=p->nFieldAlloc ){ |
| 564 | 575 | p->nFieldAlloc = p->nFieldAlloc*2 + 10; |
| 565 | 576 | p->aField = fossil_realloc(p->aField, |
| @@ -567,11 +578,11 @@ | ||
| 567 | 578 | } |
| 568 | 579 | i = p->nField++; |
| 569 | 580 | p->aField[i].zName = zName; |
| 570 | 581 | p->aField[i].zValue = zValue; |
| 571 | 582 | if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){ |
| 572 | - goto manifest_syntax_error; | |
| 583 | + SYNTAX("incorrect J-card sort order"); | |
| 573 | 584 | } |
| 574 | 585 | break; |
| 575 | 586 | } |
| 576 | 587 | |
| 577 | 588 | |
| @@ -580,14 +591,16 @@ | ||
| 580 | 591 | ** |
| 581 | 592 | ** A K-line gives the UUID for the ticket which this control file |
| 582 | 593 | ** is amending. |
| 583 | 594 | */ |
| 584 | 595 | case 'K': { |
| 585 | - if( p->zTicketUuid!=0 ) goto manifest_syntax_error; | |
| 596 | + if( p->zTicketUuid!=0 ) SYNTAX("more than one K-card"); | |
| 586 | 597 | p->zTicketUuid = next_token(&x, &sz); |
| 587 | - if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 588 | - if( !validate16(p->zTicketUuid, UUID_SIZE) ) goto manifest_syntax_error; | |
| 598 | + if( sz!=UUID_SIZE ) SYNTAX("K-card UUID is the wrong size"); | |
| 599 | + if( !validate16(p->zTicketUuid, UUID_SIZE) ){ | |
| 600 | + SYNTAX("invalid K-card UUID"); | |
| 601 | + } | |
| 589 | 602 | break; |
| 590 | 603 | } |
| 591 | 604 | |
| 592 | 605 | /* |
| 593 | 606 | ** L <wikititle> |
| @@ -594,16 +607,16 @@ | ||
| 594 | 607 | ** |
| 595 | 608 | ** The wiki page title is fossil-encoded. There may be no more than |
| 596 | 609 | ** one L line. |
| 597 | 610 | */ |
| 598 | 611 | case 'L': { |
| 599 | - if( p->zWikiTitle!=0 ) goto manifest_syntax_error; | |
| 612 | + if( p->zWikiTitle!=0 ) SYNTAX("more than one L-card"); | |
| 600 | 613 | p->zWikiTitle = next_token(&x,0); |
| 601 | - if( p->zWikiTitle==0 ) goto manifest_syntax_error; | |
| 614 | + if( p->zWikiTitle==0 ) SYNTAX("missing title on L-card"); | |
| 602 | 615 | defossilize(p->zWikiTitle); |
| 603 | 616 | if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){ |
| 604 | - goto manifest_syntax_error; | |
| 617 | + SYNTAX("L-card has malformed wiki name"); | |
| 605 | 618 | } |
| 606 | 619 | break; |
| 607 | 620 | } |
| 608 | 621 | |
| 609 | 622 | /* |
| @@ -612,22 +625,22 @@ | ||
| 612 | 625 | ** An M-line identifies another artifact by its UUID. M-lines |
| 613 | 626 | ** occur in clusters only. |
| 614 | 627 | */ |
| 615 | 628 | case 'M': { |
| 616 | 629 | zUuid = next_token(&x, &sz); |
| 617 | - if( zUuid==0 ) goto manifest_syntax_error; | |
| 618 | - if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 619 | - if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; | |
| 630 | + if( zUuid==0 ) SYNTAX("missing UUID on M-card"); | |
| 631 | + if( sz!=UUID_SIZE ) SYNTAX("wrong size for UUID on M-card"); | |
| 632 | + if( !validate16(zUuid, UUID_SIZE) ) SYNTAX("UUID invalid on M-card"); | |
| 620 | 633 | if( p->nCChild>=p->nCChildAlloc ){ |
| 621 | 634 | p->nCChildAlloc = p->nCChildAlloc*2 + 10; |
| 622 | 635 | p->azCChild = fossil_realloc(p->azCChild |
| 623 | 636 | , p->nCChildAlloc*sizeof(p->azCChild[0]) ); |
| 624 | 637 | } |
| 625 | 638 | i = p->nCChild++; |
| 626 | 639 | p->azCChild[i] = zUuid; |
| 627 | 640 | if( i>0 && fossil_strcmp(p->azCChild[i-1], zUuid)>=0 ){ |
| 628 | - goto manifest_syntax_error; | |
| 641 | + SYNTAX("M-card in the wrong order"); | |
| 629 | 642 | } |
| 630 | 643 | break; |
| 631 | 644 | } |
| 632 | 645 | |
| 633 | 646 | /* |
| @@ -637,12 +650,12 @@ | ||
| 637 | 650 | ** this artifact. The first parent is the primary parent. All |
| 638 | 651 | ** others are parents by merge. |
| 639 | 652 | */ |
| 640 | 653 | case 'P': { |
| 641 | 654 | while( (zUuid = next_token(&x, &sz))!=0 ){ |
| 642 | - if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 643 | - if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; | |
| 655 | + if( sz!=UUID_SIZE ) SYNTAX("wrong size UUID on P-card"); | |
| 656 | + if( !validate16(zUuid, UUID_SIZE) )SYNTAX("invalid UUID on P-card"); | |
| 644 | 657 | if( p->nParent>=p->nParentAlloc ){ |
| 645 | 658 | p->nParentAlloc = p->nParentAlloc*2 + 5; |
| 646 | 659 | p->azParent = fossil_realloc(p->azParent, |
| 647 | 660 | p->nParentAlloc*sizeof(char*)); |
| 648 | 661 | } |
| @@ -657,23 +670,29 @@ | ||
| 657 | 670 | ** |
| 658 | 671 | ** Specify one or a range of checkins that are cherrypicked into |
| 659 | 672 | ** this checkin ("+") or backed out of this checkin ("-"). |
| 660 | 673 | */ |
| 661 | 674 | case 'Q': { |
| 662 | - if( (zUuid = next_token(&x, &sz))==0 ) goto manifest_syntax_error; | |
| 663 | - if( sz!=UUID_SIZE+1 ) goto manifest_syntax_error; | |
| 664 | - if( zUuid[0]!='+' && zUuid[0]!='-' ) goto manifest_syntax_error; | |
| 665 | - if( !validate16(&zUuid[1], UUID_SIZE) ) goto manifest_syntax_error; | |
| 675 | + if( (zUuid=next_token(&x, &sz))==0 ) SYNTAX("missing UUID on Q-card"); | |
| 676 | + if( sz!=UUID_SIZE+1 ) SYNTAX("wrong size UUID on Q-card"); | |
| 677 | + if( zUuid[0]!='+' && zUuid[0]!='-' ){ | |
| 678 | + SYNTAX("Q-card does not begin with '+' or '-'"); | |
| 679 | + } | |
| 680 | + if( !validate16(&zUuid[1], UUID_SIZE) ){ | |
| 681 | + SYNTAX("invalid UUID on Q-card"); | |
| 682 | + } | |
| 666 | 683 | n = p->nCherrypick; |
| 667 | 684 | p->nCherrypick++; |
| 668 | 685 | p->aCherrypick = fossil_realloc(p->aCherrypick, |
| 669 | 686 | p->nCherrypick*sizeof(p->aCherrypick[0])); |
| 670 | 687 | p->aCherrypick[n].zCPTarget = zUuid; |
| 671 | 688 | p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz); |
| 672 | 689 | if( zUuid ){ |
| 673 | - if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 674 | - if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; | |
| 690 | + if( sz!=UUID_SIZE ) SYNTAX("wrong size second UUID in Q-card"); | |
| 691 | + if( !validate16(zUuid, UUID_SIZE) ){ | |
| 692 | + SYNTAX("invalid second UUID on Q-card"); | |
| 693 | + } | |
| 675 | 694 | } |
| 676 | 695 | break; |
| 677 | 696 | } |
| 678 | 697 | |
| 679 | 698 | /* |
| @@ -681,14 +700,14 @@ | ||
| 681 | 700 | ** |
| 682 | 701 | ** Specify the MD5 checksum over the name and content of all files |
| 683 | 702 | ** in the manifest. |
| 684 | 703 | */ |
| 685 | 704 | case 'R': { |
| 686 | - if( p->zRepoCksum!=0 ) goto manifest_syntax_error; | |
| 705 | + if( p->zRepoCksum!=0 ) SYNTAX("more than on R-card"); | |
| 687 | 706 | p->zRepoCksum = next_token(&x, &sz); |
| 688 | - if( sz!=32 ) goto manifest_syntax_error; | |
| 689 | - if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error; | |
| 707 | + if( sz!=32 ) SYNTAX("wrong size cksum on R-card"); | |
| 708 | + if( !validate16(p->zRepoCksum, 32) ) SYNTAX("malformed R-card cksum"); | |
| 690 | 709 | break; |
| 691 | 710 | } |
| 692 | 711 | |
| 693 | 712 | /* |
| 694 | 713 | ** T (+|*|-)<tagname> <uuid> ?<value>? |
| @@ -706,29 +725,29 @@ | ||
| 706 | 725 | ** Tags are not allowed in clusters. Multiple T lines are allowed. |
| 707 | 726 | */ |
| 708 | 727 | case 'T': { |
| 709 | 728 | char *zName, *zValue; |
| 710 | 729 | zName = next_token(&x, 0); |
| 711 | - if( zName==0 ) goto manifest_syntax_error; | |
| 730 | + if( zName==0 ) SYNTAX("missing name on T-card"); | |
| 712 | 731 | zUuid = next_token(&x, &sz); |
| 713 | - if( zUuid==0 ) goto manifest_syntax_error; | |
| 732 | + if( zUuid==0 ) SYNTAX("missing UUID on T-card"); | |
| 714 | 733 | zValue = next_token(&x, 0); |
| 715 | 734 | if( zValue ) defossilize(zValue); |
| 716 | 735 | if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){ |
| 717 | 736 | /* A valid uuid */ |
| 718 | 737 | }else if( sz==1 && zUuid[0]=='*' ){ |
| 719 | 738 | zUuid = 0; |
| 720 | 739 | }else{ |
| 721 | - goto manifest_syntax_error; | |
| 740 | + SYNTAX("malformed UUID on T-card"); | |
| 722 | 741 | } |
| 723 | 742 | defossilize(zName); |
| 724 | 743 | if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){ |
| 725 | - goto manifest_syntax_error; | |
| 744 | + SYNTAX("T-card name does not begin with '-', '+', or '*'"); | |
| 726 | 745 | } |
| 727 | 746 | if( validate16(&zName[1], strlen(&zName[1])) ){ |
| 728 | 747 | /* Do not allow tags whose names look like UUIDs */ |
| 729 | - goto manifest_syntax_error; | |
| 748 | + SYNTAX("T-card name looks like a UUID"); | |
| 730 | 749 | } |
| 731 | 750 | if( p->nTag>=p->nTagAlloc ){ |
| 732 | 751 | p->nTagAlloc = p->nTagAlloc*2 + 10; |
| 733 | 752 | p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) ); |
| 734 | 753 | } |
| @@ -735,11 +754,11 @@ | ||
| 735 | 754 | i = p->nTag++; |
| 736 | 755 | p->aTag[i].zName = zName; |
| 737 | 756 | p->aTag[i].zUuid = zUuid; |
| 738 | 757 | p->aTag[i].zValue = zValue; |
| 739 | 758 | if( i>0 && fossil_strcmp(p->aTag[i-1].zName, zName)>=0 ){ |
| 740 | - goto manifest_syntax_error; | |
| 759 | + SYNTAX("T-card in the wrong order"); | |
| 741 | 760 | } |
| 742 | 761 | break; |
| 743 | 762 | } |
| 744 | 763 | |
| 745 | 764 | /* |
| @@ -748,11 +767,11 @@ | ||
| 748 | 767 | ** Identify the user who created this control file by their |
| 749 | 768 | ** login. Only one U line is allowed. Prohibited in clusters. |
| 750 | 769 | ** If the user name is omitted, take that to be "anonymous". |
| 751 | 770 | */ |
| 752 | 771 | case 'U': { |
| 753 | - if( p->zUser!=0 ) goto manifest_syntax_error; | |
| 772 | + if( p->zUser!=0 ) SYNTAX("more than on U-card"); | |
| 754 | 773 | p->zUser = next_token(&x, 0); |
| 755 | 774 | if( p->zUser==0 ){ |
| 756 | 775 | p->zUser = "anonymous"; |
| 757 | 776 | }else{ |
| 758 | 777 | defossilize(p->zUser); |
| @@ -767,25 +786,26 @@ | ||
| 767 | 786 | ** page. There is always an extra \n before the start of the next |
| 768 | 787 | ** record. |
| 769 | 788 | */ |
| 770 | 789 | case 'W': { |
| 771 | 790 | char *zSize; |
| 772 | - int size, c; | |
| 791 | + unsigned size, oldsize, c; | |
| 773 | 792 | Blob wiki; |
| 774 | 793 | zSize = next_token(&x, 0); |
| 775 | - if( zSize==0 ) goto manifest_syntax_error; | |
| 776 | - if( x.atEol==0 ) goto manifest_syntax_error; | |
| 777 | - for(size=0; (c = zSize[0])>='0' && c<='9'; zSize++){ | |
| 778 | - size = size*10 + c - '0'; | |
| 794 | + if( zSize==0 ) SYNTAX("missing size on W-card"); | |
| 795 | + if( x.atEol==0 ) SYNTAX("no content after W-card"); | |
| 796 | + for(oldsize=size=0; (c = zSize[0])>='0' && c<='9'; zSize++){ | |
| 797 | + size = oldsize*10 + c - '0'; | |
| 798 | + if( size<oldsize ) SYNTAX("size overflow on W-card"); | |
| 799 | + oldsize = size; | |
| 779 | 800 | } |
| 780 | - if( size<0 ) goto manifest_syntax_error; | |
| 781 | - if( p->zWiki!=0 ) goto manifest_syntax_error; | |
| 801 | + if( p->zWiki!=0 ) SYNTAX("more than one W-card"); | |
| 782 | 802 | blob_zero(&wiki); |
| 783 | - if( (&x.z[size+1])>=x.zEnd ) goto manifest_syntax_error; | |
| 803 | + if( (&x.z[size+1])>=x.zEnd )SYNTAX("not enough content after W-card"); | |
| 784 | 804 | p->zWiki = x.z; |
| 785 | 805 | x.z += size; |
| 786 | - if( x.z[0]!='\n' ) goto manifest_syntax_error; | |
| 806 | + if( x.z[0]!='\n' ) SYNTAX("W-card content no \\n terminated"); | |
| 787 | 807 | x.z[0] = 0; |
| 788 | 808 | x.z++; |
| 789 | 809 | break; |
| 790 | 810 | } |
| 791 | 811 | |
| @@ -801,111 +821,117 @@ | ||
| 801 | 821 | ** Manifest. It is not required for manifest only for historical |
| 802 | 822 | ** compatibility reasons. |
| 803 | 823 | */ |
| 804 | 824 | case 'Z': { |
| 805 | 825 | zUuid = next_token(&x, &sz); |
| 806 | - if( sz!=32 ) goto manifest_syntax_error; | |
| 807 | - if( !validate16(zUuid, 32) ) goto manifest_syntax_error; | |
| 826 | + if( sz!=32 ) SYNTAX("wrong size for Z-card cksum"); | |
| 827 | + if( !validate16(zUuid, 32) ) SYNTAX("malformed Z-card cksum"); | |
| 808 | 828 | seenZ = 1; |
| 809 | 829 | break; |
| 810 | 830 | } |
| 811 | 831 | default: { |
| 812 | - goto manifest_syntax_error; | |
| 832 | + SYNTAX("unrecognized card"); | |
| 813 | 833 | } |
| 814 | 834 | } |
| 815 | 835 | } |
| 816 | - if( x.z<x.zEnd ) goto manifest_syntax_error; | |
| 836 | + if( x.z<x.zEnd ) SYNTAX("card in the wrong order"); | |
| 817 | 837 | |
| 818 | 838 | if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){ |
| 819 | - if( p->nCChild>0 ) goto manifest_syntax_error; | |
| 820 | - if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 821 | - if( p->nField>0 ) goto manifest_syntax_error; | |
| 822 | - if( p->zTicketUuid ) goto manifest_syntax_error; | |
| 823 | - if( p->zWiki ) goto manifest_syntax_error; | |
| 824 | - if( p->zWikiTitle ) goto manifest_syntax_error; | |
| 825 | - if( p->zEventId ) goto manifest_syntax_error; | |
| 826 | - if( p->zTicketUuid ) goto manifest_syntax_error; | |
| 827 | - if( p->zAttachName ) goto manifest_syntax_error; | |
| 839 | + if( p->nCChild>0 ) SYNTAX("M-card in check-in"); | |
| 840 | + if( p->rDate<=0.0 ) SYNTAX("missing date for check-in"); | |
| 841 | + if( p->nField>0 ) SYNTAX("J-card in check-in"); | |
| 842 | + if( p->zTicketUuid ) SYNTAX("K-card in check-in"); | |
| 843 | + if( p->zWiki ) SYNTAX("W-card in check-in"); | |
| 844 | + if( p->zWikiTitle ) SYNTAX("L-card in check-in"); | |
| 845 | + if( p->zEventId ) SYNTAX("E-card in check-in"); | |
| 846 | + if( p->zTicketUuid ) SYNTAX("K-card in check-in"); | |
| 847 | + if( p->zAttachName ) SYNTAX("A-card in check-in"); | |
| 828 | 848 | p->type = CFTYPE_MANIFEST; |
| 829 | 849 | }else if( p->nCChild>0 ){ |
| 830 | - if( p->rDate>0.0 ) goto manifest_syntax_error; | |
| 831 | - if( p->zComment!=0 ) goto manifest_syntax_error; | |
| 832 | - if( p->zUser!=0 ) goto manifest_syntax_error; | |
| 833 | - if( p->nTag>0 ) goto manifest_syntax_error; | |
| 834 | - if( p->nParent>0 ) goto manifest_syntax_error; | |
| 835 | - if( p->nField>0 ) goto manifest_syntax_error; | |
| 836 | - if( p->zTicketUuid ) goto manifest_syntax_error; | |
| 837 | - if( p->zWiki ) goto manifest_syntax_error; | |
| 838 | - if( p->zWikiTitle ) goto manifest_syntax_error; | |
| 839 | - if( p->zEventId ) goto manifest_syntax_error; | |
| 840 | - if( p->zAttachName ) goto manifest_syntax_error; | |
| 841 | - if( !seenZ ) goto manifest_syntax_error; | |
| 850 | + if( p->rDate>0.0 | |
| 851 | + || p->zComment!=0 | |
| 852 | + || p->zUser!=0 | |
| 853 | + || p->nTag>0 | |
| 854 | + || p->nParent>0 | |
| 855 | + || p->nField>0 | |
| 856 | + || p->zTicketUuid | |
| 857 | + || p->zWiki | |
| 858 | + || p->zWikiTitle | |
| 859 | + || p->zEventId | |
| 860 | + || p->zAttachName | |
| 861 | + ){ | |
| 862 | + SYNTAX("cluster contains a card other than M- or Z-"); | |
| 863 | + } | |
| 864 | + if( !seenZ ) SYNTAX("missing Z-card on cluster"); | |
| 842 | 865 | p->type = CFTYPE_CLUSTER; |
| 843 | 866 | }else if( p->nField>0 ){ |
| 844 | - if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 845 | - if( p->zWiki ) goto manifest_syntax_error; | |
| 846 | - if( p->zWikiTitle ) goto manifest_syntax_error; | |
| 847 | - if( p->zEventId ) goto manifest_syntax_error; | |
| 848 | - if( p->nCChild>0 ) goto manifest_syntax_error; | |
| 849 | - if( p->nTag>0 ) goto manifest_syntax_error; | |
| 850 | - if( p->zTicketUuid==0 ) goto manifest_syntax_error; | |
| 851 | - if( p->zUser==0 ) goto manifest_syntax_error; | |
| 852 | - if( p->zAttachName ) goto manifest_syntax_error; | |
| 853 | - if( !seenZ ) goto manifest_syntax_error; | |
| 867 | + if( p->rDate<=0.0 ) SYNTAX("missing date for ticket"); | |
| 868 | + if( p->zWiki ) SYNTAX("W-card in ticket"); | |
| 869 | + if( p->zWikiTitle ) SYNTAX("L-card in ticket"); | |
| 870 | + if( p->zEventId ) SYNTAX("E-card in ticket"); | |
| 871 | + if( p->nCChild>0 ) SYNTAX("M-card in ticket"); | |
| 872 | + if( p->nTag>0 ) SYNTAX("T-card in ticket"); | |
| 873 | + if( p->zTicketUuid==0 ) SYNTAX("missing K-card in ticket"); | |
| 874 | + if( p->zUser==0 ) SYNTAX("missing U-card in ticket"); | |
| 875 | + if( p->zAttachName ) SYNTAX("A-card in ticket"); | |
| 876 | + if( !seenZ ) SYNTAX("missing Z-card in ticket"); | |
| 854 | 877 | p->type = CFTYPE_TICKET; |
| 855 | 878 | }else if( p->zEventId ){ |
| 856 | - if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 857 | - if( p->nCChild>0 ) goto manifest_syntax_error; | |
| 858 | - if( p->zTicketUuid!=0 ) goto manifest_syntax_error; | |
| 859 | - if( p->zWikiTitle!=0 ) goto manifest_syntax_error; | |
| 860 | - if( p->zWiki==0 ) goto manifest_syntax_error; | |
| 861 | - if( p->zAttachName ) goto manifest_syntax_error; | |
| 879 | + if( p->rDate<=0.0 ) SYNTAX("missing date for event"); | |
| 880 | + if( p->nCChild>0 ) SYNTAX("M-card in event"); | |
| 881 | + if( p->zTicketUuid!=0 ) SYNTAX("K-card in event"); | |
| 882 | + if( p->zWikiTitle!=0 ) SYNTAX("L-card in event"); | |
| 883 | + if( p->zWiki==0 ) SYNTAX("W-card in event"); | |
| 884 | + if( p->zAttachName ) SYNTAX("A-card in event"); | |
| 862 | 885 | for(i=0; i<p->nTag; i++){ |
| 863 | - if( p->aTag[i].zName[0]!='+' ) goto manifest_syntax_error; | |
| 864 | - if( p->aTag[i].zUuid!=0 ) goto manifest_syntax_error; | |
| 886 | + if( p->aTag[i].zName[0]!='+' ) SYNTAX("propagating tag in event"); | |
| 887 | + if( p->aTag[i].zUuid!=0 ) SYNTAX("non-self-referential tag in event"); | |
| 865 | 888 | } |
| 866 | - if( !seenZ ) goto manifest_syntax_error; | |
| 889 | + if( !seenZ ) SYNTAX("Z-card missing in event"); | |
| 867 | 890 | p->type = CFTYPE_EVENT; |
| 868 | 891 | }else if( p->zWiki!=0 ){ |
| 869 | - if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 870 | - if( p->nCChild>0 ) goto manifest_syntax_error; | |
| 871 | - if( p->nTag>0 ) goto manifest_syntax_error; | |
| 872 | - if( p->zTicketUuid!=0 ) goto manifest_syntax_error; | |
| 873 | - if( p->zWikiTitle==0 ) goto manifest_syntax_error; | |
| 874 | - if( p->zAttachName ) goto manifest_syntax_error; | |
| 875 | - if( !seenZ ) goto manifest_syntax_error; | |
| 892 | + if( p->rDate<=0.0 ) SYNTAX("date missing on wiki"); | |
| 893 | + if( p->nCChild>0 ) SYNTAX("M-card in wiki"); | |
| 894 | + if( p->nTag>0 ) SYNTAX("T-card in wiki"); | |
| 895 | + if( p->zTicketUuid!=0 ) SYNTAX("K-card in wiki"); | |
| 896 | + if( p->zWikiTitle==0 ) SYNTAX("L-card in wiki"); | |
| 897 | + if( p->zAttachName ) SYNTAX("A-card in wiki"); | |
| 898 | + if( !seenZ ) SYNTAX("missing Z-card on wiki"); | |
| 876 | 899 | p->type = CFTYPE_WIKI; |
| 877 | 900 | }else if( p->nTag>0 ){ |
| 878 | - if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 879 | - if( p->nParent>0 ) goto manifest_syntax_error; | |
| 880 | - if( p->zWikiTitle ) goto manifest_syntax_error; | |
| 881 | - if( p->zTicketUuid ) goto manifest_syntax_error; | |
| 882 | - if( p->zAttachName ) goto manifest_syntax_error; | |
| 883 | - if( !seenZ ) goto manifest_syntax_error; | |
| 901 | + if( p->rDate<=0.0 ) SYNTAX("date missing on tag"); | |
| 902 | + if( p->nParent>0 ) SYNTAX("P-card on tag"); | |
| 903 | + if( p->zWikiTitle ) SYNTAX("L-card on tag"); | |
| 904 | + if( p->zTicketUuid ) SYNTAX("K-card in tag"); | |
| 905 | + if( p->zAttachName ) SYNTAX("A-card in tag"); | |
| 906 | + if( !seenZ ) SYNTAX("missing Z-card on tag"); | |
| 884 | 907 | p->type = CFTYPE_CONTROL; |
| 885 | 908 | }else if( p->zAttachName ){ |
| 886 | - if( p->nCChild>0 ) goto manifest_syntax_error; | |
| 887 | - if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 888 | - if( p->zTicketUuid ) goto manifest_syntax_error; | |
| 889 | - if( p->zWikiTitle ) goto manifest_syntax_error; | |
| 890 | - if( !seenZ ) goto manifest_syntax_error; | |
| 909 | + if( p->nCChild>0 ) SYNTAX("M-card in attachment"); | |
| 910 | + if( p->rDate<=0.0 ) SYNTAX("missing date in attachment"); | |
| 911 | + if( p->zTicketUuid ) SYNTAX("K-card in attachment"); | |
| 912 | + if( p->zWikiTitle ) SYNTAX("L-card in attachment"); | |
| 913 | + if( !seenZ ) SYNTAX("missing Z-card on attachment"); | |
| 891 | 914 | p->type = CFTYPE_ATTACHMENT; |
| 892 | 915 | }else{ |
| 893 | - if( p->nCChild>0 ) goto manifest_syntax_error; | |
| 894 | - if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 895 | - if( p->nField>0 ) goto manifest_syntax_error; | |
| 896 | - if( p->zTicketUuid ) goto manifest_syntax_error; | |
| 897 | - if( p->zWikiTitle ) goto manifest_syntax_error; | |
| 898 | - if( p->zTicketUuid ) goto manifest_syntax_error; | |
| 916 | + if( p->nCChild>0 ) SYNTAX("M-card in check-in"); | |
| 917 | + if( p->rDate<=0.0 ) SYNTAX("missing date in check-in"); | |
| 918 | + if( p->nField>0 ) SYNTAX("J-card in check-in"); | |
| 919 | + if( p->zTicketUuid ) SYNTAX("K-card in check-in"); | |
| 920 | + if( p->zWikiTitle ) SYNTAX("L-card in check-in"); | |
| 899 | 921 | p->type = CFTYPE_MANIFEST; |
| 900 | 922 | } |
| 901 | 923 | md5sum_init(); |
| 902 | 924 | if( !isRepeat ) g.parseCnt[p->type]++; |
| 903 | 925 | return p; |
| 904 | 926 | |
| 905 | 927 | manifest_syntax_error: |
| 906 | - /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/ | |
| 928 | + if( zErr ){ | |
| 929 | + blob_appendf(pErr, "line %d: %s", lineNo, zErr); | |
| 930 | + }else{ | |
| 931 | + blob_appendf(pErr, "unknown error on line %d", lineNo); | |
| 932 | + } | |
| 907 | 933 | md5sum_init(); |
| 908 | 934 | manifest_destroy(p); |
| 909 | 935 | return 0; |
| 910 | 936 | } |
| 911 | 937 | |
| @@ -924,11 +950,11 @@ | ||
| 924 | 950 | p = 0; |
| 925 | 951 | } |
| 926 | 952 | return p; |
| 927 | 953 | } |
| 928 | 954 | content_get(rid, &content); |
| 929 | - p = manifest_parse(&content, rid); | |
| 955 | + p = manifest_parse(&content, rid, 0); | |
| 930 | 956 | if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){ |
| 931 | 957 | manifest_destroy(p); |
| 932 | 958 | p = 0; |
| 933 | 959 | } |
| 934 | 960 | return p; |
| @@ -972,13 +998,16 @@ | ||
| 972 | 998 | } |
| 973 | 999 | blob_read_from_file(&b, g.argv[2]); |
| 974 | 1000 | if( g.argc>3 ) n = atoi(g.argv[3]); |
| 975 | 1001 | for(i=0; i<n; i++){ |
| 976 | 1002 | Blob b2; |
| 1003 | + Blob err; | |
| 977 | 1004 | blob_copy(&b2, &b); |
| 978 | - p = manifest_parse(&b2, 0); | |
| 979 | - if( p==0 ) fossil_print("FAILED!\n"); | |
| 1005 | + blob_zero(&err); | |
| 1006 | + p = manifest_parse(&b2, 0, &err); | |
| 1007 | + if( p==0 ) fossil_print("ERROR: %s\n", blob_str(&err)); | |
| 1008 | + blob_reset(&err); | |
| 980 | 1009 | manifest_destroy(p); |
| 981 | 1010 | } |
| 982 | 1011 | } |
| 983 | 1012 | |
| 984 | 1013 | /* |
| @@ -1299,11 +1328,11 @@ | ||
| 1299 | 1328 | otherRid = cid; |
| 1300 | 1329 | } |
| 1301 | 1330 | if( (*ppOther = manifest_cache_find(otherRid))==0 ){ |
| 1302 | 1331 | content_get(otherRid, &otherContent); |
| 1303 | 1332 | if( blob_size(&otherContent)==0 ) return; |
| 1304 | - *ppOther = manifest_parse(&otherContent, otherRid); | |
| 1333 | + *ppOther = manifest_parse(&otherContent, otherRid, 0); | |
| 1305 | 1334 | if( *ppOther==0 ) return; |
| 1306 | 1335 | } |
| 1307 | 1336 | if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){ |
| 1308 | 1337 | manifest_destroy(*ppOther); |
| 1309 | 1338 | return; |
| @@ -1597,11 +1626,11 @@ | ||
| 1597 | 1626 | Stmt q; |
| 1598 | 1627 | int parentid = 0; |
| 1599 | 1628 | |
| 1600 | 1629 | if( (p = manifest_cache_find(rid))!=0 ){ |
| 1601 | 1630 | blob_reset(pContent); |
| 1602 | - }else if( (p = manifest_parse(pContent, rid))==0 ){ | |
| 1631 | + }else if( (p = manifest_parse(pContent, rid, 0))==0 ){ | |
| 1603 | 1632 | assert( blob_is_reset(pContent) || pContent==0 ); |
| 1604 | 1633 | return 0; |
| 1605 | 1634 | } |
| 1606 | 1635 | if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ |
| 1607 | 1636 | manifest_destroy(p); |
| 1608 | 1637 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -307,10 +307,15 @@ | |
| 307 | c = 0; |
| 308 | } |
| 309 | return c; |
| 310 | } |
| 311 | |
| 312 | /* |
| 313 | ** Parse a blob into a Manifest object. The Manifest object |
| 314 | ** takes over the input blob and will free it when the |
| 315 | ** Manifest object is freed. Zeros are inserted into the blob |
| 316 | ** as string terminators so that blob should not be used again. |
| @@ -334,11 +339,11 @@ | |
| 334 | ** Each card is divided into tokens by a single space character. |
| 335 | ** The first token is a single upper-case letter which is the card type. |
| 336 | ** The card type determines the other parameters to the card. |
| 337 | ** Cards must occur in lexicographical order. |
| 338 | */ |
| 339 | Manifest *manifest_parse(Blob *pContent, int rid){ |
| 340 | Manifest *p; |
| 341 | int seenZ = 0; |
| 342 | int i, lineNo=0; |
| 343 | ManifestText x; |
| 344 | char cPrevType = 0; |
| @@ -347,10 +352,11 @@ | |
| 347 | int n; |
| 348 | char *zUuid; |
| 349 | int sz = 0; |
| 350 | int isRepeat; |
| 351 | static Bag seen; |
| 352 | |
| 353 | if( rid==0 ){ |
| 354 | isRepeat = 1; |
| 355 | }else if( bag_find(&seen, rid) ){ |
| 356 | isRepeat = 1; |
| @@ -365,27 +371,30 @@ | |
| 365 | if( !isRepeat ) g.parseCnt[0]++; |
| 366 | z = blob_materialize(pContent); |
| 367 | n = blob_size(pContent); |
| 368 | if( n<=0 || z[n-1]!='\n' ){ |
| 369 | blob_reset(pContent); |
| 370 | return 0; |
| 371 | } |
| 372 | |
| 373 | /* Strip off the PGP signature if there is one. Then verify the |
| 374 | ** Z-card. |
| 375 | */ |
| 376 | remove_pgp_signature(&z, &n); |
| 377 | if( verify_z_card(z, n)==0 ){ |
| 378 | blob_reset(pContent); |
| 379 | return 0; |
| 380 | } |
| 381 | |
| 382 | /* Verify that the first few characters of the artifact look like |
| 383 | ** a control artifact. |
| 384 | */ |
| 385 | if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){ |
| 386 | blob_reset(pContent); |
| 387 | return 0; |
| 388 | } |
| 389 | |
| 390 | /* Allocate a Manifest object to hold the parsed control artifact. |
| 391 | */ |
| @@ -420,19 +429,19 @@ | |
| 420 | zSrc = next_token(&x, &nSrc); |
| 421 | if( zName==0 || zTarget==0 ) goto manifest_syntax_error; |
| 422 | if( p->zAttachName!=0 ) goto manifest_syntax_error; |
| 423 | defossilize(zName); |
| 424 | if( !file_is_simple_pathname(zName) ){ |
| 425 | goto manifest_syntax_error; |
| 426 | } |
| 427 | defossilize(zTarget); |
| 428 | if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE)) |
| 429 | && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| 430 | goto manifest_syntax_error; |
| 431 | } |
| 432 | if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){ |
| 433 | goto manifest_syntax_error; |
| 434 | } |
| 435 | p->zAttachName = (char*)file_tail(zName); |
| 436 | p->zAttachSrc = zSrc; |
| 437 | p->zAttachTarget = zTarget; |
| 438 | break; |
| @@ -442,15 +451,16 @@ | |
| 442 | ** B <uuid> |
| 443 | ** |
| 444 | ** A B-line gives the UUID for the baselinen of a delta-manifest. |
| 445 | */ |
| 446 | case 'B': { |
| 447 | if( p->zBaseline ) goto manifest_syntax_error; |
| 448 | p->zBaseline = next_token(&x, &sz); |
| 449 | if( p->zBaseline==0 ) goto manifest_syntax_error; |
| 450 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 451 | if( !validate16(p->zBaseline, UUID_SIZE) ) goto manifest_syntax_error; |
| 452 | break; |
| 453 | } |
| 454 | |
| 455 | |
| 456 | /* |
| @@ -459,13 +469,13 @@ | |
| 459 | ** Comment text is fossil-encoded. There may be no more than |
| 460 | ** one C line. C lines are required for manifests and are |
| 461 | ** disallowed on all other control files. |
| 462 | */ |
| 463 | case 'C': { |
| 464 | if( p->zComment!=0 ) goto manifest_syntax_error; |
| 465 | p->zComment = next_token(&x, 0); |
| 466 | if( p->zComment==0 ) goto manifest_syntax_error; |
| 467 | defossilize(p->zComment); |
| 468 | break; |
| 469 | } |
| 470 | |
| 471 | /* |
| @@ -474,13 +484,13 @@ | |
| 474 | ** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS |
| 475 | ** There can be no more than 1 D line. D lines are required |
| 476 | ** for all control files except for clusters. |
| 477 | */ |
| 478 | case 'D': { |
| 479 | if( p->rDate>0.0 ) goto manifest_syntax_error; |
| 480 | p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0)); |
| 481 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 482 | break; |
| 483 | } |
| 484 | |
| 485 | /* |
| 486 | ** E <timestamp> <uuid> |
| @@ -490,16 +500,17 @@ | |
| 490 | ** The event timestamp is distinct from the D timestamp. The D |
| 491 | ** timestamp is when the artifact was created whereas the E timestamp |
| 492 | ** is when the specific event is said to occur. |
| 493 | */ |
| 494 | case 'E': { |
| 495 | if( p->rEventDate>0.0 ) goto manifest_syntax_error; |
| 496 | p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); |
| 497 | if( p->rEventDate<=0.0 ) goto manifest_syntax_error; |
| 498 | p->zEventId = next_token(&x, &sz); |
| 499 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 500 | if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error; |
| 501 | break; |
| 502 | } |
| 503 | |
| 504 | /* |
| 505 | ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>? |
| @@ -509,26 +520,26 @@ | |
| 509 | ** other control file. The filename and old-name are fossil-encoded. |
| 510 | */ |
| 511 | case 'F': { |
| 512 | char *zName, *zPerm, *zPriorName; |
| 513 | zName = next_token(&x,0); |
| 514 | if( zName==0 ) goto manifest_syntax_error; |
| 515 | defossilize(zName); |
| 516 | if( !file_is_simple_pathname(zName) ){ |
| 517 | goto manifest_syntax_error; |
| 518 | } |
| 519 | zUuid = next_token(&x, &sz); |
| 520 | if( p->zBaseline==0 || zUuid!=0 ){ |
| 521 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 522 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 523 | } |
| 524 | zPerm = next_token(&x,0); |
| 525 | zPriorName = next_token(&x,0); |
| 526 | if( zPriorName ){ |
| 527 | defossilize(zPriorName); |
| 528 | if( !file_is_simple_pathname(zPriorName) ){ |
| 529 | goto manifest_syntax_error; |
| 530 | } |
| 531 | } |
| 532 | if( p->nFile>=p->nFileAlloc ){ |
| 533 | p->nFileAlloc = p->nFileAlloc*2 + 10; |
| 534 | p->aFile = fossil_realloc(p->aFile, |
| @@ -538,11 +549,11 @@ | |
| 538 | p->aFile[i].zName = zName; |
| 539 | p->aFile[i].zUuid = zUuid; |
| 540 | p->aFile[i].zPerm = zPerm; |
| 541 | p->aFile[i].zPrior = zPriorName; |
| 542 | if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){ |
| 543 | goto manifest_syntax_error; |
| 544 | } |
| 545 | break; |
| 546 | } |
| 547 | |
| 548 | /* |
| @@ -555,11 +566,11 @@ | |
| 555 | */ |
| 556 | case 'J': { |
| 557 | char *zName, *zValue; |
| 558 | zName = next_token(&x,0); |
| 559 | zValue = next_token(&x,0); |
| 560 | if( zName==0 ) goto manifest_syntax_error; |
| 561 | if( zValue==0 ) zValue = ""; |
| 562 | defossilize(zValue); |
| 563 | if( p->nField>=p->nFieldAlloc ){ |
| 564 | p->nFieldAlloc = p->nFieldAlloc*2 + 10; |
| 565 | p->aField = fossil_realloc(p->aField, |
| @@ -567,11 +578,11 @@ | |
| 567 | } |
| 568 | i = p->nField++; |
| 569 | p->aField[i].zName = zName; |
| 570 | p->aField[i].zValue = zValue; |
| 571 | if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){ |
| 572 | goto manifest_syntax_error; |
| 573 | } |
| 574 | break; |
| 575 | } |
| 576 | |
| 577 | |
| @@ -580,14 +591,16 @@ | |
| 580 | ** |
| 581 | ** A K-line gives the UUID for the ticket which this control file |
| 582 | ** is amending. |
| 583 | */ |
| 584 | case 'K': { |
| 585 | if( p->zTicketUuid!=0 ) goto manifest_syntax_error; |
| 586 | p->zTicketUuid = next_token(&x, &sz); |
| 587 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 588 | if( !validate16(p->zTicketUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 589 | break; |
| 590 | } |
| 591 | |
| 592 | /* |
| 593 | ** L <wikititle> |
| @@ -594,16 +607,16 @@ | |
| 594 | ** |
| 595 | ** The wiki page title is fossil-encoded. There may be no more than |
| 596 | ** one L line. |
| 597 | */ |
| 598 | case 'L': { |
| 599 | if( p->zWikiTitle!=0 ) goto manifest_syntax_error; |
| 600 | p->zWikiTitle = next_token(&x,0); |
| 601 | if( p->zWikiTitle==0 ) goto manifest_syntax_error; |
| 602 | defossilize(p->zWikiTitle); |
| 603 | if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){ |
| 604 | goto manifest_syntax_error; |
| 605 | } |
| 606 | break; |
| 607 | } |
| 608 | |
| 609 | /* |
| @@ -612,22 +625,22 @@ | |
| 612 | ** An M-line identifies another artifact by its UUID. M-lines |
| 613 | ** occur in clusters only. |
| 614 | */ |
| 615 | case 'M': { |
| 616 | zUuid = next_token(&x, &sz); |
| 617 | if( zUuid==0 ) goto manifest_syntax_error; |
| 618 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 619 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 620 | if( p->nCChild>=p->nCChildAlloc ){ |
| 621 | p->nCChildAlloc = p->nCChildAlloc*2 + 10; |
| 622 | p->azCChild = fossil_realloc(p->azCChild |
| 623 | , p->nCChildAlloc*sizeof(p->azCChild[0]) ); |
| 624 | } |
| 625 | i = p->nCChild++; |
| 626 | p->azCChild[i] = zUuid; |
| 627 | if( i>0 && fossil_strcmp(p->azCChild[i-1], zUuid)>=0 ){ |
| 628 | goto manifest_syntax_error; |
| 629 | } |
| 630 | break; |
| 631 | } |
| 632 | |
| 633 | /* |
| @@ -637,12 +650,12 @@ | |
| 637 | ** this artifact. The first parent is the primary parent. All |
| 638 | ** others are parents by merge. |
| 639 | */ |
| 640 | case 'P': { |
| 641 | while( (zUuid = next_token(&x, &sz))!=0 ){ |
| 642 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 643 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 644 | if( p->nParent>=p->nParentAlloc ){ |
| 645 | p->nParentAlloc = p->nParentAlloc*2 + 5; |
| 646 | p->azParent = fossil_realloc(p->azParent, |
| 647 | p->nParentAlloc*sizeof(char*)); |
| 648 | } |
| @@ -657,23 +670,29 @@ | |
| 657 | ** |
| 658 | ** Specify one or a range of checkins that are cherrypicked into |
| 659 | ** this checkin ("+") or backed out of this checkin ("-"). |
| 660 | */ |
| 661 | case 'Q': { |
| 662 | if( (zUuid = next_token(&x, &sz))==0 ) goto manifest_syntax_error; |
| 663 | if( sz!=UUID_SIZE+1 ) goto manifest_syntax_error; |
| 664 | if( zUuid[0]!='+' && zUuid[0]!='-' ) goto manifest_syntax_error; |
| 665 | if( !validate16(&zUuid[1], UUID_SIZE) ) goto manifest_syntax_error; |
| 666 | n = p->nCherrypick; |
| 667 | p->nCherrypick++; |
| 668 | p->aCherrypick = fossil_realloc(p->aCherrypick, |
| 669 | p->nCherrypick*sizeof(p->aCherrypick[0])); |
| 670 | p->aCherrypick[n].zCPTarget = zUuid; |
| 671 | p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz); |
| 672 | if( zUuid ){ |
| 673 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 674 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 675 | } |
| 676 | break; |
| 677 | } |
| 678 | |
| 679 | /* |
| @@ -681,14 +700,14 @@ | |
| 681 | ** |
| 682 | ** Specify the MD5 checksum over the name and content of all files |
| 683 | ** in the manifest. |
| 684 | */ |
| 685 | case 'R': { |
| 686 | if( p->zRepoCksum!=0 ) goto manifest_syntax_error; |
| 687 | p->zRepoCksum = next_token(&x, &sz); |
| 688 | if( sz!=32 ) goto manifest_syntax_error; |
| 689 | if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error; |
| 690 | break; |
| 691 | } |
| 692 | |
| 693 | /* |
| 694 | ** T (+|*|-)<tagname> <uuid> ?<value>? |
| @@ -706,29 +725,29 @@ | |
| 706 | ** Tags are not allowed in clusters. Multiple T lines are allowed. |
| 707 | */ |
| 708 | case 'T': { |
| 709 | char *zName, *zValue; |
| 710 | zName = next_token(&x, 0); |
| 711 | if( zName==0 ) goto manifest_syntax_error; |
| 712 | zUuid = next_token(&x, &sz); |
| 713 | if( zUuid==0 ) goto manifest_syntax_error; |
| 714 | zValue = next_token(&x, 0); |
| 715 | if( zValue ) defossilize(zValue); |
| 716 | if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){ |
| 717 | /* A valid uuid */ |
| 718 | }else if( sz==1 && zUuid[0]=='*' ){ |
| 719 | zUuid = 0; |
| 720 | }else{ |
| 721 | goto manifest_syntax_error; |
| 722 | } |
| 723 | defossilize(zName); |
| 724 | if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){ |
| 725 | goto manifest_syntax_error; |
| 726 | } |
| 727 | if( validate16(&zName[1], strlen(&zName[1])) ){ |
| 728 | /* Do not allow tags whose names look like UUIDs */ |
| 729 | goto manifest_syntax_error; |
| 730 | } |
| 731 | if( p->nTag>=p->nTagAlloc ){ |
| 732 | p->nTagAlloc = p->nTagAlloc*2 + 10; |
| 733 | p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) ); |
| 734 | } |
| @@ -735,11 +754,11 @@ | |
| 735 | i = p->nTag++; |
| 736 | p->aTag[i].zName = zName; |
| 737 | p->aTag[i].zUuid = zUuid; |
| 738 | p->aTag[i].zValue = zValue; |
| 739 | if( i>0 && fossil_strcmp(p->aTag[i-1].zName, zName)>=0 ){ |
| 740 | goto manifest_syntax_error; |
| 741 | } |
| 742 | break; |
| 743 | } |
| 744 | |
| 745 | /* |
| @@ -748,11 +767,11 @@ | |
| 748 | ** Identify the user who created this control file by their |
| 749 | ** login. Only one U line is allowed. Prohibited in clusters. |
| 750 | ** If the user name is omitted, take that to be "anonymous". |
| 751 | */ |
| 752 | case 'U': { |
| 753 | if( p->zUser!=0 ) goto manifest_syntax_error; |
| 754 | p->zUser = next_token(&x, 0); |
| 755 | if( p->zUser==0 ){ |
| 756 | p->zUser = "anonymous"; |
| 757 | }else{ |
| 758 | defossilize(p->zUser); |
| @@ -767,25 +786,26 @@ | |
| 767 | ** page. There is always an extra \n before the start of the next |
| 768 | ** record. |
| 769 | */ |
| 770 | case 'W': { |
| 771 | char *zSize; |
| 772 | int size, c; |
| 773 | Blob wiki; |
| 774 | zSize = next_token(&x, 0); |
| 775 | if( zSize==0 ) goto manifest_syntax_error; |
| 776 | if( x.atEol==0 ) goto manifest_syntax_error; |
| 777 | for(size=0; (c = zSize[0])>='0' && c<='9'; zSize++){ |
| 778 | size = size*10 + c - '0'; |
| 779 | } |
| 780 | if( size<0 ) goto manifest_syntax_error; |
| 781 | if( p->zWiki!=0 ) goto manifest_syntax_error; |
| 782 | blob_zero(&wiki); |
| 783 | if( (&x.z[size+1])>=x.zEnd ) goto manifest_syntax_error; |
| 784 | p->zWiki = x.z; |
| 785 | x.z += size; |
| 786 | if( x.z[0]!='\n' ) goto manifest_syntax_error; |
| 787 | x.z[0] = 0; |
| 788 | x.z++; |
| 789 | break; |
| 790 | } |
| 791 | |
| @@ -801,111 +821,117 @@ | |
| 801 | ** Manifest. It is not required for manifest only for historical |
| 802 | ** compatibility reasons. |
| 803 | */ |
| 804 | case 'Z': { |
| 805 | zUuid = next_token(&x, &sz); |
| 806 | if( sz!=32 ) goto manifest_syntax_error; |
| 807 | if( !validate16(zUuid, 32) ) goto manifest_syntax_error; |
| 808 | seenZ = 1; |
| 809 | break; |
| 810 | } |
| 811 | default: { |
| 812 | goto manifest_syntax_error; |
| 813 | } |
| 814 | } |
| 815 | } |
| 816 | if( x.z<x.zEnd ) goto manifest_syntax_error; |
| 817 | |
| 818 | if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){ |
| 819 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 820 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 821 | if( p->nField>0 ) goto manifest_syntax_error; |
| 822 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 823 | if( p->zWiki ) goto manifest_syntax_error; |
| 824 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 825 | if( p->zEventId ) goto manifest_syntax_error; |
| 826 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 827 | if( p->zAttachName ) goto manifest_syntax_error; |
| 828 | p->type = CFTYPE_MANIFEST; |
| 829 | }else if( p->nCChild>0 ){ |
| 830 | if( p->rDate>0.0 ) goto manifest_syntax_error; |
| 831 | if( p->zComment!=0 ) goto manifest_syntax_error; |
| 832 | if( p->zUser!=0 ) goto manifest_syntax_error; |
| 833 | if( p->nTag>0 ) goto manifest_syntax_error; |
| 834 | if( p->nParent>0 ) goto manifest_syntax_error; |
| 835 | if( p->nField>0 ) goto manifest_syntax_error; |
| 836 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 837 | if( p->zWiki ) goto manifest_syntax_error; |
| 838 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 839 | if( p->zEventId ) goto manifest_syntax_error; |
| 840 | if( p->zAttachName ) goto manifest_syntax_error; |
| 841 | if( !seenZ ) goto manifest_syntax_error; |
| 842 | p->type = CFTYPE_CLUSTER; |
| 843 | }else if( p->nField>0 ){ |
| 844 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 845 | if( p->zWiki ) goto manifest_syntax_error; |
| 846 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 847 | if( p->zEventId ) goto manifest_syntax_error; |
| 848 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 849 | if( p->nTag>0 ) goto manifest_syntax_error; |
| 850 | if( p->zTicketUuid==0 ) goto manifest_syntax_error; |
| 851 | if( p->zUser==0 ) goto manifest_syntax_error; |
| 852 | if( p->zAttachName ) goto manifest_syntax_error; |
| 853 | if( !seenZ ) goto manifest_syntax_error; |
| 854 | p->type = CFTYPE_TICKET; |
| 855 | }else if( p->zEventId ){ |
| 856 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 857 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 858 | if( p->zTicketUuid!=0 ) goto manifest_syntax_error; |
| 859 | if( p->zWikiTitle!=0 ) goto manifest_syntax_error; |
| 860 | if( p->zWiki==0 ) goto manifest_syntax_error; |
| 861 | if( p->zAttachName ) goto manifest_syntax_error; |
| 862 | for(i=0; i<p->nTag; i++){ |
| 863 | if( p->aTag[i].zName[0]!='+' ) goto manifest_syntax_error; |
| 864 | if( p->aTag[i].zUuid!=0 ) goto manifest_syntax_error; |
| 865 | } |
| 866 | if( !seenZ ) goto manifest_syntax_error; |
| 867 | p->type = CFTYPE_EVENT; |
| 868 | }else if( p->zWiki!=0 ){ |
| 869 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 870 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 871 | if( p->nTag>0 ) goto manifest_syntax_error; |
| 872 | if( p->zTicketUuid!=0 ) goto manifest_syntax_error; |
| 873 | if( p->zWikiTitle==0 ) goto manifest_syntax_error; |
| 874 | if( p->zAttachName ) goto manifest_syntax_error; |
| 875 | if( !seenZ ) goto manifest_syntax_error; |
| 876 | p->type = CFTYPE_WIKI; |
| 877 | }else if( p->nTag>0 ){ |
| 878 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 879 | if( p->nParent>0 ) goto manifest_syntax_error; |
| 880 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 881 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 882 | if( p->zAttachName ) goto manifest_syntax_error; |
| 883 | if( !seenZ ) goto manifest_syntax_error; |
| 884 | p->type = CFTYPE_CONTROL; |
| 885 | }else if( p->zAttachName ){ |
| 886 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 887 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 888 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 889 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 890 | if( !seenZ ) goto manifest_syntax_error; |
| 891 | p->type = CFTYPE_ATTACHMENT; |
| 892 | }else{ |
| 893 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 894 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 895 | if( p->nField>0 ) goto manifest_syntax_error; |
| 896 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 897 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 898 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 899 | p->type = CFTYPE_MANIFEST; |
| 900 | } |
| 901 | md5sum_init(); |
| 902 | if( !isRepeat ) g.parseCnt[p->type]++; |
| 903 | return p; |
| 904 | |
| 905 | manifest_syntax_error: |
| 906 | /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/ |
| 907 | md5sum_init(); |
| 908 | manifest_destroy(p); |
| 909 | return 0; |
| 910 | } |
| 911 | |
| @@ -924,11 +950,11 @@ | |
| 924 | p = 0; |
| 925 | } |
| 926 | return p; |
| 927 | } |
| 928 | content_get(rid, &content); |
| 929 | p = manifest_parse(&content, rid); |
| 930 | if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){ |
| 931 | manifest_destroy(p); |
| 932 | p = 0; |
| 933 | } |
| 934 | return p; |
| @@ -972,13 +998,16 @@ | |
| 972 | } |
| 973 | blob_read_from_file(&b, g.argv[2]); |
| 974 | if( g.argc>3 ) n = atoi(g.argv[3]); |
| 975 | for(i=0; i<n; i++){ |
| 976 | Blob b2; |
| 977 | blob_copy(&b2, &b); |
| 978 | p = manifest_parse(&b2, 0); |
| 979 | if( p==0 ) fossil_print("FAILED!\n"); |
| 980 | manifest_destroy(p); |
| 981 | } |
| 982 | } |
| 983 | |
| 984 | /* |
| @@ -1299,11 +1328,11 @@ | |
| 1299 | otherRid = cid; |
| 1300 | } |
| 1301 | if( (*ppOther = manifest_cache_find(otherRid))==0 ){ |
| 1302 | content_get(otherRid, &otherContent); |
| 1303 | if( blob_size(&otherContent)==0 ) return; |
| 1304 | *ppOther = manifest_parse(&otherContent, otherRid); |
| 1305 | if( *ppOther==0 ) return; |
| 1306 | } |
| 1307 | if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){ |
| 1308 | manifest_destroy(*ppOther); |
| 1309 | return; |
| @@ -1597,11 +1626,11 @@ | |
| 1597 | Stmt q; |
| 1598 | int parentid = 0; |
| 1599 | |
| 1600 | if( (p = manifest_cache_find(rid))!=0 ){ |
| 1601 | blob_reset(pContent); |
| 1602 | }else if( (p = manifest_parse(pContent, rid))==0 ){ |
| 1603 | assert( blob_is_reset(pContent) || pContent==0 ); |
| 1604 | return 0; |
| 1605 | } |
| 1606 | if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ |
| 1607 | manifest_destroy(p); |
| 1608 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -307,10 +307,15 @@ | |
| 307 | c = 0; |
| 308 | } |
| 309 | return c; |
| 310 | } |
| 311 | |
| 312 | /* |
| 313 | ** Shorthand for a control-artifact parsing error |
| 314 | */ |
| 315 | #define SYNTAX(T) {zErr=(T); goto manifest_syntax_error;} |
| 316 | |
| 317 | /* |
| 318 | ** Parse a blob into a Manifest object. The Manifest object |
| 319 | ** takes over the input blob and will free it when the |
| 320 | ** Manifest object is freed. Zeros are inserted into the blob |
| 321 | ** as string terminators so that blob should not be used again. |
| @@ -334,11 +339,11 @@ | |
| 339 | ** Each card is divided into tokens by a single space character. |
| 340 | ** The first token is a single upper-case letter which is the card type. |
| 341 | ** The card type determines the other parameters to the card. |
| 342 | ** Cards must occur in lexicographical order. |
| 343 | */ |
| 344 | Manifest *manifest_parse(Blob *pContent, int rid, Blob *pErr){ |
| 345 | Manifest *p; |
| 346 | int seenZ = 0; |
| 347 | int i, lineNo=0; |
| 348 | ManifestText x; |
| 349 | char cPrevType = 0; |
| @@ -347,10 +352,11 @@ | |
| 352 | int n; |
| 353 | char *zUuid; |
| 354 | int sz = 0; |
| 355 | int isRepeat; |
| 356 | static Bag seen; |
| 357 | const char *zErr = 0; |
| 358 | |
| 359 | if( rid==0 ){ |
| 360 | isRepeat = 1; |
| 361 | }else if( bag_find(&seen, rid) ){ |
| 362 | isRepeat = 1; |
| @@ -365,27 +371,30 @@ | |
| 371 | if( !isRepeat ) g.parseCnt[0]++; |
| 372 | z = blob_materialize(pContent); |
| 373 | n = blob_size(pContent); |
| 374 | if( n<=0 || z[n-1]!='\n' ){ |
| 375 | blob_reset(pContent); |
| 376 | blob_appendf(pErr, n ? "not terminated with \\n" : "zero-length"); |
| 377 | return 0; |
| 378 | } |
| 379 | |
| 380 | /* Strip off the PGP signature if there is one. Then verify the |
| 381 | ** Z-card. |
| 382 | */ |
| 383 | remove_pgp_signature(&z, &n); |
| 384 | if( verify_z_card(z, n)==2 ){ |
| 385 | blob_reset(pContent); |
| 386 | blob_appendf(pErr, "incorrect Z-card cksum"); |
| 387 | return 0; |
| 388 | } |
| 389 | |
| 390 | /* Verify that the first few characters of the artifact look like |
| 391 | ** a control artifact. |
| 392 | */ |
| 393 | if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){ |
| 394 | blob_reset(pContent); |
| 395 | blob_appendf(pErr, "line 1 not recognized"); |
| 396 | return 0; |
| 397 | } |
| 398 | |
| 399 | /* Allocate a Manifest object to hold the parsed control artifact. |
| 400 | */ |
| @@ -420,19 +429,19 @@ | |
| 429 | zSrc = next_token(&x, &nSrc); |
| 430 | if( zName==0 || zTarget==0 ) goto manifest_syntax_error; |
| 431 | if( p->zAttachName!=0 ) goto manifest_syntax_error; |
| 432 | defossilize(zName); |
| 433 | if( !file_is_simple_pathname(zName) ){ |
| 434 | SYNTAX("invalid filename on A-card"); |
| 435 | } |
| 436 | defossilize(zTarget); |
| 437 | if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE)) |
| 438 | && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| 439 | SYNTAX("invalid target on A-card"); |
| 440 | } |
| 441 | if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){ |
| 442 | SYNTAX("invalid source on A-card"); |
| 443 | } |
| 444 | p->zAttachName = (char*)file_tail(zName); |
| 445 | p->zAttachSrc = zSrc; |
| 446 | p->zAttachTarget = zTarget; |
| 447 | break; |
| @@ -442,15 +451,16 @@ | |
| 451 | ** B <uuid> |
| 452 | ** |
| 453 | ** A B-line gives the UUID for the baselinen of a delta-manifest. |
| 454 | */ |
| 455 | case 'B': { |
| 456 | if( p->zBaseline ) SYNTAX("more than one B-card"); |
| 457 | p->zBaseline = next_token(&x, &sz); |
| 458 | if( p->zBaseline==0 ) SYNTAX("missing UUID on B-card"); |
| 459 | if( sz!=UUID_SIZE || !validate16(p->zBaseline, UUID_SIZE) ){ |
| 460 | SYNTAX("invalid UUID on B-card"); |
| 461 | } |
| 462 | break; |
| 463 | } |
| 464 | |
| 465 | |
| 466 | /* |
| @@ -459,13 +469,13 @@ | |
| 469 | ** Comment text is fossil-encoded. There may be no more than |
| 470 | ** one C line. C lines are required for manifests and are |
| 471 | ** disallowed on all other control files. |
| 472 | */ |
| 473 | case 'C': { |
| 474 | if( p->zComment!=0 ) SYNTAX("more than one C-card"); |
| 475 | p->zComment = next_token(&x, 0); |
| 476 | if( p->zComment==0 ) SYNTAX("missing comment text on C-card"); |
| 477 | defossilize(p->zComment); |
| 478 | break; |
| 479 | } |
| 480 | |
| 481 | /* |
| @@ -474,13 +484,13 @@ | |
| 484 | ** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS |
| 485 | ** There can be no more than 1 D line. D lines are required |
| 486 | ** for all control files except for clusters. |
| 487 | */ |
| 488 | case 'D': { |
| 489 | if( p->rDate>0.0 ) SYNTAX("more than one D-card"); |
| 490 | p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0)); |
| 491 | if( p->rDate<=0.0 ) SYNTAX("cannot parse date on D-card"); |
| 492 | break; |
| 493 | } |
| 494 | |
| 495 | /* |
| 496 | ** E <timestamp> <uuid> |
| @@ -490,16 +500,17 @@ | |
| 500 | ** The event timestamp is distinct from the D timestamp. The D |
| 501 | ** timestamp is when the artifact was created whereas the E timestamp |
| 502 | ** is when the specific event is said to occur. |
| 503 | */ |
| 504 | case 'E': { |
| 505 | if( p->rEventDate>0.0 ) SYNTAX("more than one E-card"); |
| 506 | p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); |
| 507 | if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card"); |
| 508 | p->zEventId = next_token(&x, &sz); |
| 509 | if( sz!=UUID_SIZE || !validate16(p->zEventId, UUID_SIZE) ){ |
| 510 | SYNTAX("malformed UUID on E-card"); |
| 511 | } |
| 512 | break; |
| 513 | } |
| 514 | |
| 515 | /* |
| 516 | ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>? |
| @@ -509,26 +520,26 @@ | |
| 520 | ** other control file. The filename and old-name are fossil-encoded. |
| 521 | */ |
| 522 | case 'F': { |
| 523 | char *zName, *zPerm, *zPriorName; |
| 524 | zName = next_token(&x,0); |
| 525 | if( zName==0 ) SYNTAX("missing filename on F-card"); |
| 526 | defossilize(zName); |
| 527 | if( !file_is_simple_pathname(zName) ){ |
| 528 | SYNTAX("F-card filename is not a simple path"); |
| 529 | } |
| 530 | zUuid = next_token(&x, &sz); |
| 531 | if( p->zBaseline==0 || zUuid!=0 ){ |
| 532 | if( sz!=UUID_SIZE ) SYNTAX("F-card UUID is the wrong size"); |
| 533 | if( !validate16(zUuid, UUID_SIZE) ) SYNTAX("F-card UUID invalid"); |
| 534 | } |
| 535 | zPerm = next_token(&x,0); |
| 536 | zPriorName = next_token(&x,0); |
| 537 | if( zPriorName ){ |
| 538 | defossilize(zPriorName); |
| 539 | if( !file_is_simple_pathname(zPriorName) ){ |
| 540 | SYNTAX("F-card old filename is not a simple path"); |
| 541 | } |
| 542 | } |
| 543 | if( p->nFile>=p->nFileAlloc ){ |
| 544 | p->nFileAlloc = p->nFileAlloc*2 + 10; |
| 545 | p->aFile = fossil_realloc(p->aFile, |
| @@ -538,11 +549,11 @@ | |
| 549 | p->aFile[i].zName = zName; |
| 550 | p->aFile[i].zUuid = zUuid; |
| 551 | p->aFile[i].zPerm = zPerm; |
| 552 | p->aFile[i].zPrior = zPriorName; |
| 553 | if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){ |
| 554 | SYNTAX("incorrect F-card sort order"); |
| 555 | } |
| 556 | break; |
| 557 | } |
| 558 | |
| 559 | /* |
| @@ -555,11 +566,11 @@ | |
| 566 | */ |
| 567 | case 'J': { |
| 568 | char *zName, *zValue; |
| 569 | zName = next_token(&x,0); |
| 570 | zValue = next_token(&x,0); |
| 571 | if( zName==0 ) SYNTAX("name missing from J-card"); |
| 572 | if( zValue==0 ) zValue = ""; |
| 573 | defossilize(zValue); |
| 574 | if( p->nField>=p->nFieldAlloc ){ |
| 575 | p->nFieldAlloc = p->nFieldAlloc*2 + 10; |
| 576 | p->aField = fossil_realloc(p->aField, |
| @@ -567,11 +578,11 @@ | |
| 578 | } |
| 579 | i = p->nField++; |
| 580 | p->aField[i].zName = zName; |
| 581 | p->aField[i].zValue = zValue; |
| 582 | if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){ |
| 583 | SYNTAX("incorrect J-card sort order"); |
| 584 | } |
| 585 | break; |
| 586 | } |
| 587 | |
| 588 | |
| @@ -580,14 +591,16 @@ | |
| 591 | ** |
| 592 | ** A K-line gives the UUID for the ticket which this control file |
| 593 | ** is amending. |
| 594 | */ |
| 595 | case 'K': { |
| 596 | if( p->zTicketUuid!=0 ) SYNTAX("more than one K-card"); |
| 597 | p->zTicketUuid = next_token(&x, &sz); |
| 598 | if( sz!=UUID_SIZE ) SYNTAX("K-card UUID is the wrong size"); |
| 599 | if( !validate16(p->zTicketUuid, UUID_SIZE) ){ |
| 600 | SYNTAX("invalid K-card UUID"); |
| 601 | } |
| 602 | break; |
| 603 | } |
| 604 | |
| 605 | /* |
| 606 | ** L <wikititle> |
| @@ -594,16 +607,16 @@ | |
| 607 | ** |
| 608 | ** The wiki page title is fossil-encoded. There may be no more than |
| 609 | ** one L line. |
| 610 | */ |
| 611 | case 'L': { |
| 612 | if( p->zWikiTitle!=0 ) SYNTAX("more than one L-card"); |
| 613 | p->zWikiTitle = next_token(&x,0); |
| 614 | if( p->zWikiTitle==0 ) SYNTAX("missing title on L-card"); |
| 615 | defossilize(p->zWikiTitle); |
| 616 | if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){ |
| 617 | SYNTAX("L-card has malformed wiki name"); |
| 618 | } |
| 619 | break; |
| 620 | } |
| 621 | |
| 622 | /* |
| @@ -612,22 +625,22 @@ | |
| 625 | ** An M-line identifies another artifact by its UUID. M-lines |
| 626 | ** occur in clusters only. |
| 627 | */ |
| 628 | case 'M': { |
| 629 | zUuid = next_token(&x, &sz); |
| 630 | if( zUuid==0 ) SYNTAX("missing UUID on M-card"); |
| 631 | if( sz!=UUID_SIZE ) SYNTAX("wrong size for UUID on M-card"); |
| 632 | if( !validate16(zUuid, UUID_SIZE) ) SYNTAX("UUID invalid on M-card"); |
| 633 | if( p->nCChild>=p->nCChildAlloc ){ |
| 634 | p->nCChildAlloc = p->nCChildAlloc*2 + 10; |
| 635 | p->azCChild = fossil_realloc(p->azCChild |
| 636 | , p->nCChildAlloc*sizeof(p->azCChild[0]) ); |
| 637 | } |
| 638 | i = p->nCChild++; |
| 639 | p->azCChild[i] = zUuid; |
| 640 | if( i>0 && fossil_strcmp(p->azCChild[i-1], zUuid)>=0 ){ |
| 641 | SYNTAX("M-card in the wrong order"); |
| 642 | } |
| 643 | break; |
| 644 | } |
| 645 | |
| 646 | /* |
| @@ -637,12 +650,12 @@ | |
| 650 | ** this artifact. The first parent is the primary parent. All |
| 651 | ** others are parents by merge. |
| 652 | */ |
| 653 | case 'P': { |
| 654 | while( (zUuid = next_token(&x, &sz))!=0 ){ |
| 655 | if( sz!=UUID_SIZE ) SYNTAX("wrong size UUID on P-card"); |
| 656 | if( !validate16(zUuid, UUID_SIZE) )SYNTAX("invalid UUID on P-card"); |
| 657 | if( p->nParent>=p->nParentAlloc ){ |
| 658 | p->nParentAlloc = p->nParentAlloc*2 + 5; |
| 659 | p->azParent = fossil_realloc(p->azParent, |
| 660 | p->nParentAlloc*sizeof(char*)); |
| 661 | } |
| @@ -657,23 +670,29 @@ | |
| 670 | ** |
| 671 | ** Specify one or a range of checkins that are cherrypicked into |
| 672 | ** this checkin ("+") or backed out of this checkin ("-"). |
| 673 | */ |
| 674 | case 'Q': { |
| 675 | if( (zUuid=next_token(&x, &sz))==0 ) SYNTAX("missing UUID on Q-card"); |
| 676 | if( sz!=UUID_SIZE+1 ) SYNTAX("wrong size UUID on Q-card"); |
| 677 | if( zUuid[0]!='+' && zUuid[0]!='-' ){ |
| 678 | SYNTAX("Q-card does not begin with '+' or '-'"); |
| 679 | } |
| 680 | if( !validate16(&zUuid[1], UUID_SIZE) ){ |
| 681 | SYNTAX("invalid UUID on Q-card"); |
| 682 | } |
| 683 | n = p->nCherrypick; |
| 684 | p->nCherrypick++; |
| 685 | p->aCherrypick = fossil_realloc(p->aCherrypick, |
| 686 | p->nCherrypick*sizeof(p->aCherrypick[0])); |
| 687 | p->aCherrypick[n].zCPTarget = zUuid; |
| 688 | p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz); |
| 689 | if( zUuid ){ |
| 690 | if( sz!=UUID_SIZE ) SYNTAX("wrong size second UUID in Q-card"); |
| 691 | if( !validate16(zUuid, UUID_SIZE) ){ |
| 692 | SYNTAX("invalid second UUID on Q-card"); |
| 693 | } |
| 694 | } |
| 695 | break; |
| 696 | } |
| 697 | |
| 698 | /* |
| @@ -681,14 +700,14 @@ | |
| 700 | ** |
| 701 | ** Specify the MD5 checksum over the name and content of all files |
| 702 | ** in the manifest. |
| 703 | */ |
| 704 | case 'R': { |
| 705 | if( p->zRepoCksum!=0 ) SYNTAX("more than on R-card"); |
| 706 | p->zRepoCksum = next_token(&x, &sz); |
| 707 | if( sz!=32 ) SYNTAX("wrong size cksum on R-card"); |
| 708 | if( !validate16(p->zRepoCksum, 32) ) SYNTAX("malformed R-card cksum"); |
| 709 | break; |
| 710 | } |
| 711 | |
| 712 | /* |
| 713 | ** T (+|*|-)<tagname> <uuid> ?<value>? |
| @@ -706,29 +725,29 @@ | |
| 725 | ** Tags are not allowed in clusters. Multiple T lines are allowed. |
| 726 | */ |
| 727 | case 'T': { |
| 728 | char *zName, *zValue; |
| 729 | zName = next_token(&x, 0); |
| 730 | if( zName==0 ) SYNTAX("missing name on T-card"); |
| 731 | zUuid = next_token(&x, &sz); |
| 732 | if( zUuid==0 ) SYNTAX("missing UUID on T-card"); |
| 733 | zValue = next_token(&x, 0); |
| 734 | if( zValue ) defossilize(zValue); |
| 735 | if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){ |
| 736 | /* A valid uuid */ |
| 737 | }else if( sz==1 && zUuid[0]=='*' ){ |
| 738 | zUuid = 0; |
| 739 | }else{ |
| 740 | SYNTAX("malformed UUID on T-card"); |
| 741 | } |
| 742 | defossilize(zName); |
| 743 | if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){ |
| 744 | SYNTAX("T-card name does not begin with '-', '+', or '*'"); |
| 745 | } |
| 746 | if( validate16(&zName[1], strlen(&zName[1])) ){ |
| 747 | /* Do not allow tags whose names look like UUIDs */ |
| 748 | SYNTAX("T-card name looks like a UUID"); |
| 749 | } |
| 750 | if( p->nTag>=p->nTagAlloc ){ |
| 751 | p->nTagAlloc = p->nTagAlloc*2 + 10; |
| 752 | p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) ); |
| 753 | } |
| @@ -735,11 +754,11 @@ | |
| 754 | i = p->nTag++; |
| 755 | p->aTag[i].zName = zName; |
| 756 | p->aTag[i].zUuid = zUuid; |
| 757 | p->aTag[i].zValue = zValue; |
| 758 | if( i>0 && fossil_strcmp(p->aTag[i-1].zName, zName)>=0 ){ |
| 759 | SYNTAX("T-card in the wrong order"); |
| 760 | } |
| 761 | break; |
| 762 | } |
| 763 | |
| 764 | /* |
| @@ -748,11 +767,11 @@ | |
| 767 | ** Identify the user who created this control file by their |
| 768 | ** login. Only one U line is allowed. Prohibited in clusters. |
| 769 | ** If the user name is omitted, take that to be "anonymous". |
| 770 | */ |
| 771 | case 'U': { |
| 772 | if( p->zUser!=0 ) SYNTAX("more than on U-card"); |
| 773 | p->zUser = next_token(&x, 0); |
| 774 | if( p->zUser==0 ){ |
| 775 | p->zUser = "anonymous"; |
| 776 | }else{ |
| 777 | defossilize(p->zUser); |
| @@ -767,25 +786,26 @@ | |
| 786 | ** page. There is always an extra \n before the start of the next |
| 787 | ** record. |
| 788 | */ |
| 789 | case 'W': { |
| 790 | char *zSize; |
| 791 | unsigned size, oldsize, c; |
| 792 | Blob wiki; |
| 793 | zSize = next_token(&x, 0); |
| 794 | if( zSize==0 ) SYNTAX("missing size on W-card"); |
| 795 | if( x.atEol==0 ) SYNTAX("no content after W-card"); |
| 796 | for(oldsize=size=0; (c = zSize[0])>='0' && c<='9'; zSize++){ |
| 797 | size = oldsize*10 + c - '0'; |
| 798 | if( size<oldsize ) SYNTAX("size overflow on W-card"); |
| 799 | oldsize = size; |
| 800 | } |
| 801 | if( p->zWiki!=0 ) SYNTAX("more than one W-card"); |
| 802 | blob_zero(&wiki); |
| 803 | if( (&x.z[size+1])>=x.zEnd )SYNTAX("not enough content after W-card"); |
| 804 | p->zWiki = x.z; |
| 805 | x.z += size; |
| 806 | if( x.z[0]!='\n' ) SYNTAX("W-card content no \\n terminated"); |
| 807 | x.z[0] = 0; |
| 808 | x.z++; |
| 809 | break; |
| 810 | } |
| 811 | |
| @@ -801,111 +821,117 @@ | |
| 821 | ** Manifest. It is not required for manifest only for historical |
| 822 | ** compatibility reasons. |
| 823 | */ |
| 824 | case 'Z': { |
| 825 | zUuid = next_token(&x, &sz); |
| 826 | if( sz!=32 ) SYNTAX("wrong size for Z-card cksum"); |
| 827 | if( !validate16(zUuid, 32) ) SYNTAX("malformed Z-card cksum"); |
| 828 | seenZ = 1; |
| 829 | break; |
| 830 | } |
| 831 | default: { |
| 832 | SYNTAX("unrecognized card"); |
| 833 | } |
| 834 | } |
| 835 | } |
| 836 | if( x.z<x.zEnd ) SYNTAX("card in the wrong order"); |
| 837 | |
| 838 | if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){ |
| 839 | if( p->nCChild>0 ) SYNTAX("M-card in check-in"); |
| 840 | if( p->rDate<=0.0 ) SYNTAX("missing date for check-in"); |
| 841 | if( p->nField>0 ) SYNTAX("J-card in check-in"); |
| 842 | if( p->zTicketUuid ) SYNTAX("K-card in check-in"); |
| 843 | if( p->zWiki ) SYNTAX("W-card in check-in"); |
| 844 | if( p->zWikiTitle ) SYNTAX("L-card in check-in"); |
| 845 | if( p->zEventId ) SYNTAX("E-card in check-in"); |
| 846 | if( p->zTicketUuid ) SYNTAX("K-card in check-in"); |
| 847 | if( p->zAttachName ) SYNTAX("A-card in check-in"); |
| 848 | p->type = CFTYPE_MANIFEST; |
| 849 | }else if( p->nCChild>0 ){ |
| 850 | if( p->rDate>0.0 |
| 851 | || p->zComment!=0 |
| 852 | || p->zUser!=0 |
| 853 | || p->nTag>0 |
| 854 | || p->nParent>0 |
| 855 | || p->nField>0 |
| 856 | || p->zTicketUuid |
| 857 | || p->zWiki |
| 858 | || p->zWikiTitle |
| 859 | || p->zEventId |
| 860 | || p->zAttachName |
| 861 | ){ |
| 862 | SYNTAX("cluster contains a card other than M- or Z-"); |
| 863 | } |
| 864 | if( !seenZ ) SYNTAX("missing Z-card on cluster"); |
| 865 | p->type = CFTYPE_CLUSTER; |
| 866 | }else if( p->nField>0 ){ |
| 867 | if( p->rDate<=0.0 ) SYNTAX("missing date for ticket"); |
| 868 | if( p->zWiki ) SYNTAX("W-card in ticket"); |
| 869 | if( p->zWikiTitle ) SYNTAX("L-card in ticket"); |
| 870 | if( p->zEventId ) SYNTAX("E-card in ticket"); |
| 871 | if( p->nCChild>0 ) SYNTAX("M-card in ticket"); |
| 872 | if( p->nTag>0 ) SYNTAX("T-card in ticket"); |
| 873 | if( p->zTicketUuid==0 ) SYNTAX("missing K-card in ticket"); |
| 874 | if( p->zUser==0 ) SYNTAX("missing U-card in ticket"); |
| 875 | if( p->zAttachName ) SYNTAX("A-card in ticket"); |
| 876 | if( !seenZ ) SYNTAX("missing Z-card in ticket"); |
| 877 | p->type = CFTYPE_TICKET; |
| 878 | }else if( p->zEventId ){ |
| 879 | if( p->rDate<=0.0 ) SYNTAX("missing date for event"); |
| 880 | if( p->nCChild>0 ) SYNTAX("M-card in event"); |
| 881 | if( p->zTicketUuid!=0 ) SYNTAX("K-card in event"); |
| 882 | if( p->zWikiTitle!=0 ) SYNTAX("L-card in event"); |
| 883 | if( p->zWiki==0 ) SYNTAX("W-card in event"); |
| 884 | if( p->zAttachName ) SYNTAX("A-card in event"); |
| 885 | for(i=0; i<p->nTag; i++){ |
| 886 | if( p->aTag[i].zName[0]!='+' ) SYNTAX("propagating tag in event"); |
| 887 | if( p->aTag[i].zUuid!=0 ) SYNTAX("non-self-referential tag in event"); |
| 888 | } |
| 889 | if( !seenZ ) SYNTAX("Z-card missing in event"); |
| 890 | p->type = CFTYPE_EVENT; |
| 891 | }else if( p->zWiki!=0 ){ |
| 892 | if( p->rDate<=0.0 ) SYNTAX("date missing on wiki"); |
| 893 | if( p->nCChild>0 ) SYNTAX("M-card in wiki"); |
| 894 | if( p->nTag>0 ) SYNTAX("T-card in wiki"); |
| 895 | if( p->zTicketUuid!=0 ) SYNTAX("K-card in wiki"); |
| 896 | if( p->zWikiTitle==0 ) SYNTAX("L-card in wiki"); |
| 897 | if( p->zAttachName ) SYNTAX("A-card in wiki"); |
| 898 | if( !seenZ ) SYNTAX("missing Z-card on wiki"); |
| 899 | p->type = CFTYPE_WIKI; |
| 900 | }else if( p->nTag>0 ){ |
| 901 | if( p->rDate<=0.0 ) SYNTAX("date missing on tag"); |
| 902 | if( p->nParent>0 ) SYNTAX("P-card on tag"); |
| 903 | if( p->zWikiTitle ) SYNTAX("L-card on tag"); |
| 904 | if( p->zTicketUuid ) SYNTAX("K-card in tag"); |
| 905 | if( p->zAttachName ) SYNTAX("A-card in tag"); |
| 906 | if( !seenZ ) SYNTAX("missing Z-card on tag"); |
| 907 | p->type = CFTYPE_CONTROL; |
| 908 | }else if( p->zAttachName ){ |
| 909 | if( p->nCChild>0 ) SYNTAX("M-card in attachment"); |
| 910 | if( p->rDate<=0.0 ) SYNTAX("missing date in attachment"); |
| 911 | if( p->zTicketUuid ) SYNTAX("K-card in attachment"); |
| 912 | if( p->zWikiTitle ) SYNTAX("L-card in attachment"); |
| 913 | if( !seenZ ) SYNTAX("missing Z-card on attachment"); |
| 914 | p->type = CFTYPE_ATTACHMENT; |
| 915 | }else{ |
| 916 | if( p->nCChild>0 ) SYNTAX("M-card in check-in"); |
| 917 | if( p->rDate<=0.0 ) SYNTAX("missing date in check-in"); |
| 918 | if( p->nField>0 ) SYNTAX("J-card in check-in"); |
| 919 | if( p->zTicketUuid ) SYNTAX("K-card in check-in"); |
| 920 | if( p->zWikiTitle ) SYNTAX("L-card in check-in"); |
| 921 | p->type = CFTYPE_MANIFEST; |
| 922 | } |
| 923 | md5sum_init(); |
| 924 | if( !isRepeat ) g.parseCnt[p->type]++; |
| 925 | return p; |
| 926 | |
| 927 | manifest_syntax_error: |
| 928 | if( zErr ){ |
| 929 | blob_appendf(pErr, "line %d: %s", lineNo, zErr); |
| 930 | }else{ |
| 931 | blob_appendf(pErr, "unknown error on line %d", lineNo); |
| 932 | } |
| 933 | md5sum_init(); |
| 934 | manifest_destroy(p); |
| 935 | return 0; |
| 936 | } |
| 937 | |
| @@ -924,11 +950,11 @@ | |
| 950 | p = 0; |
| 951 | } |
| 952 | return p; |
| 953 | } |
| 954 | content_get(rid, &content); |
| 955 | p = manifest_parse(&content, rid, 0); |
| 956 | if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){ |
| 957 | manifest_destroy(p); |
| 958 | p = 0; |
| 959 | } |
| 960 | return p; |
| @@ -972,13 +998,16 @@ | |
| 998 | } |
| 999 | blob_read_from_file(&b, g.argv[2]); |
| 1000 | if( g.argc>3 ) n = atoi(g.argv[3]); |
| 1001 | for(i=0; i<n; i++){ |
| 1002 | Blob b2; |
| 1003 | Blob err; |
| 1004 | blob_copy(&b2, &b); |
| 1005 | blob_zero(&err); |
| 1006 | p = manifest_parse(&b2, 0, &err); |
| 1007 | if( p==0 ) fossil_print("ERROR: %s\n", blob_str(&err)); |
| 1008 | blob_reset(&err); |
| 1009 | manifest_destroy(p); |
| 1010 | } |
| 1011 | } |
| 1012 | |
| 1013 | /* |
| @@ -1299,11 +1328,11 @@ | |
| 1328 | otherRid = cid; |
| 1329 | } |
| 1330 | if( (*ppOther = manifest_cache_find(otherRid))==0 ){ |
| 1331 | content_get(otherRid, &otherContent); |
| 1332 | if( blob_size(&otherContent)==0 ) return; |
| 1333 | *ppOther = manifest_parse(&otherContent, otherRid, 0); |
| 1334 | if( *ppOther==0 ) return; |
| 1335 | } |
| 1336 | if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){ |
| 1337 | manifest_destroy(*ppOther); |
| 1338 | return; |
| @@ -1597,11 +1626,11 @@ | |
| 1626 | Stmt q; |
| 1627 | int parentid = 0; |
| 1628 | |
| 1629 | if( (p = manifest_cache_find(rid))!=0 ){ |
| 1630 | blob_reset(pContent); |
| 1631 | }else if( (p = manifest_parse(pContent, rid, 0))==0 ){ |
| 1632 | assert( blob_is_reset(pContent) || pContent==0 ); |
| 1633 | return 0; |
| 1634 | } |
| 1635 | if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ |
| 1636 | manifest_destroy(p); |
| 1637 |
+75
-127
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -215,18 +215,22 @@ | ||
| 215 | 215 | @ <td><i>Check-In:</i> Commit new versions in the repository</td></tr> |
| 216 | 216 | @ <tr><td valign="top"><b>j</b></td> |
| 217 | 217 | @ <td><i>Read-Wiki:</i> View wiki pages</td></tr> |
| 218 | 218 | @ <tr><td valign="top"><b>k</b></td> |
| 219 | 219 | @ <td><i>Write-Wiki:</i> Edit wiki pages</td></tr> |
| 220 | + @ <tr><td valign="top"><b>l</b></td> | |
| 221 | + @ <td><i>Mod-Wiki:</i> Moderator for wiki pages</td></tr> | |
| 220 | 222 | @ <tr><td valign="top"><b>m</b></td> |
| 221 | 223 | @ <td><i>Append-Wiki:</i> Append to wiki pages</td></tr> |
| 222 | 224 | @ <tr><td valign="top"><b>n</b></td> |
| 223 | 225 | @ <td><i>New-Tkt:</i> Create new tickets</td></tr> |
| 224 | 226 | @ <tr><td valign="top"><b>o</b></td> |
| 225 | 227 | @ <td><i>Check-Out:</i> Check out versions</td></tr> |
| 226 | 228 | @ <tr><td valign="top"><b>p</b></td> |
| 227 | 229 | @ <td><i>Password:</i> Change your own password</td></tr> |
| 230 | + @ <tr><td valign="top"><b>q</b></td> | |
| 231 | + @ <td><i>Mod-Tkt:</i> Moderator for tickets</td></tr> | |
| 228 | 232 | @ <tr><td valign="top"><b>r</b></td> |
| 229 | 233 | @ <td><i>Read-Tkt:</i> View tickets</td></tr> |
| 230 | 234 | @ <tr><td valign="top"><b>s</b></td> |
| 231 | 235 | @ <td><i>Setup/Super-user:</i> Setup and configure this website</td></tr> |
| 232 | 236 | @ <tr><td valign="top"><b>t</b></td> |
| @@ -292,20 +296,19 @@ | ||
| 292 | 296 | /* |
| 293 | 297 | ** WEBPAGE: /setup_uedit |
| 294 | 298 | */ |
| 295 | 299 | void user_edit(void){ |
| 296 | 300 | const char *zId, *zLogin, *zInfo, *zCap, *zPw; |
| 297 | - char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap; | |
| 298 | - char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae; | |
| 299 | - char *oat, *oau, *oav, *oab, *oax, *oaz; | |
| 300 | 301 | const char *zGroup; |
| 301 | 302 | const char *zOldLogin; |
| 302 | - char *inherit[128]; | |
| 303 | 303 | int doWrite; |
| 304 | - int uid; | |
| 304 | + int uid, i; | |
| 305 | 305 | int higherUser = 0; /* True if user being edited is SETUP and the */ |
| 306 | 306 | /* user doing the editing is ADMIN. Disallow editing */ |
| 307 | + char *inherit[128]; | |
| 308 | + int a[128]; | |
| 309 | + char *oa[128]; | |
| 307 | 310 | |
| 308 | 311 | /* Must have ADMIN privleges to access this page |
| 309 | 312 | */ |
| 310 | 313 | login_check_credentials(); |
| 311 | 314 | if( !g.perm.Admin ){ login_needed(); return; } |
| @@ -330,58 +333,19 @@ | ||
| 330 | 333 | ** modified user record. After writing the user record, redirect |
| 331 | 334 | ** to the page that displays a list of users. |
| 332 | 335 | */ |
| 333 | 336 | doWrite = cgi_all("login","info","pw") && !higherUser; |
| 334 | 337 | if( doWrite ){ |
| 335 | - char zCap[50]; | |
| 336 | - int i = 0; | |
| 337 | - int aa = P("aa")!=0; | |
| 338 | - int ab = P("ab")!=0; | |
| 339 | - int ad = P("ad")!=0; | |
| 340 | - int ae = P("ae")!=0; | |
| 341 | - int ai = P("ai")!=0; | |
| 342 | - int aj = P("aj")!=0; | |
| 343 | - int ak = P("ak")!=0; | |
| 344 | - int an = P("an")!=0; | |
| 345 | - int ao = P("ao")!=0; | |
| 346 | - int ap = P("ap")!=0; | |
| 347 | - int ar = P("ar")!=0; | |
| 348 | - int as = g.perm.Setup && P("as")!=0; | |
| 349 | - int aw = P("aw")!=0; | |
| 350 | - int ac = P("ac")!=0; | |
| 351 | - int af = P("af")!=0; | |
| 352 | - int am = P("am")!=0; | |
| 353 | - int ah = P("ah")!=0; | |
| 354 | - int ag = P("ag")!=0; | |
| 355 | - int at = P("at")!=0; | |
| 356 | - int au = P("au")!=0; | |
| 357 | - int av = P("av")!=0; | |
| 358 | - int ax = P("ax")!=0; | |
| 359 | - int az = P("az")!=0; | |
| 360 | - if( aa ){ zCap[i++] = 'a'; } | |
| 361 | - if( ab ){ zCap[i++] = 'b'; } | |
| 362 | - if( ac ){ zCap[i++] = 'c'; } | |
| 363 | - if( ad ){ zCap[i++] = 'd'; } | |
| 364 | - if( ae ){ zCap[i++] = 'e'; } | |
| 365 | - if( af ){ zCap[i++] = 'f'; } | |
| 366 | - if( ah ){ zCap[i++] = 'h'; } | |
| 367 | - if( ag ){ zCap[i++] = 'g'; } | |
| 368 | - if( ai ){ zCap[i++] = 'i'; } | |
| 369 | - if( aj ){ zCap[i++] = 'j'; } | |
| 370 | - if( ak ){ zCap[i++] = 'k'; } | |
| 371 | - if( am ){ zCap[i++] = 'm'; } | |
| 372 | - if( an ){ zCap[i++] = 'n'; } | |
| 373 | - if( ao ){ zCap[i++] = 'o'; } | |
| 374 | - if( ap ){ zCap[i++] = 'p'; } | |
| 375 | - if( ar ){ zCap[i++] = 'r'; } | |
| 376 | - if( as ){ zCap[i++] = 's'; } | |
| 377 | - if( at ){ zCap[i++] = 't'; } | |
| 378 | - if( au ){ zCap[i++] = 'u'; } | |
| 379 | - if( av ){ zCap[i++] = 'v'; } | |
| 380 | - if( aw ){ zCap[i++] = 'w'; } | |
| 381 | - if( ax ){ zCap[i++] = 'x'; } | |
| 382 | - if( az ){ zCap[i++] = 'z'; } | |
| 338 | + char c; | |
| 339 | + char zCap[50], zNm[4]; | |
| 340 | + zNm[0] = 'a'; | |
| 341 | + zNm[2] = 0; | |
| 342 | + for(i=0, c='a'; c<='z'; c++){ | |
| 343 | + zNm[1] = c; | |
| 344 | + a[c] = (c!='s' || g.perm.Setup) && P(zNm)!=0; | |
| 345 | + if( a[c] ) zCap[i++] = c; | |
| 346 | + } | |
| 383 | 347 | |
| 384 | 348 | zCap[i] = 0; |
| 385 | 349 | zPw = P("pw"); |
| 386 | 350 | zLogin = P("login"); |
| 387 | 351 | if( strlen(zLogin)==0 ){ |
| @@ -457,40 +421,20 @@ | ||
| 457 | 421 | */ |
| 458 | 422 | zLogin = ""; |
| 459 | 423 | zInfo = ""; |
| 460 | 424 | zCap = ""; |
| 461 | 425 | zPw = ""; |
| 462 | - oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam = | |
| 463 | - oan = oao = oap = oar = oas = oat = oau = oav = oaw = oax = oaz = ""; | |
| 426 | + for(i='a'; i<='z'; i++) oa[i] = ""; | |
| 464 | 427 | if( uid ){ |
| 465 | 428 | zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); |
| 466 | 429 | zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); |
| 467 | 430 | zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); |
| 468 | 431 | zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); |
| 469 | - if( strchr(zCap, 'a') ) oaa = " checked=\"checked\""; | |
| 470 | - if( strchr(zCap, 'b') ) oab = " checked=\"checked\""; | |
| 471 | - if( strchr(zCap, 'c') ) oac = " checked=\"checked\""; | |
| 472 | - if( strchr(zCap, 'd') ) oad = " checked=\"checked\""; | |
| 473 | - if( strchr(zCap, 'e') ) oae = " checked=\"checked\""; | |
| 474 | - if( strchr(zCap, 'f') ) oaf = " checked=\"checked\""; | |
| 475 | - if( strchr(zCap, 'g') ) oag = " checked=\"checked\""; | |
| 476 | - if( strchr(zCap, 'h') ) oah = " checked=\"checked\""; | |
| 477 | - if( strchr(zCap, 'i') ) oai = " checked=\"checked\""; | |
| 478 | - if( strchr(zCap, 'j') ) oaj = " checked=\"checked\""; | |
| 479 | - if( strchr(zCap, 'k') ) oak = " checked=\"checked\""; | |
| 480 | - if( strchr(zCap, 'm') ) oam = " checked=\"checked\""; | |
| 481 | - if( strchr(zCap, 'n') ) oan = " checked=\"checked\""; | |
| 482 | - if( strchr(zCap, 'o') ) oao = " checked=\"checked\""; | |
| 483 | - if( strchr(zCap, 'p') ) oap = " checked=\"checked\""; | |
| 484 | - if( strchr(zCap, 'r') ) oar = " checked=\"checked\""; | |
| 485 | - if( strchr(zCap, 's') ) oas = " checked=\"checked\""; | |
| 486 | - if( strchr(zCap, 't') ) oat = " checked=\"checked\""; | |
| 487 | - if( strchr(zCap, 'u') ) oau = " checked=\"checked\""; | |
| 488 | - if( strchr(zCap, 'v') ) oav = " checked=\"checked\""; | |
| 489 | - if( strchr(zCap, 'w') ) oaw = " checked=\"checked\""; | |
| 490 | - if( strchr(zCap, 'x') ) oax = " checked=\"checked\""; | |
| 491 | - if( strchr(zCap, 'z') ) oaz = " checked=\"checked\""; | |
| 432 | + for(i=0; zCap[i]; i++){ | |
| 433 | + char c = zCap[i]; | |
| 434 | + if( c>='a' && c<='z' ) oa[c] = " checked=\"checked\""; | |
| 435 | + } | |
| 492 | 436 | } |
| 493 | 437 | |
| 494 | 438 | /* figure out inherited permissions */ |
| 495 | 439 | memset(inherit, 0, sizeof(inherit)); |
| 496 | 440 | if( fossil_strcmp(zLogin, "developer") ){ |
| @@ -562,59 +506,63 @@ | ||
| 562 | 506 | @ <td class="usetupEditLabel">Capabilities:</td> |
| 563 | 507 | @ <td> |
| 564 | 508 | #define B(x) inherit[x] |
| 565 | 509 | @ <table border=0><tr><td valign="top"> |
| 566 | 510 | if( g.perm.Setup ){ |
| 567 | - @ <label><input type="checkbox" name="as"%s(oas) />%s(B('s'))Setup | |
| 568 | - @ </label><br /> | |
| 569 | - } | |
| 570 | - @ <label><input type="checkbox" name="aa"%s(oaa) />%s(B('a'))Admin | |
| 571 | - @ </label><br /> | |
| 572 | - @ <label><input type="checkbox" name="ad"%s(oad) />%s(B('d'))Delete | |
| 573 | - @ </label><br /> | |
| 574 | - @ <label><input type="checkbox" name="ae"%s(oae) />%s(B('e'))Email | |
| 575 | - @ </label><br /> | |
| 576 | - @ <label><input type="checkbox" name="ap"%s(oap) />%s(B('p'))Password | |
| 577 | - @ </label><br /> | |
| 578 | - @ <label><input type="checkbox" name="ai"%s(oai) />%s(B('i'))Check-In | |
| 579 | - @ </label><br /> | |
| 580 | - @ <label><input type="checkbox" name="ao"%s(oao) />%s(B('o'))Check-Out | |
| 581 | - @ </label><br /> | |
| 582 | - @ <label><input type="checkbox" name="ah"%s(oah) />%s(B('h'))Hyperlinks | |
| 583 | - @ </label><br /> | |
| 584 | - @ </td><td><td width="40"></td><td valign="top"> | |
| 585 | - @ <label><input type="checkbox" name="au"%s(oau) />%s(B('u'))Reader | |
| 586 | - @ </label><br /> | |
| 587 | - @ <label><input type="checkbox" name="av"%s(oav) />%s(B('v'))Developer | |
| 588 | - @ </label><br /> | |
| 589 | - @ <label><input type="checkbox" name="ag"%s(oag) />%s(B('g'))Clone | |
| 590 | - @ </label><br /> | |
| 591 | - @ <label><input type="checkbox" name="aj"%s(oaj) />%s(B('j'))Read Wiki | |
| 592 | - @ </label><br /> | |
| 593 | - @ <label><input type="checkbox" name="af"%s(oaf) />%s(B('f'))New Wiki | |
| 594 | - @ </label><br /> | |
| 595 | - @ <label><input type="checkbox" name="am"%s(oam) />%s(B('m'))Append Wiki | |
| 596 | - @ </label><br /> | |
| 597 | - @ <label><input type="checkbox" name="ak"%s(oak) />%s(B('k'))Write Wiki | |
| 598 | - @ </label><br /> | |
| 599 | - @ <label><input type="checkbox" name="ab"%s(oab) />%s(B('b'))Attachments | |
| 600 | - @ </label><br /> | |
| 601 | - @ </td><td><td width="40"></td><td valign="top"> | |
| 602 | - @ <label><input type="checkbox" name="ar"%s(oar) />%s(B('r'))Read Ticket | |
| 603 | - @ </label><br /> | |
| 604 | - @ <label><input type="checkbox" name="an"%s(oan) />%s(B('n'))New Ticket | |
| 605 | - @ </label><br /> | |
| 606 | - @ <label><input type="checkbox" name="ac"%s(oac) />%s(B('c'))Append Ticket | |
| 607 | - @ </label><br /> | |
| 608 | - @ <label><input type="checkbox" name="aw"%s(oaw) />%s(B('w'))Write Ticket | |
| 609 | - @ </label><br /> | |
| 610 | - @ <label><input type="checkbox" name="at"%s(oat) />%s(B('t'))Ticket Report | |
| 611 | - @ </label><br /> | |
| 612 | - @ <label><input type="checkbox" name="ax"%s(oax) />%s(B('x'))Private | |
| 613 | - @ </label><br /> | |
| 614 | - @ <label><input type="checkbox" name="az"%s(oaz) />%s(B('z'))Download Zip | |
| 615 | - @ </label> | |
| 511 | + @ <label><input type="checkbox" name="as"%s(oa['s']) />%s(B('s'))Setup | |
| 512 | + @ </label><br /> | |
| 513 | + } | |
| 514 | + @ <label><input type="checkbox" name="aa"%s(oa['a']) />%s(B('a'))Admin | |
| 515 | + @ </label><br /> | |
| 516 | + @ <label><input type="checkbox" name="ad"%s(oa['d']) />%s(B('d'))Delete | |
| 517 | + @ </label><br /> | |
| 518 | + @ <label><input type="checkbox" name="ae"%s(oa['e']) />%s(B('e'))Email | |
| 519 | + @ </label><br /> | |
| 520 | + @ <label><input type="checkbox" name="ap"%s(oa['p']) />%s(B('p'))Password | |
| 521 | + @ </label><br /> | |
| 522 | + @ <label><input type="checkbox" name="ai"%s(oa['i']) />%s(B('i'))Check-In | |
| 523 | + @ </label><br /> | |
| 524 | + @ <label><input type="checkbox" name="ao"%s(oa['o']) />%s(B('o'))Check-Out | |
| 525 | + @ </label><br /> | |
| 526 | + @ <label><input type="checkbox" name="ah"%s(oa['h']) />%s(B('h'))Hyperlinks | |
| 527 | + @ </label><br /> | |
| 528 | + @ <label><input type="checkbox" name="ab"%s(oa['b']) />%s(B('b'))Attachments | |
| 529 | + @ </label><br /> | |
| 530 | + @ </td><td><td width="40"></td><td valign="top"> | |
| 531 | + @ <label><input type="checkbox" name="au"%s(oa['u']) />%s(B('u'))Reader | |
| 532 | + @ </label><br /> | |
| 533 | + @ <label><input type="checkbox" name="av"%s(oa['v']) />%s(B('v'))Developer | |
| 534 | + @ </label><br /> | |
| 535 | + @ <label><input type="checkbox" name="ag"%s(oa['g']) />%s(B('g'))Clone | |
| 536 | + @ </label><br /> | |
| 537 | + @ <label><input type="checkbox" name="aj"%s(oa['j']) />%s(B('j'))Read Wiki | |
| 538 | + @ </label><br /> | |
| 539 | + @ <label><input type="checkbox" name="af"%s(oa['f']) />%s(B('f'))New Wiki | |
| 540 | + @ </label><br /> | |
| 541 | + @ <label><input type="checkbox" name="am"%s(oa['m']) />%s(B('m'))Append Wiki | |
| 542 | + @ </label><br /> | |
| 543 | + @ <label><input type="checkbox" name="ak"%s(oa['k']) />%s(B('k'))Write Wiki | |
| 544 | + @ </label><br /> | |
| 545 | + @ <label><input type="checkbox" name="al"%s(oa['l']) />%s(B('l'))Moderate | |
| 546 | + @ Wiki</label><br /> | |
| 547 | + @ </td><td><td width="40"></td><td valign="top"> | |
| 548 | + @ <label><input type="checkbox" name="ar"%s(oa['r']) />%s(B('r'))Read Ticket | |
| 549 | + @ </label><br /> | |
| 550 | + @ <label><input type="checkbox" name="an"%s(oa['n']) />%s(B('n'))New Tickets | |
| 551 | + @ </label><br /> | |
| 552 | + @ <label><input type="checkbox" name="ac"%s(oa['c']) />%s(B('c'))Append | |
| 553 | + @ To Ticket </label><br /> | |
| 554 | + @ <label><input type="checkbox" name="aw"%s(oa['w']) />%s(B('w'))Write | |
| 555 | + @ Tickets </label><br /> | |
| 556 | + @ <label><input type="checkbox" name="aq"%s(oa['q']) />%s(B('q'))Moderate | |
| 557 | + @ Tickets </label><br /> | |
| 558 | + @ <label><input type="checkbox" name="at"%s(oa['t']) />%s(B('t'))Ticket | |
| 559 | + @ Report </label><br /> | |
| 560 | + @ <label><input type="checkbox" name="ax"%s(oa['x']) />%s(B('x'))Private | |
| 561 | + @ </label><br /> | |
| 562 | + @ <label><input type="checkbox" name="az"%s(oa['z']) />%s(B('z'))Download | |
| 563 | + @ Zip </label> | |
| 616 | 564 | @ </td></tr></table> |
| 617 | 565 | @ </td> |
| 618 | 566 | @ </tr> |
| 619 | 567 | @ <tr> |
| 620 | 568 | @ <td align="right">Password:</td> |
| 621 | 569 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -215,18 +215,22 @@ | |
| 215 | @ <td><i>Check-In:</i> Commit new versions in the repository</td></tr> |
| 216 | @ <tr><td valign="top"><b>j</b></td> |
| 217 | @ <td><i>Read-Wiki:</i> View wiki pages</td></tr> |
| 218 | @ <tr><td valign="top"><b>k</b></td> |
| 219 | @ <td><i>Write-Wiki:</i> Edit wiki pages</td></tr> |
| 220 | @ <tr><td valign="top"><b>m</b></td> |
| 221 | @ <td><i>Append-Wiki:</i> Append to wiki pages</td></tr> |
| 222 | @ <tr><td valign="top"><b>n</b></td> |
| 223 | @ <td><i>New-Tkt:</i> Create new tickets</td></tr> |
| 224 | @ <tr><td valign="top"><b>o</b></td> |
| 225 | @ <td><i>Check-Out:</i> Check out versions</td></tr> |
| 226 | @ <tr><td valign="top"><b>p</b></td> |
| 227 | @ <td><i>Password:</i> Change your own password</td></tr> |
| 228 | @ <tr><td valign="top"><b>r</b></td> |
| 229 | @ <td><i>Read-Tkt:</i> View tickets</td></tr> |
| 230 | @ <tr><td valign="top"><b>s</b></td> |
| 231 | @ <td><i>Setup/Super-user:</i> Setup and configure this website</td></tr> |
| 232 | @ <tr><td valign="top"><b>t</b></td> |
| @@ -292,20 +296,19 @@ | |
| 292 | /* |
| 293 | ** WEBPAGE: /setup_uedit |
| 294 | */ |
| 295 | void user_edit(void){ |
| 296 | const char *zId, *zLogin, *zInfo, *zCap, *zPw; |
| 297 | char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap; |
| 298 | char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae; |
| 299 | char *oat, *oau, *oav, *oab, *oax, *oaz; |
| 300 | const char *zGroup; |
| 301 | const char *zOldLogin; |
| 302 | char *inherit[128]; |
| 303 | int doWrite; |
| 304 | int uid; |
| 305 | int higherUser = 0; /* True if user being edited is SETUP and the */ |
| 306 | /* user doing the editing is ADMIN. Disallow editing */ |
| 307 | |
| 308 | /* Must have ADMIN privleges to access this page |
| 309 | */ |
| 310 | login_check_credentials(); |
| 311 | if( !g.perm.Admin ){ login_needed(); return; } |
| @@ -330,58 +333,19 @@ | |
| 330 | ** modified user record. After writing the user record, redirect |
| 331 | ** to the page that displays a list of users. |
| 332 | */ |
| 333 | doWrite = cgi_all("login","info","pw") && !higherUser; |
| 334 | if( doWrite ){ |
| 335 | char zCap[50]; |
| 336 | int i = 0; |
| 337 | int aa = P("aa")!=0; |
| 338 | int ab = P("ab")!=0; |
| 339 | int ad = P("ad")!=0; |
| 340 | int ae = P("ae")!=0; |
| 341 | int ai = P("ai")!=0; |
| 342 | int aj = P("aj")!=0; |
| 343 | int ak = P("ak")!=0; |
| 344 | int an = P("an")!=0; |
| 345 | int ao = P("ao")!=0; |
| 346 | int ap = P("ap")!=0; |
| 347 | int ar = P("ar")!=0; |
| 348 | int as = g.perm.Setup && P("as")!=0; |
| 349 | int aw = P("aw")!=0; |
| 350 | int ac = P("ac")!=0; |
| 351 | int af = P("af")!=0; |
| 352 | int am = P("am")!=0; |
| 353 | int ah = P("ah")!=0; |
| 354 | int ag = P("ag")!=0; |
| 355 | int at = P("at")!=0; |
| 356 | int au = P("au")!=0; |
| 357 | int av = P("av")!=0; |
| 358 | int ax = P("ax")!=0; |
| 359 | int az = P("az")!=0; |
| 360 | if( aa ){ zCap[i++] = 'a'; } |
| 361 | if( ab ){ zCap[i++] = 'b'; } |
| 362 | if( ac ){ zCap[i++] = 'c'; } |
| 363 | if( ad ){ zCap[i++] = 'd'; } |
| 364 | if( ae ){ zCap[i++] = 'e'; } |
| 365 | if( af ){ zCap[i++] = 'f'; } |
| 366 | if( ah ){ zCap[i++] = 'h'; } |
| 367 | if( ag ){ zCap[i++] = 'g'; } |
| 368 | if( ai ){ zCap[i++] = 'i'; } |
| 369 | if( aj ){ zCap[i++] = 'j'; } |
| 370 | if( ak ){ zCap[i++] = 'k'; } |
| 371 | if( am ){ zCap[i++] = 'm'; } |
| 372 | if( an ){ zCap[i++] = 'n'; } |
| 373 | if( ao ){ zCap[i++] = 'o'; } |
| 374 | if( ap ){ zCap[i++] = 'p'; } |
| 375 | if( ar ){ zCap[i++] = 'r'; } |
| 376 | if( as ){ zCap[i++] = 's'; } |
| 377 | if( at ){ zCap[i++] = 't'; } |
| 378 | if( au ){ zCap[i++] = 'u'; } |
| 379 | if( av ){ zCap[i++] = 'v'; } |
| 380 | if( aw ){ zCap[i++] = 'w'; } |
| 381 | if( ax ){ zCap[i++] = 'x'; } |
| 382 | if( az ){ zCap[i++] = 'z'; } |
| 383 | |
| 384 | zCap[i] = 0; |
| 385 | zPw = P("pw"); |
| 386 | zLogin = P("login"); |
| 387 | if( strlen(zLogin)==0 ){ |
| @@ -457,40 +421,20 @@ | |
| 457 | */ |
| 458 | zLogin = ""; |
| 459 | zInfo = ""; |
| 460 | zCap = ""; |
| 461 | zPw = ""; |
| 462 | oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam = |
| 463 | oan = oao = oap = oar = oas = oat = oau = oav = oaw = oax = oaz = ""; |
| 464 | if( uid ){ |
| 465 | zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); |
| 466 | zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); |
| 467 | zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); |
| 468 | zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); |
| 469 | if( strchr(zCap, 'a') ) oaa = " checked=\"checked\""; |
| 470 | if( strchr(zCap, 'b') ) oab = " checked=\"checked\""; |
| 471 | if( strchr(zCap, 'c') ) oac = " checked=\"checked\""; |
| 472 | if( strchr(zCap, 'd') ) oad = " checked=\"checked\""; |
| 473 | if( strchr(zCap, 'e') ) oae = " checked=\"checked\""; |
| 474 | if( strchr(zCap, 'f') ) oaf = " checked=\"checked\""; |
| 475 | if( strchr(zCap, 'g') ) oag = " checked=\"checked\""; |
| 476 | if( strchr(zCap, 'h') ) oah = " checked=\"checked\""; |
| 477 | if( strchr(zCap, 'i') ) oai = " checked=\"checked\""; |
| 478 | if( strchr(zCap, 'j') ) oaj = " checked=\"checked\""; |
| 479 | if( strchr(zCap, 'k') ) oak = " checked=\"checked\""; |
| 480 | if( strchr(zCap, 'm') ) oam = " checked=\"checked\""; |
| 481 | if( strchr(zCap, 'n') ) oan = " checked=\"checked\""; |
| 482 | if( strchr(zCap, 'o') ) oao = " checked=\"checked\""; |
| 483 | if( strchr(zCap, 'p') ) oap = " checked=\"checked\""; |
| 484 | if( strchr(zCap, 'r') ) oar = " checked=\"checked\""; |
| 485 | if( strchr(zCap, 's') ) oas = " checked=\"checked\""; |
| 486 | if( strchr(zCap, 't') ) oat = " checked=\"checked\""; |
| 487 | if( strchr(zCap, 'u') ) oau = " checked=\"checked\""; |
| 488 | if( strchr(zCap, 'v') ) oav = " checked=\"checked\""; |
| 489 | if( strchr(zCap, 'w') ) oaw = " checked=\"checked\""; |
| 490 | if( strchr(zCap, 'x') ) oax = " checked=\"checked\""; |
| 491 | if( strchr(zCap, 'z') ) oaz = " checked=\"checked\""; |
| 492 | } |
| 493 | |
| 494 | /* figure out inherited permissions */ |
| 495 | memset(inherit, 0, sizeof(inherit)); |
| 496 | if( fossil_strcmp(zLogin, "developer") ){ |
| @@ -562,59 +506,63 @@ | |
| 562 | @ <td class="usetupEditLabel">Capabilities:</td> |
| 563 | @ <td> |
| 564 | #define B(x) inherit[x] |
| 565 | @ <table border=0><tr><td valign="top"> |
| 566 | if( g.perm.Setup ){ |
| 567 | @ <label><input type="checkbox" name="as"%s(oas) />%s(B('s'))Setup |
| 568 | @ </label><br /> |
| 569 | } |
| 570 | @ <label><input type="checkbox" name="aa"%s(oaa) />%s(B('a'))Admin |
| 571 | @ </label><br /> |
| 572 | @ <label><input type="checkbox" name="ad"%s(oad) />%s(B('d'))Delete |
| 573 | @ </label><br /> |
| 574 | @ <label><input type="checkbox" name="ae"%s(oae) />%s(B('e'))Email |
| 575 | @ </label><br /> |
| 576 | @ <label><input type="checkbox" name="ap"%s(oap) />%s(B('p'))Password |
| 577 | @ </label><br /> |
| 578 | @ <label><input type="checkbox" name="ai"%s(oai) />%s(B('i'))Check-In |
| 579 | @ </label><br /> |
| 580 | @ <label><input type="checkbox" name="ao"%s(oao) />%s(B('o'))Check-Out |
| 581 | @ </label><br /> |
| 582 | @ <label><input type="checkbox" name="ah"%s(oah) />%s(B('h'))Hyperlinks |
| 583 | @ </label><br /> |
| 584 | @ </td><td><td width="40"></td><td valign="top"> |
| 585 | @ <label><input type="checkbox" name="au"%s(oau) />%s(B('u'))Reader |
| 586 | @ </label><br /> |
| 587 | @ <label><input type="checkbox" name="av"%s(oav) />%s(B('v'))Developer |
| 588 | @ </label><br /> |
| 589 | @ <label><input type="checkbox" name="ag"%s(oag) />%s(B('g'))Clone |
| 590 | @ </label><br /> |
| 591 | @ <label><input type="checkbox" name="aj"%s(oaj) />%s(B('j'))Read Wiki |
| 592 | @ </label><br /> |
| 593 | @ <label><input type="checkbox" name="af"%s(oaf) />%s(B('f'))New Wiki |
| 594 | @ </label><br /> |
| 595 | @ <label><input type="checkbox" name="am"%s(oam) />%s(B('m'))Append Wiki |
| 596 | @ </label><br /> |
| 597 | @ <label><input type="checkbox" name="ak"%s(oak) />%s(B('k'))Write Wiki |
| 598 | @ </label><br /> |
| 599 | @ <label><input type="checkbox" name="ab"%s(oab) />%s(B('b'))Attachments |
| 600 | @ </label><br /> |
| 601 | @ </td><td><td width="40"></td><td valign="top"> |
| 602 | @ <label><input type="checkbox" name="ar"%s(oar) />%s(B('r'))Read Ticket |
| 603 | @ </label><br /> |
| 604 | @ <label><input type="checkbox" name="an"%s(oan) />%s(B('n'))New Ticket |
| 605 | @ </label><br /> |
| 606 | @ <label><input type="checkbox" name="ac"%s(oac) />%s(B('c'))Append Ticket |
| 607 | @ </label><br /> |
| 608 | @ <label><input type="checkbox" name="aw"%s(oaw) />%s(B('w'))Write Ticket |
| 609 | @ </label><br /> |
| 610 | @ <label><input type="checkbox" name="at"%s(oat) />%s(B('t'))Ticket Report |
| 611 | @ </label><br /> |
| 612 | @ <label><input type="checkbox" name="ax"%s(oax) />%s(B('x'))Private |
| 613 | @ </label><br /> |
| 614 | @ <label><input type="checkbox" name="az"%s(oaz) />%s(B('z'))Download Zip |
| 615 | @ </label> |
| 616 | @ </td></tr></table> |
| 617 | @ </td> |
| 618 | @ </tr> |
| 619 | @ <tr> |
| 620 | @ <td align="right">Password:</td> |
| 621 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -215,18 +215,22 @@ | |
| 215 | @ <td><i>Check-In:</i> Commit new versions in the repository</td></tr> |
| 216 | @ <tr><td valign="top"><b>j</b></td> |
| 217 | @ <td><i>Read-Wiki:</i> View wiki pages</td></tr> |
| 218 | @ <tr><td valign="top"><b>k</b></td> |
| 219 | @ <td><i>Write-Wiki:</i> Edit wiki pages</td></tr> |
| 220 | @ <tr><td valign="top"><b>l</b></td> |
| 221 | @ <td><i>Mod-Wiki:</i> Moderator for wiki pages</td></tr> |
| 222 | @ <tr><td valign="top"><b>m</b></td> |
| 223 | @ <td><i>Append-Wiki:</i> Append to wiki pages</td></tr> |
| 224 | @ <tr><td valign="top"><b>n</b></td> |
| 225 | @ <td><i>New-Tkt:</i> Create new tickets</td></tr> |
| 226 | @ <tr><td valign="top"><b>o</b></td> |
| 227 | @ <td><i>Check-Out:</i> Check out versions</td></tr> |
| 228 | @ <tr><td valign="top"><b>p</b></td> |
| 229 | @ <td><i>Password:</i> Change your own password</td></tr> |
| 230 | @ <tr><td valign="top"><b>q</b></td> |
| 231 | @ <td><i>Mod-Tkt:</i> Moderator for tickets</td></tr> |
| 232 | @ <tr><td valign="top"><b>r</b></td> |
| 233 | @ <td><i>Read-Tkt:</i> View tickets</td></tr> |
| 234 | @ <tr><td valign="top"><b>s</b></td> |
| 235 | @ <td><i>Setup/Super-user:</i> Setup and configure this website</td></tr> |
| 236 | @ <tr><td valign="top"><b>t</b></td> |
| @@ -292,20 +296,19 @@ | |
| 296 | /* |
| 297 | ** WEBPAGE: /setup_uedit |
| 298 | */ |
| 299 | void user_edit(void){ |
| 300 | const char *zId, *zLogin, *zInfo, *zCap, *zPw; |
| 301 | const char *zGroup; |
| 302 | const char *zOldLogin; |
| 303 | int doWrite; |
| 304 | int uid, i; |
| 305 | int higherUser = 0; /* True if user being edited is SETUP and the */ |
| 306 | /* user doing the editing is ADMIN. Disallow editing */ |
| 307 | char *inherit[128]; |
| 308 | int a[128]; |
| 309 | char *oa[128]; |
| 310 | |
| 311 | /* Must have ADMIN privleges to access this page |
| 312 | */ |
| 313 | login_check_credentials(); |
| 314 | if( !g.perm.Admin ){ login_needed(); return; } |
| @@ -330,58 +333,19 @@ | |
| 333 | ** modified user record. After writing the user record, redirect |
| 334 | ** to the page that displays a list of users. |
| 335 | */ |
| 336 | doWrite = cgi_all("login","info","pw") && !higherUser; |
| 337 | if( doWrite ){ |
| 338 | char c; |
| 339 | char zCap[50], zNm[4]; |
| 340 | zNm[0] = 'a'; |
| 341 | zNm[2] = 0; |
| 342 | for(i=0, c='a'; c<='z'; c++){ |
| 343 | zNm[1] = c; |
| 344 | a[c] = (c!='s' || g.perm.Setup) && P(zNm)!=0; |
| 345 | if( a[c] ) zCap[i++] = c; |
| 346 | } |
| 347 | |
| 348 | zCap[i] = 0; |
| 349 | zPw = P("pw"); |
| 350 | zLogin = P("login"); |
| 351 | if( strlen(zLogin)==0 ){ |
| @@ -457,40 +421,20 @@ | |
| 421 | */ |
| 422 | zLogin = ""; |
| 423 | zInfo = ""; |
| 424 | zCap = ""; |
| 425 | zPw = ""; |
| 426 | for(i='a'; i<='z'; i++) oa[i] = ""; |
| 427 | if( uid ){ |
| 428 | zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); |
| 429 | zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); |
| 430 | zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); |
| 431 | zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); |
| 432 | for(i=0; zCap[i]; i++){ |
| 433 | char c = zCap[i]; |
| 434 | if( c>='a' && c<='z' ) oa[c] = " checked=\"checked\""; |
| 435 | } |
| 436 | } |
| 437 | |
| 438 | /* figure out inherited permissions */ |
| 439 | memset(inherit, 0, sizeof(inherit)); |
| 440 | if( fossil_strcmp(zLogin, "developer") ){ |
| @@ -562,59 +506,63 @@ | |
| 506 | @ <td class="usetupEditLabel">Capabilities:</td> |
| 507 | @ <td> |
| 508 | #define B(x) inherit[x] |
| 509 | @ <table border=0><tr><td valign="top"> |
| 510 | if( g.perm.Setup ){ |
| 511 | @ <label><input type="checkbox" name="as"%s(oa['s']) />%s(B('s'))Setup |
| 512 | @ </label><br /> |
| 513 | } |
| 514 | @ <label><input type="checkbox" name="aa"%s(oa['a']) />%s(B('a'))Admin |
| 515 | @ </label><br /> |
| 516 | @ <label><input type="checkbox" name="ad"%s(oa['d']) />%s(B('d'))Delete |
| 517 | @ </label><br /> |
| 518 | @ <label><input type="checkbox" name="ae"%s(oa['e']) />%s(B('e'))Email |
| 519 | @ </label><br /> |
| 520 | @ <label><input type="checkbox" name="ap"%s(oa['p']) />%s(B('p'))Password |
| 521 | @ </label><br /> |
| 522 | @ <label><input type="checkbox" name="ai"%s(oa['i']) />%s(B('i'))Check-In |
| 523 | @ </label><br /> |
| 524 | @ <label><input type="checkbox" name="ao"%s(oa['o']) />%s(B('o'))Check-Out |
| 525 | @ </label><br /> |
| 526 | @ <label><input type="checkbox" name="ah"%s(oa['h']) />%s(B('h'))Hyperlinks |
| 527 | @ </label><br /> |
| 528 | @ <label><input type="checkbox" name="ab"%s(oa['b']) />%s(B('b'))Attachments |
| 529 | @ </label><br /> |
| 530 | @ </td><td><td width="40"></td><td valign="top"> |
| 531 | @ <label><input type="checkbox" name="au"%s(oa['u']) />%s(B('u'))Reader |
| 532 | @ </label><br /> |
| 533 | @ <label><input type="checkbox" name="av"%s(oa['v']) />%s(B('v'))Developer |
| 534 | @ </label><br /> |
| 535 | @ <label><input type="checkbox" name="ag"%s(oa['g']) />%s(B('g'))Clone |
| 536 | @ </label><br /> |
| 537 | @ <label><input type="checkbox" name="aj"%s(oa['j']) />%s(B('j'))Read Wiki |
| 538 | @ </label><br /> |
| 539 | @ <label><input type="checkbox" name="af"%s(oa['f']) />%s(B('f'))New Wiki |
| 540 | @ </label><br /> |
| 541 | @ <label><input type="checkbox" name="am"%s(oa['m']) />%s(B('m'))Append Wiki |
| 542 | @ </label><br /> |
| 543 | @ <label><input type="checkbox" name="ak"%s(oa['k']) />%s(B('k'))Write Wiki |
| 544 | @ </label><br /> |
| 545 | @ <label><input type="checkbox" name="al"%s(oa['l']) />%s(B('l'))Moderate |
| 546 | @ Wiki</label><br /> |
| 547 | @ </td><td><td width="40"></td><td valign="top"> |
| 548 | @ <label><input type="checkbox" name="ar"%s(oa['r']) />%s(B('r'))Read Ticket |
| 549 | @ </label><br /> |
| 550 | @ <label><input type="checkbox" name="an"%s(oa['n']) />%s(B('n'))New Tickets |
| 551 | @ </label><br /> |
| 552 | @ <label><input type="checkbox" name="ac"%s(oa['c']) />%s(B('c'))Append |
| 553 | @ To Ticket </label><br /> |
| 554 | @ <label><input type="checkbox" name="aw"%s(oa['w']) />%s(B('w'))Write |
| 555 | @ Tickets </label><br /> |
| 556 | @ <label><input type="checkbox" name="aq"%s(oa['q']) />%s(B('q'))Moderate |
| 557 | @ Tickets </label><br /> |
| 558 | @ <label><input type="checkbox" name="at"%s(oa['t']) />%s(B('t'))Ticket |
| 559 | @ Report </label><br /> |
| 560 | @ <label><input type="checkbox" name="ax"%s(oa['x']) />%s(B('x'))Private |
| 561 | @ </label><br /> |
| 562 | @ <label><input type="checkbox" name="az"%s(oa['z']) />%s(B('z'))Download |
| 563 | @ Zip </label> |
| 564 | @ </td></tr></table> |
| 565 | @ </td> |
| 566 | @ </tr> |
| 567 | @ <tr> |
| 568 | @ <td align="right">Password:</td> |
| 569 |
+4
-1
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -362,11 +362,14 @@ | ||
| 362 | 362 | @ %h(zFile) |
| 363 | 363 | } |
| 364 | 364 | @ added by %h(zUser) on |
| 365 | 365 | hyperlink_to_date(zDate, "."); |
| 366 | 366 | if( g.perm.WrTkt && g.perm.Attach ){ |
| 367 | - @ [%z(href("%R/attachdelete?tkt=%s&file=%t&from=%R/tktview%%3fname=%s",zFullName,zFile,zFullName))delete</a>] | |
| 367 | + char *zH; | |
| 368 | + zH = href("%R/attachdelete?tkt=%s&file=%t&from=%R/tktview%%3fname=%s", | |
| 369 | + zFullName, zFile, zFullName); | |
| 370 | + @ [%z(zH)delete</a>] | |
| 368 | 371 | } |
| 369 | 372 | @ </li> |
| 370 | 373 | } |
| 371 | 374 | if( cnt ){ |
| 372 | 375 | @ </ul> |
| 373 | 376 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -362,11 +362,14 @@ | |
| 362 | @ %h(zFile) |
| 363 | } |
| 364 | @ added by %h(zUser) on |
| 365 | hyperlink_to_date(zDate, "."); |
| 366 | if( g.perm.WrTkt && g.perm.Attach ){ |
| 367 | @ [%z(href("%R/attachdelete?tkt=%s&file=%t&from=%R/tktview%%3fname=%s",zFullName,zFile,zFullName))delete</a>] |
| 368 | } |
| 369 | @ </li> |
| 370 | } |
| 371 | if( cnt ){ |
| 372 | @ </ul> |
| 373 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -362,11 +362,14 @@ | |
| 362 | @ %h(zFile) |
| 363 | } |
| 364 | @ added by %h(zUser) on |
| 365 | hyperlink_to_date(zDate, "."); |
| 366 | if( g.perm.WrTkt && g.perm.Attach ){ |
| 367 | char *zH; |
| 368 | zH = href("%R/attachdelete?tkt=%s&file=%t&from=%R/tktview%%3fname=%s", |
| 369 | zFullName, zFile, zFullName); |
| 370 | @ [%z(zH)delete</a>] |
| 371 | } |
| 372 | @ </li> |
| 373 | } |
| 374 | if( cnt ){ |
| 375 | @ </ul> |
| 376 |