Fossil SCM

Don't make it too complicated, too much risk to break other things.

jan.nijtmans 2012-10-31 20:49 UTC improve_looks_like_binary merge
Commit de5a2ef66171ff07aa819d53c7e5632b0437619f
+26 -26
--- src/allrepo.c
+++ src/allrepo.c
@@ -50,32 +50,19 @@
5050
/*
5151
** Build a string that contains all of the command-line options
5252
** specified as arguments. If the option name begins with "+" then
5353
** it takes an argument. Without the "+" it does not.
5454
*/
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);
7764
}
7865
}
7966
8067
8168
/*
@@ -119,11 +106,11 @@
119106
Stmt q;
120107
const char *zCmd;
121108
char *zSyscmd;
122109
char *zFossil;
123110
char *zQFilename;
124
- const char *zExtra = "";
111
+ Blob extra;
125112
int useCheckouts = 0;
126113
int quiet = 0;
127114
int testRun = 0;
128115
int stopOnError = find_option("dontstop",0,0)==0;
129116
int rc;
@@ -138,10 +125,11 @@
138125
if( g.argc<3 ){
139126
usage("changes|list|ls|pull|push|rebuild|sync");
140127
}
141128
n = strlen(g.argv[2]);
142129
db_open_config(1);
130
+ blob_zero(&extra);
143131
zCmd = g.argv[2];
144132
if( strncmp(zCmd, "list", n)==0 || strncmp(zCmd,"ls",n)==0 ){
145133
zCmd = "list";
146134
useCheckouts = find_option("ckout","c",0)!=0;
147135
}else if( strncmp(zCmd, "push", n)==0 ){
@@ -148,16 +136,27 @@
148136
zCmd = "push -autourl -R";
149137
}else if( strncmp(zCmd, "pull", n)==0 ){
150138
zCmd = "pull -autourl -R";
151139
}else if( strncmp(zCmd, "rebuild", n)==0 ){
152140
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");
155149
}else if( strncmp(zCmd, "sync", n)==0 ){
156150
zCmd = "sync -autourl -R";
157151
}else if( strncmp(zCmd, "test-integrity", n)==0 ){
158152
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");
159158
}else if( strncmp(zCmd, "changes", n)==0 ){
160159
zCmd = "changes --quiet --header --chdir";
161160
useCheckouts = 1;
162161
stopOnError = 0;
163162
quiet = 1;
@@ -213,11 +212,12 @@
213212
if( zCmd[0]=='l' ){
214213
fossil_print("%s\n", zFilename);
215214
continue;
216215
}
217216
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));
219219
if( !quiet || testRun ){
220220
fossil_print("%s\n", zSyscmd);
221221
fflush(stdout);
222222
}
223223
rc = testRun ? 0 : fossil_system(zSyscmd);
224224
--- 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 @@
243243
int addCompress = 0;
244244
Manifest *pManifest;
245245
246246
db_begin_transaction();
247247
blob_init(&content, aContent, szContent);
248
- pManifest = manifest_parse(&content, 0);
248
+ pManifest = manifest_parse(&content, 0, 0);
249249
manifest_destroy(pManifest);
250250
blob_init(&content, aContent, szContent);
251251
if( pManifest ){
252252
blob_compress(&content, &content);
253253
addCompress = 1;
254254
--- 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 @@
659659
660660
/*
661661
** Do printf-style string rendering and append the results to a blob.
662662
*/
663663
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
+ }
668670
}
669671
void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){
670
- vxprintf(pBlob, zFormat, ap);
672
+ if( pBlob ) vxprintf(pBlob, zFormat, ap);
671673
}
672674
673675
/*
674676
** Initalize a blob to the data on an input channel. Return
675677
** the number of bytes read into the blob. Any prior content
676678
--- 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 @@
900900
Blob ans;
901901
char cReply;
902902
903903
if( eType&1 ){
904904
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. */
906906
}
907
- zWarning = "CR/NL line endings";
907
+ zWarning = "CR/NL line endings";
908908
}else{
909
- zWarning = "Unicode";
909
+ zWarning = "Unicode";
910910
}
911911
file_relative_name(zFilename, &fname, 0);
912912
blob_zero(&ans);
913913
zMsg = mprintf(
914
- "%s contains %s. commit anyhow (a=all/y/N)? ",
914
+ "%s contains %s. commit anyhow (a=all/y/N)? ",
915915
blob_str(&fname), zWarning );
916916
prompt_user(zMsg, &ans);
917917
fossil_free(zMsg);
918918
cReply = blob_str(&ans)[0];
919919
if( cReply=='a' || cReply=='A' ){
920920
--- 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 @@
926926
cnt++;
927927
}
928928
db_finalize(&q);
929929
fossil_print("%d orphans\n", cnt);
930930
}
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
+}
9311069
--- 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 @@
195195
unsigned char *z = (unsigned char *) blob_buffer(pContent);
196196
unsigned int n = blob_size(pContent);
197197
int j;
198198
unsigned char c;
199199
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
- };
204200
205
-
206
- /* Check individual lines.
201
+ /*
202
+ ** Check individual lines.
207203
*/
208204
if( n==0 ) return result; /* Empty file -> text */
209205
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 */
211207
if ( (n&1)==0 ){ /* UTF-16 must have an even blob length */
212208
if ( (c==0xff) && (z[1]==0xfe) ){ /* UTF-16 LE BOM */
213209
result = -2;
214210
j = LENGTH_MASK/3;
215211
while( (n-=2)>0 ){
216212
c = *(z+=2);
217213
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 */
219215
if( c=='\n' ){
220216
j = LENGTH_MASK/3;
221217
}
222
- }else if( (c+z[1])>0x1fc ){
223
- /* FEFF, FFFE and FFFF are invalid UTF-16 here. */
224
- return 0;
225218
}
226219
if( --j==0 ){
227220
return 0; /* Very long line -> binary */
228221
}
229222
}
@@ -232,17 +225,14 @@
232225
result = -2;
233226
++z; j = LENGTH_MASK/3;
234227
while( (n-=2)>0 ){
235228
c = *(z+=2);
236229
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 */
238231
if( c=='\n' ){
239232
j = LENGTH_MASK/3;
240233
}
241
- }else if( (c+z[-1])>0x1fc ){
242
- /* FEFF, FFFE and FFFF are invalid UTF-16 here. */
243
- return 0;
244234
}
245235
if( --j==0 ){
246236
return 0; /* Very long line -> binary */
247237
}
248238
}
@@ -250,11 +240,11 @@
250240
}
251241
}
252242
j = LENGTH_MASK - (c!='\n');
253243
while( --n>0 ){
254244
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 */
256246
if( c=='\n' ){
257247
if( z[-1]=='\r' ){
258248
result = -1; /* Contains CR/NL, continue */
259249
}
260250
j = LENGTH_MASK;
261251
--- 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 @@
195195
unsigned char *z = (unsigned char *) blob_buffer(pContent);
196196
unsigned int n = blob_size(pContent);
197197
int j;
198198
unsigned char c;
199199
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
- };
204200
205
-
206
- /* Check individual lines.
201
+ /*
202
+ ** Check individual lines.
207203
*/
208204
if( n==0 ) return result; /* Empty file -> text */
209205
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 */
211207
if ( (n&1)==0 ){ /* UTF-16 must have an even blob length */
212208
if ( (c==0xff) && (z[1]==0xfe) ){ /* UTF-16 LE BOM */
213209
result = -2;
214210
j = LENGTH_MASK/3;
215211
while( (n-=2)>0 ){
216212
c = *(z+=2);
217213
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 */
219215
if( c=='\n' ){
220216
j = LENGTH_MASK/3;
221217
}
222
- }else if( (c+z[1])>0x1fc ){
223
- /* FEFF, FFFE and FFFF are invalid UTF-16 here. */
224
- return 0;
225218
}
226219
if( --j==0 ){
227220
return 0; /* Very long line -> binary */
228221
}
229222
}
@@ -232,17 +225,14 @@
232225
result = -2;
233226
++z; j = LENGTH_MASK/3;
234227
while( (n-=2)>0 ){
235228
c = *(z+=2);
236229
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 */
238231
if( c=='\n' ){
239232
j = LENGTH_MASK/3;
240233
}
241
- }else if( (c+z[-1])>0x1fc ){
242
- /* FEFF, FFFE and FFFF are invalid UTF-16 here. */
243
- return 0;
244234
}
245235
if( --j==0 ){
246236
return 0; /* Very long line -> binary */
247237
}
248238
}
@@ -250,11 +240,11 @@
250240
}
251241
}
252242
j = LENGTH_MASK - (c!='\n');
253243
while( --n>0 ){
254244
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 */
256246
if( c=='\n' ){
257247
if( z[-1]=='\r' ){
258248
result = -1; /* Contains CR/NL, continue */
259249
}
260250
j = LENGTH_MASK;
261251
--- 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 @@
3434
*/
3535
const char *mimetype_from_content(Blob *pBlob){
3636
int i;
3737
int n;
3838
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
+ };
3944
4045
/* A table of mimetypes based on file content prefixes
4146
*/
4247
static const struct {
4348
const char *zPrefix; /* The file prefix */
@@ -51,11 +56,17 @@
5156
{ "\377\330\377", 3, "image/jpeg" },
5257
};
5358
5459
x = (const unsigned char*)blob_buffer(pBlob);
5560
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 ){
5768
return 0; /* Plain text */
5869
}
5970
for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){
6071
if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){
6172
return aMime[i].zMimetype;
6273
--- 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 @@
3434
*/
3535
const char *mimetype_from_content(Blob *pBlob){
3636
int i;
3737
int n;
3838
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
+ };
3944
4045
/* A table of mimetypes based on file content prefixes
4146
*/
4247
static const struct {
4348
const char *zPrefix; /* The file prefix */
@@ -51,11 +56,17 @@
5156
{ "\377\330\377", 3, "image/jpeg" },
5257
};
5358
5459
x = (const unsigned char*)blob_buffer(pBlob);
5560
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 ){
5768
return 0; /* Plain text */
5869
}
5970
for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){
6071
if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){
6172
return aMime[i].zMimetype;
6273
--- 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 @@
2222
#include "config.h"
2323
#include "info.h"
2424
#include <assert.h>
2525
2626
/*
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
2929
** record-id rid. If the "propagatingOnly" flag is true, then only
3030
** show branch tags (tags that propagate to children).
3131
**
3232
** Return NULL if there are no such tags.
3333
*/
@@ -62,21 +62,21 @@
6262
char *zTags;
6363
char *zDate;
6464
char *zUuid;
6565
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
6666
if( zUuid ){
67
- zDate = db_text(0,
67
+ zDate = db_text(0,
6868
"SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
6969
rid
7070
);
7171
/* 01234567890123 */
7272
fossil_print("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : "");
7373
free(zUuid);
7474
free(zDate);
7575
}
7676
if( zUuid && showComment ){
77
- zComment = db_text(0,
77
+ zComment = db_text(0,
7878
"SELECT coalesce(ecomment,comment) || "
7979
" ' (user: ' || coalesce(euser,user,'?') || ')' "
8080
" FROM event WHERE objid=%d",
8181
rid
8282
);
@@ -86,11 +86,11 @@
8686
" WHERE cid=%d"
8787
" ORDER BY isprim DESC, mtime DESC /*sort*/", rid);
8888
while( db_step(&q)==SQLITE_ROW ){
8989
const char *zUuid = db_column_text(&q, 0);
9090
const char *zType = db_column_int(&q, 2) ? "parent:" : "merged-from:";
91
- zDate = db_text("",
91
+ zDate = db_text("",
9292
"SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
9393
db_column_int(&q, 1)
9494
);
9595
fossil_print("%-13s %s %s\n", zType, zUuid, zDate);
9696
free(zDate);
@@ -100,11 +100,11 @@
100100
" WHERE pid=%d"
101101
" ORDER BY isprim DESC, mtime DESC /*sort*/", rid);
102102
while( db_step(&q)==SQLITE_ROW ){
103103
const char *zUuid = db_column_text(&q, 0);
104104
const char *zType = db_column_int(&q, 2) ? "child:" : "merged-into:";
105
- zDate = db_text("",
105
+ zDate = db_text("",
106106
"SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
107107
db_column_int(&q, 1)
108108
);
109109
fossil_print("%-13s %s %s\n", zType, zUuid, zDate);
110110
free(zDate);
@@ -289,11 +289,11 @@
289289
290290
291291
/*
292292
** Append the difference between two RIDs to the output
293293
*/
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){
295295
int fromid;
296296
int toid;
297297
Blob from, to, out;
298298
if( zFrom ){
299299
fromid = uuid_to_rid(zFrom, 0);
@@ -319,16 +319,16 @@
319319
@ %s(blob_str(&out))
320320
@ </div>
321321
}
322322
blob_reset(&from);
323323
blob_reset(&to);
324
- blob_reset(&out);
324
+ blob_reset(&out);
325325
}
326326
327327
328328
/*
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
330330
** to a file between two check-ins.
331331
*/
332332
static void append_file_change_line(
333333
const char *zName, /* Name of the file that has changed */
334334
const char *zOld, /* blob.uuid before change. NULL for added files */
@@ -424,11 +424,11 @@
424424
/*
425425
** WEBPAGE: vinfo
426426
** WEBPAGE: ci
427427
** URL: /ci?name=RID|ARTIFACTID
428428
**
429
-** Display information about a particular check-in.
429
+** Display information about a particular check-in.
430430
**
431431
** We also jump here from /info if the name is a version.
432432
**
433433
** If the /ci page is used (instead of /vinfo or /info) then the
434434
** default behavior is to show unified diffs of all file changes.
@@ -462,11 +462,11 @@
462462
"SELECT uuid FROM plink, blob"
463463
" WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
464464
rid
465465
);
466466
isLeaf = is_a_leaf(rid);
467
- db_prepare(&q,
467
+ db_prepare(&q,
468468
"SELECT uuid, datetime(mtime, 'localtime'), user, comment,"
469469
" datetime(omtime, 'localtime'), mtime"
470470
" FROM blob, event"
471471
" WHERE blob.rid=%d"
472472
" AND event.objid=%d",
@@ -482,18 +482,18 @@
482482
const char *zDate;
483483
const char *zOrigDate;
484484
char *zThisBranch;
485485
double thisMtime;
486486
int seenDiffTitle = 0;
487
-
487
+
488488
style_header(zTitle);
489489
login_anonymous_available();
490490
free(zTitle);
491491
zEUser = db_text(0,
492492
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
493493
TAG_USER, rid);
494
- zEComment = db_text(0,
494
+ zEComment = db_text(0,
495495
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
496496
TAG_COMMENT, rid);
497497
zUser = db_column_text(&q, 2);
498498
zComment = db_column_text(&q, 3);
499499
zDate = db_column_text(&q,1);
@@ -526,11 +526,11 @@
526526
@ <tr><th>Original&nbsp;Comment:</th><td>%w(zComment)</td></tr>
527527
}else{
528528
@ <tr><th>Comment:</th><td>%w(zComment)</td></tr>
529529
}
530530
if( g.perm.Admin ){
531
- db_prepare(&q,
531
+ db_prepare(&q,
532532
"SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)"
533533
" FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)"
534534
" WHERE blob.rid=%d",
535535
rid
536536
);
@@ -730,11 +730,11 @@
730730
style_header("Wiki Page Information Error");
731731
@ No such object: %h(g.argv[2])
732732
style_footer();
733733
return;
734734
}
735
- db_prepare(&q,
735
+ db_prepare(&q,
736736
"SELECT substr(tagname, 6, 1000), uuid,"
737737
" datetime(event.mtime, 'localtime'), user"
738738
" FROM tagxref, tag, blob, event"
739739
" WHERE tagxref.rid=%d"
740740
" AND tag.tagid=tagxref.tagid"
@@ -901,11 +901,11 @@
901901
** branch=TAG
902902
** detail=BOOLEAN
903903
** sbs=BOOLEAN
904904
**
905905
**
906
-** Show all differences between two checkins.
906
+** Show all differences between two checkins.
907907
*/
908908
void vdiff_page(void){
909909
int ridFrom, ridTo;
910910
int showDetail = 0;
911911
int sideBySide = 0;
@@ -966,24 +966,24 @@
966966
cmp = -1;
967967
}else{
968968
cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
969969
}
970970
if( cmp<0 ){
971
- append_file_change_line(pFileFrom->zName,
971
+ append_file_change_line(pFileFrom->zName,
972972
pFileFrom->zUuid, 0, 0, diffFlags, 0);
973973
pFileFrom = manifest_file_next(pFrom, 0);
974974
}else if( cmp>0 ){
975
- append_file_change_line(pFileTo->zName,
975
+ append_file_change_line(pFileTo->zName,
976976
0, pFileTo->zUuid, 0, diffFlags,
977977
manifest_file_mperm(pFileTo));
978978
pFileTo = manifest_file_next(pTo, 0);
979979
}else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
980980
/* No changes */
981981
pFileFrom = manifest_file_next(pFrom, 0);
982982
pFileTo = manifest_file_next(pTo, 0);
983983
}else{
984
- append_file_change_line(pFileFrom->zName,
984
+ append_file_change_line(pFileFrom->zName,
985985
pFileFrom->zUuid,
986986
pFileTo->zUuid, 0, diffFlags,
987987
manifest_file_mperm(pFileTo));
988988
pFileFrom = manifest_file_next(pFrom, 0);
989989
pFileTo = manifest_file_next(pTo, 0);
@@ -1054,11 +1054,11 @@
10541054
if( mPerm==PERM_LNK ){
10551055
@ <li>Symbolic link
10561056
}else if( mPerm==PERM_EXE ){
10571057
@ <li>Executable file
10581058
}else{
1059
- @ <li>File
1059
+ @ <li>File
10601060
}
10611061
@ %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
10621062
@ <ul>
10631063
prevName = fossil_strdup(zName);
10641064
}
@@ -1081,16 +1081,16 @@
10811081
}
10821082
}
10831083
@ </ul></ul>
10841084
free(prevName);
10851085
db_finalize(&q);
1086
- db_prepare(&q,
1086
+ db_prepare(&q,
10871087
"SELECT substr(tagname, 6, 10000), datetime(event.mtime),"
10881088
" coalesce(event.euser, event.user)"
10891089
" FROM tagxref, tag, event"
10901090
" WHERE tagxref.rid=%d"
1091
- " AND tag.tagid=tagxref.tagid"
1091
+ " AND tag.tagid=tagxref.tagid"
10921092
" AND tag.tagname LIKE 'wiki-%%'"
10931093
" AND event.objid=tagxref.rid",
10941094
rid
10951095
);
10961096
while( db_step(&q)==SQLITE_ROW ){
@@ -1152,11 +1152,11 @@
11521152
}
11531153
cnt++;
11541154
}
11551155
db_finalize(&q);
11561156
}
1157
- db_prepare(&q,
1157
+ db_prepare(&q,
11581158
"SELECT target, filename, datetime(mtime), user, src"
11591159
" FROM attachment"
11601160
" WHERE src=(SELECT uuid FROM blob WHERE rid=%d)"
11611161
" ORDER BY mtime DESC /*sort*/",
11621162
rid
@@ -1287,11 +1287,11 @@
12871287
}
12881288
12891289
/*
12901290
** WEBPAGE: raw
12911291
** URL: /raw?name=ARTIFACTID&m=TYPE
1292
-**
1292
+**
12931293
** Return the uninterpreted content of an artifact. Used primarily
12941294
** to view artifacts that are images.
12951295
*/
12961296
void rawartifact_page(void){
12971297
int rid;
@@ -1373,11 +1373,11 @@
13731373
}
13741374
13751375
/*
13761376
** WEBPAGE: hexdump
13771377
** URL: /hexdump?name=ARTIFACTID
1378
-**
1378
+**
13791379
** Show the complete content of a file identified by ARTIFACTID
13801380
** as preformatted text.
13811381
*/
13821382
void hexdump_page(void){
13831383
int rid;
@@ -1402,11 +1402,11 @@
14021402
style_header("Hex Artifact Content");
14031403
zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
14041404
@ <h2>Artifact %s(zUuid):</h2>
14051405
blob_zero(&downloadName);
14061406
object_description(rid, 0, &downloadName);
1407
- style_submenu_element("Download", "Download",
1407
+ style_submenu_element("Download", "Download",
14081408
"%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
14091409
@ <hr />
14101410
content_get(rid, &content);
14111411
@ <blockquote><pre>
14121412
hexdump(&content);
@@ -1508,11 +1508,11 @@
15081508
** Additional query parameters:
15091509
**
15101510
** ln - show line numbers
15111511
** ln=N - highlight line number N
15121512
** ln=M-N - highlight lines M through N inclusive
1513
-**
1513
+**
15141514
** Show the complete content of a file identified by ARTIFACTID
15151515
** as preformatted text.
15161516
*/
15171517
void artifact_page(void){
15181518
int rid = 0;
@@ -1545,11 +1545,11 @@
15451545
style_header("Artifact Content");
15461546
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
15471547
@ <h2>Artifact %s(zUuid)</h2>
15481548
blob_zero(&downloadName);
15491549
object_description(rid, 0, &downloadName);
1550
- style_submenu_element("Download", "Download",
1550
+ style_submenu_element("Download", "Download",
15511551
"%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
15521552
zMime = mimetype_from_name(blob_str(&downloadName));
15531553
if( zMime ){
15541554
if( fossil_strcmp(zMime, "text/html")==0 ){
15551555
if( P("txt") ){
@@ -1575,19 +1575,22 @@
15751575
content_get(rid, &content);
15761576
if( renderAsWiki ){
15771577
wiki_convert(&content, 0, 0);
15781578
}else if( renderAsHtml ){
15791579
@ <div>
1580
+ blob_strip_bom(&content, 0);
15801581
cgi_append_content(blob_buffer(&content), blob_size(&content));
15811582
@ </div>
15821583
}else{
15831584
style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
15841585
zMime = mimetype_from_content(&content);
15851586
@ <blockquote>
15861587
if( zMime==0 ){
15871588
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);
15891592
if( zLn ){
15901593
output_text_with_line_numbers(z, zLn);
15911594
}else{
15921595
@ <pre>
15931596
@ %h(z)
@@ -1599,11 +1602,11 @@
15991602
@ <i>(file is %d(blob_size(&content)) bytes of binary data)</i>
16001603
}
16011604
@ </blockquote>
16021605
}
16031606
style_footer();
1604
-}
1607
+}
16051608
16061609
/*
16071610
** WEBPAGE: tinfo
16081611
** URL: /tinfo?name=ARTIFACTID
16091612
**
@@ -1663,20 +1666,20 @@
16631666
/*
16641667
** WEBPAGE: info
16651668
** URL: info/ARTIFACTID
16661669
**
16671670
** 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.
16691672
**
16701673
** Figure out what the artifact ID is and jump to it.
16711674
*/
16721675
void info_page(void){
16731676
const char *zName;
16741677
Blob uuid;
16751678
int rid;
16761679
int rc;
1677
-
1680
+
16781681
zName = P("name");
16791682
if( zName==0 ) fossil_redirect_home();
16801683
if( validate16(zName, strlen(zName)) ){
16811684
if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){
16821685
tktview_page();
@@ -1769,28 +1772,28 @@
17691772
{ "#d3a8bc", 0 },
17701773
{ "#d3b5a8", 0 },
17711774
{ "#d1d3a8", 0 },
17721775
{ "#b1d3a8", 0 },
17731776
1774
- { "#8eb2a1", 0 },
1777
+ { "#8eb2a1", 0 },
17751778
{ "#8ea7b2", 0 },
17761779
{ "#8f8eb2", 0 },
17771780
{ "#ab8eb2", 0 },
17781781
{ "#b28e9e", 0 },
17791782
{ "#b2988e", 0 },
17801783
{ "#b0b28e", 0 },
17811784
{ "#95b28e", 0 },
17821785
1783
- { "#80d6b0", 0 },
1786
+ { "#80d6b0", 0 },
17841787
{ "#80bbd6", 0 },
17851788
{ "#8680d6", 0 },
17861789
{ "#c680d6", 0 },
17871790
{ "#d680a6", 0 },
17881791
{ "#d69b80", 0 },
17891792
{ "#d1d680", 0 },
17901793
{ "#91d680", 0 },
1791
-
1794
+
17921795
17931796
{ "custom", "##" },
17941797
};
17951798
int nColor = sizeof(aColor)/sizeof(aColor[0])-1;
17961799
int stdClrFound = 0;
@@ -1893,11 +1896,11 @@
18931896
const char *zNewComment; /* Revised check-in comment */
18941897
const char *zUser; /* Current user for the check-in */
18951898
const char *zNewUser; /* Revised user */
18961899
const char *zDate; /* Current date of the check-in */
18971900
const char *zNewDate; /* Revised check-in date */
1898
- const char *zColor;
1901
+ const char *zColor;
18991902
const char *zNewColor;
19001903
const char *zNewTagFlag;
19011904
const char *zNewTag;
19021905
const char *zNewBrFlag;
19031906
const char *zNewBranch;
@@ -1906,11 +1909,11 @@
19061909
int fNewPropagateColor; /* True if color propagates after edit */
19071910
const char *zChngTime = 0; /* Value of chngtime= query param, if any */
19081911
char *zUuid;
19091912
Blob comment;
19101913
Stmt q;
1911
-
1914
+
19121915
login_check_credentials();
19131916
if( !g.perm.Write ){ login_needed(); return; }
19141917
rid = name_to_typed_rid(P("r"), "ci");
19151918
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
19161919
zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
@@ -1953,11 +1956,11 @@
19531956
blob_zero(&ctrl);
19541957
zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
19551958
blob_appendf(&ctrl, "D %s\n", zNow);
19561959
db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
19571960
if( zNewColor[0]
1958
- && (fPropagateColor!=fNewPropagateColor
1961
+ && (fPropagateColor!=fNewPropagateColor
19591962
|| fossil_strcmp(zColor,zNewColor)!=0)
19601963
){
19611964
char *zPrefix = "+";
19621965
if( fNewPropagateColor ){
19631966
zPrefix = "*";
19641967
--- 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&nbsp;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&nbsp;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 @@
992992
993993
case 'j': g.perm.RdWiki = 1; break;
994994
case 'k': g.perm.WrWiki = g.perm.RdWiki = g.perm.ApndWiki =1; break;
995995
case 'm': g.perm.ApndWiki = 1; break;
996996
case 'f': g.perm.NewWiki = 1; break;
997
+ case 'l': g.perm.ModWiki = 1; break;
997998
998999
case 'e': g.perm.RdAddr = 1; break;
9991000
case 'r': g.perm.RdTkt = 1; break;
10001001
case 'n': g.perm.NewTkt = 1; break;
10011002
case 'w': g.perm.WrTkt = g.perm.RdTkt = g.perm.NewTkt =
10021003
g.perm.ApndTkt = 1; break;
10031004
case 'c': g.perm.ApndTkt = 1; break;
1005
+ case 'q': g.perm.ModTkt = 1; break;
10041006
case 't': g.perm.TktFmt = 1; break;
10051007
case 'b': g.perm.Attach = 1; break;
10061008
case 'x': g.perm.Private = 1; break;
10071009
10081010
/* The "u" privileges is a little different. It recursively
@@ -1058,15 +1060,16 @@
10581060
case 'g': rc = g.perm.Clone; break;
10591061
case 'h': rc = g.perm.Hyperlink; break;
10601062
case 'i': rc = g.perm.Write; break;
10611063
case 'j': rc = g.perm.RdWiki; break;
10621064
case 'k': rc = g.perm.WrWiki; break;
1065
+ case 'l': rc = g.perm.ModWiki; break;
10631066
case 'm': rc = g.perm.ApndWiki; break;
10641067
case 'n': rc = g.perm.NewTkt; break;
10651068
case 'o': rc = g.perm.Read; break;
10661069
case 'p': rc = g.perm.Password; break;
1067
- /* case 'q': */
1070
+ case 'q': rc = g.perm.ModTkt; break;
10681071
case 'r': rc = g.perm.RdTkt; break;
10691072
case 's': rc = g.perm.Setup; break;
10701073
case 't': rc = g.perm.TktFmt; break;
10711074
/* case 'u': READER */
10721075
/* case 'v': DEVELOPER */
10731076
--- 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 @@
7070
char Clone; /* g: clone */
7171
char RdWiki; /* j: view wiki via web */
7272
char NewWiki; /* f: create new wiki via web */
7373
char ApndWiki; /* m: append to wiki via web */
7474
char WrWiki; /* k: edit wiki via web */
75
+ char ModWiki; /* l: approve and publish wiki content (Moderator) */
7576
char RdTkt; /* r: view tickets via web */
7677
char NewTkt; /* n: create new tickets */
7778
char ApndTkt; /* c: append to tickets via the web */
7879
char WrTkt; /* w: make changes to tickets via web */
80
+ char ModTkt; /* q: approve and publish ticket changes (Moderator) */
7981
char Attach; /* b: add attachments */
8082
char TktFmt; /* t: create new ticket report formats */
8183
char RdAddr; /* e: read email addresses or other private data */
8284
char Zip; /* z: download zipped artifact via /zip URL */
8385
char Private; /* x: can send and receive private content */
8486
--- 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 @@
307307
c = 0;
308308
}
309309
return c;
310310
}
311311
312
+/*
313
+** Shorthand for a control-artifact parsing error
314
+*/
315
+#define SYNTAX(T) {zErr=(T); goto manifest_syntax_error;}
316
+
312317
/*
313318
** Parse a blob into a Manifest object. The Manifest object
314319
** takes over the input blob and will free it when the
315320
** Manifest object is freed. Zeros are inserted into the blob
316321
** as string terminators so that blob should not be used again.
@@ -334,11 +339,11 @@
334339
** Each card is divided into tokens by a single space character.
335340
** The first token is a single upper-case letter which is the card type.
336341
** The card type determines the other parameters to the card.
337342
** Cards must occur in lexicographical order.
338343
*/
339
-Manifest *manifest_parse(Blob *pContent, int rid){
344
+Manifest *manifest_parse(Blob *pContent, int rid, Blob *pErr){
340345
Manifest *p;
341346
int seenZ = 0;
342347
int i, lineNo=0;
343348
ManifestText x;
344349
char cPrevType = 0;
@@ -347,10 +352,11 @@
347352
int n;
348353
char *zUuid;
349354
int sz = 0;
350355
int isRepeat;
351356
static Bag seen;
357
+ const char *zErr = 0;
352358
353359
if( rid==0 ){
354360
isRepeat = 1;
355361
}else if( bag_find(&seen, rid) ){
356362
isRepeat = 1;
@@ -365,27 +371,30 @@
365371
if( !isRepeat ) g.parseCnt[0]++;
366372
z = blob_materialize(pContent);
367373
n = blob_size(pContent);
368374
if( n<=0 || z[n-1]!='\n' ){
369375
blob_reset(pContent);
376
+ blob_appendf(pErr, n ? "not terminated with \\n" : "zero-length");
370377
return 0;
371378
}
372379
373380
/* Strip off the PGP signature if there is one. Then verify the
374381
** Z-card.
375382
*/
376383
remove_pgp_signature(&z, &n);
377
- if( verify_z_card(z, n)==0 ){
384
+ if( verify_z_card(z, n)==2 ){
378385
blob_reset(pContent);
386
+ blob_appendf(pErr, "incorrect Z-card cksum");
379387
return 0;
380388
}
381389
382390
/* Verify that the first few characters of the artifact look like
383391
** a control artifact.
384392
*/
385393
if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){
386394
blob_reset(pContent);
395
+ blob_appendf(pErr, "line 1 not recognized");
387396
return 0;
388397
}
389398
390399
/* Allocate a Manifest object to hold the parsed control artifact.
391400
*/
@@ -420,19 +429,19 @@
420429
zSrc = next_token(&x, &nSrc);
421430
if( zName==0 || zTarget==0 ) goto manifest_syntax_error;
422431
if( p->zAttachName!=0 ) goto manifest_syntax_error;
423432
defossilize(zName);
424433
if( !file_is_simple_pathname(zName) ){
425
- goto manifest_syntax_error;
434
+ SYNTAX("invalid filename on A-card");
426435
}
427436
defossilize(zTarget);
428437
if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
429438
&& !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
430
- goto manifest_syntax_error;
439
+ SYNTAX("invalid target on A-card");
431440
}
432441
if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){
433
- goto manifest_syntax_error;
442
+ SYNTAX("invalid source on A-card");
434443
}
435444
p->zAttachName = (char*)file_tail(zName);
436445
p->zAttachSrc = zSrc;
437446
p->zAttachTarget = zTarget;
438447
break;
@@ -442,15 +451,16 @@
442451
** B <uuid>
443452
**
444453
** A B-line gives the UUID for the baselinen of a delta-manifest.
445454
*/
446455
case 'B': {
447
- if( p->zBaseline ) goto manifest_syntax_error;
456
+ if( p->zBaseline ) SYNTAX("more than one B-card");
448457
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
+ }
452462
break;
453463
}
454464
455465
456466
/*
@@ -459,13 +469,13 @@
459469
** Comment text is fossil-encoded. There may be no more than
460470
** one C line. C lines are required for manifests and are
461471
** disallowed on all other control files.
462472
*/
463473
case 'C': {
464
- if( p->zComment!=0 ) goto manifest_syntax_error;
474
+ if( p->zComment!=0 ) SYNTAX("more than one C-card");
465475
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");
467477
defossilize(p->zComment);
468478
break;
469479
}
470480
471481
/*
@@ -474,13 +484,13 @@
474484
** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS
475485
** There can be no more than 1 D line. D lines are required
476486
** for all control files except for clusters.
477487
*/
478488
case 'D': {
479
- if( p->rDate>0.0 ) goto manifest_syntax_error;
489
+ if( p->rDate>0.0 ) SYNTAX("more than one D-card");
480490
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");
482492
break;
483493
}
484494
485495
/*
486496
** E <timestamp> <uuid>
@@ -490,16 +500,17 @@
490500
** The event timestamp is distinct from the D timestamp. The D
491501
** timestamp is when the artifact was created whereas the E timestamp
492502
** is when the specific event is said to occur.
493503
*/
494504
case 'E': {
495
- if( p->rEventDate>0.0 ) goto manifest_syntax_error;
505
+ if( p->rEventDate>0.0 ) SYNTAX("more than one E-card");
496506
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");
498508
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
+ }
501512
break;
502513
}
503514
504515
/*
505516
** F <filename> ?<uuid>? ?<permissions>? ?<old-name>?
@@ -509,26 +520,26 @@
509520
** other control file. The filename and old-name are fossil-encoded.
510521
*/
511522
case 'F': {
512523
char *zName, *zPerm, *zPriorName;
513524
zName = next_token(&x,0);
514
- if( zName==0 ) goto manifest_syntax_error;
525
+ if( zName==0 ) SYNTAX("missing filename on F-card");
515526
defossilize(zName);
516527
if( !file_is_simple_pathname(zName) ){
517
- goto manifest_syntax_error;
528
+ SYNTAX("F-card filename is not a simple path");
518529
}
519530
zUuid = next_token(&x, &sz);
520531
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");
523534
}
524535
zPerm = next_token(&x,0);
525536
zPriorName = next_token(&x,0);
526537
if( zPriorName ){
527538
defossilize(zPriorName);
528539
if( !file_is_simple_pathname(zPriorName) ){
529
- goto manifest_syntax_error;
540
+ SYNTAX("F-card old filename is not a simple path");
530541
}
531542
}
532543
if( p->nFile>=p->nFileAlloc ){
533544
p->nFileAlloc = p->nFileAlloc*2 + 10;
534545
p->aFile = fossil_realloc(p->aFile,
@@ -538,11 +549,11 @@
538549
p->aFile[i].zName = zName;
539550
p->aFile[i].zUuid = zUuid;
540551
p->aFile[i].zPerm = zPerm;
541552
p->aFile[i].zPrior = zPriorName;
542553
if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){
543
- goto manifest_syntax_error;
554
+ SYNTAX("incorrect F-card sort order");
544555
}
545556
break;
546557
}
547558
548559
/*
@@ -555,11 +566,11 @@
555566
*/
556567
case 'J': {
557568
char *zName, *zValue;
558569
zName = next_token(&x,0);
559570
zValue = next_token(&x,0);
560
- if( zName==0 ) goto manifest_syntax_error;
571
+ if( zName==0 ) SYNTAX("name missing from J-card");
561572
if( zValue==0 ) zValue = "";
562573
defossilize(zValue);
563574
if( p->nField>=p->nFieldAlloc ){
564575
p->nFieldAlloc = p->nFieldAlloc*2 + 10;
565576
p->aField = fossil_realloc(p->aField,
@@ -567,11 +578,11 @@
567578
}
568579
i = p->nField++;
569580
p->aField[i].zName = zName;
570581
p->aField[i].zValue = zValue;
571582
if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){
572
- goto manifest_syntax_error;
583
+ SYNTAX("incorrect J-card sort order");
573584
}
574585
break;
575586
}
576587
577588
@@ -580,14 +591,16 @@
580591
**
581592
** A K-line gives the UUID for the ticket which this control file
582593
** is amending.
583594
*/
584595
case 'K': {
585
- if( p->zTicketUuid!=0 ) goto manifest_syntax_error;
596
+ if( p->zTicketUuid!=0 ) SYNTAX("more than one K-card");
586597
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
+ }
589602
break;
590603
}
591604
592605
/*
593606
** L <wikititle>
@@ -594,16 +607,16 @@
594607
**
595608
** The wiki page title is fossil-encoded. There may be no more than
596609
** one L line.
597610
*/
598611
case 'L': {
599
- if( p->zWikiTitle!=0 ) goto manifest_syntax_error;
612
+ if( p->zWikiTitle!=0 ) SYNTAX("more than one L-card");
600613
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");
602615
defossilize(p->zWikiTitle);
603616
if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){
604
- goto manifest_syntax_error;
617
+ SYNTAX("L-card has malformed wiki name");
605618
}
606619
break;
607620
}
608621
609622
/*
@@ -612,22 +625,22 @@
612625
** An M-line identifies another artifact by its UUID. M-lines
613626
** occur in clusters only.
614627
*/
615628
case 'M': {
616629
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");
620633
if( p->nCChild>=p->nCChildAlloc ){
621634
p->nCChildAlloc = p->nCChildAlloc*2 + 10;
622635
p->azCChild = fossil_realloc(p->azCChild
623636
, p->nCChildAlloc*sizeof(p->azCChild[0]) );
624637
}
625638
i = p->nCChild++;
626639
p->azCChild[i] = zUuid;
627640
if( i>0 && fossil_strcmp(p->azCChild[i-1], zUuid)>=0 ){
628
- goto manifest_syntax_error;
641
+ SYNTAX("M-card in the wrong order");
629642
}
630643
break;
631644
}
632645
633646
/*
@@ -637,12 +650,12 @@
637650
** this artifact. The first parent is the primary parent. All
638651
** others are parents by merge.
639652
*/
640653
case 'P': {
641654
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");
644657
if( p->nParent>=p->nParentAlloc ){
645658
p->nParentAlloc = p->nParentAlloc*2 + 5;
646659
p->azParent = fossil_realloc(p->azParent,
647660
p->nParentAlloc*sizeof(char*));
648661
}
@@ -657,23 +670,29 @@
657670
**
658671
** Specify one or a range of checkins that are cherrypicked into
659672
** this checkin ("+") or backed out of this checkin ("-").
660673
*/
661674
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
+ }
666683
n = p->nCherrypick;
667684
p->nCherrypick++;
668685
p->aCherrypick = fossil_realloc(p->aCherrypick,
669686
p->nCherrypick*sizeof(p->aCherrypick[0]));
670687
p->aCherrypick[n].zCPTarget = zUuid;
671688
p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz);
672689
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
+ }
675694
}
676695
break;
677696
}
678697
679698
/*
@@ -681,14 +700,14 @@
681700
**
682701
** Specify the MD5 checksum over the name and content of all files
683702
** in the manifest.
684703
*/
685704
case 'R': {
686
- if( p->zRepoCksum!=0 ) goto manifest_syntax_error;
705
+ if( p->zRepoCksum!=0 ) SYNTAX("more than on R-card");
687706
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");
690709
break;
691710
}
692711
693712
/*
694713
** T (+|*|-)<tagname> <uuid> ?<value>?
@@ -706,29 +725,29 @@
706725
** Tags are not allowed in clusters. Multiple T lines are allowed.
707726
*/
708727
case 'T': {
709728
char *zName, *zValue;
710729
zName = next_token(&x, 0);
711
- if( zName==0 ) goto manifest_syntax_error;
730
+ if( zName==0 ) SYNTAX("missing name on T-card");
712731
zUuid = next_token(&x, &sz);
713
- if( zUuid==0 ) goto manifest_syntax_error;
732
+ if( zUuid==0 ) SYNTAX("missing UUID on T-card");
714733
zValue = next_token(&x, 0);
715734
if( zValue ) defossilize(zValue);
716735
if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){
717736
/* A valid uuid */
718737
}else if( sz==1 && zUuid[0]=='*' ){
719738
zUuid = 0;
720739
}else{
721
- goto manifest_syntax_error;
740
+ SYNTAX("malformed UUID on T-card");
722741
}
723742
defossilize(zName);
724743
if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){
725
- goto manifest_syntax_error;
744
+ SYNTAX("T-card name does not begin with '-', '+', or '*'");
726745
}
727746
if( validate16(&zName[1], strlen(&zName[1])) ){
728747
/* Do not allow tags whose names look like UUIDs */
729
- goto manifest_syntax_error;
748
+ SYNTAX("T-card name looks like a UUID");
730749
}
731750
if( p->nTag>=p->nTagAlloc ){
732751
p->nTagAlloc = p->nTagAlloc*2 + 10;
733752
p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) );
734753
}
@@ -735,11 +754,11 @@
735754
i = p->nTag++;
736755
p->aTag[i].zName = zName;
737756
p->aTag[i].zUuid = zUuid;
738757
p->aTag[i].zValue = zValue;
739758
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");
741760
}
742761
break;
743762
}
744763
745764
/*
@@ -748,11 +767,11 @@
748767
** Identify the user who created this control file by their
749768
** login. Only one U line is allowed. Prohibited in clusters.
750769
** If the user name is omitted, take that to be "anonymous".
751770
*/
752771
case 'U': {
753
- if( p->zUser!=0 ) goto manifest_syntax_error;
772
+ if( p->zUser!=0 ) SYNTAX("more than on U-card");
754773
p->zUser = next_token(&x, 0);
755774
if( p->zUser==0 ){
756775
p->zUser = "anonymous";
757776
}else{
758777
defossilize(p->zUser);
@@ -767,25 +786,26 @@
767786
** page. There is always an extra \n before the start of the next
768787
** record.
769788
*/
770789
case 'W': {
771790
char *zSize;
772
- int size, c;
791
+ unsigned size, oldsize, c;
773792
Blob wiki;
774793
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;
779800
}
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");
782802
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");
784804
p->zWiki = x.z;
785805
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");
787807
x.z[0] = 0;
788808
x.z++;
789809
break;
790810
}
791811
@@ -801,111 +821,117 @@
801821
** Manifest. It is not required for manifest only for historical
802822
** compatibility reasons.
803823
*/
804824
case 'Z': {
805825
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");
808828
seenZ = 1;
809829
break;
810830
}
811831
default: {
812
- goto manifest_syntax_error;
832
+ SYNTAX("unrecognized card");
813833
}
814834
}
815835
}
816
- if( x.z<x.zEnd ) goto manifest_syntax_error;
836
+ if( x.z<x.zEnd ) SYNTAX("card in the wrong order");
817837
818838
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");
828848
p->type = CFTYPE_MANIFEST;
829849
}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");
842865
p->type = CFTYPE_CLUSTER;
843866
}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");
854877
p->type = CFTYPE_TICKET;
855878
}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");
862885
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");
865888
}
866
- if( !seenZ ) goto manifest_syntax_error;
889
+ if( !seenZ ) SYNTAX("Z-card missing in event");
867890
p->type = CFTYPE_EVENT;
868891
}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");
876899
p->type = CFTYPE_WIKI;
877900
}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");
884907
p->type = CFTYPE_CONTROL;
885908
}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");
891914
p->type = CFTYPE_ATTACHMENT;
892915
}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");
899921
p->type = CFTYPE_MANIFEST;
900922
}
901923
md5sum_init();
902924
if( !isRepeat ) g.parseCnt[p->type]++;
903925
return p;
904926
905927
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
+ }
907933
md5sum_init();
908934
manifest_destroy(p);
909935
return 0;
910936
}
911937
@@ -924,11 +950,11 @@
924950
p = 0;
925951
}
926952
return p;
927953
}
928954
content_get(rid, &content);
929
- p = manifest_parse(&content, rid);
955
+ p = manifest_parse(&content, rid, 0);
930956
if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){
931957
manifest_destroy(p);
932958
p = 0;
933959
}
934960
return p;
@@ -972,13 +998,16 @@
972998
}
973999
blob_read_from_file(&b, g.argv[2]);
9741000
if( g.argc>3 ) n = atoi(g.argv[3]);
9751001
for(i=0; i<n; i++){
9761002
Blob b2;
1003
+ Blob err;
9771004
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);
9801009
manifest_destroy(p);
9811010
}
9821011
}
9831012
9841013
/*
@@ -1299,11 +1328,11 @@
12991328
otherRid = cid;
13001329
}
13011330
if( (*ppOther = manifest_cache_find(otherRid))==0 ){
13021331
content_get(otherRid, &otherContent);
13031332
if( blob_size(&otherContent)==0 ) return;
1304
- *ppOther = manifest_parse(&otherContent, otherRid);
1333
+ *ppOther = manifest_parse(&otherContent, otherRid, 0);
13051334
if( *ppOther==0 ) return;
13061335
}
13071336
if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){
13081337
manifest_destroy(*ppOther);
13091338
return;
@@ -1597,11 +1626,11 @@
15971626
Stmt q;
15981627
int parentid = 0;
15991628
16001629
if( (p = manifest_cache_find(rid))!=0 ){
16011630
blob_reset(pContent);
1602
- }else if( (p = manifest_parse(pContent, rid))==0 ){
1631
+ }else if( (p = manifest_parse(pContent, rid, 0))==0 ){
16031632
assert( blob_is_reset(pContent) || pContent==0 );
16041633
return 0;
16051634
}
16061635
if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){
16071636
manifest_destroy(p);
16081637
--- 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 @@
215215
@ <td><i>Check-In:</i> Commit new versions in the repository</td></tr>
216216
@ <tr><td valign="top"><b>j</b></td>
217217
@ <td><i>Read-Wiki:</i> View wiki pages</td></tr>
218218
@ <tr><td valign="top"><b>k</b></td>
219219
@ <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>
220222
@ <tr><td valign="top"><b>m</b></td>
221223
@ <td><i>Append-Wiki:</i> Append to wiki pages</td></tr>
222224
@ <tr><td valign="top"><b>n</b></td>
223225
@ <td><i>New-Tkt:</i> Create new tickets</td></tr>
224226
@ <tr><td valign="top"><b>o</b></td>
225227
@ <td><i>Check-Out:</i> Check out versions</td></tr>
226228
@ <tr><td valign="top"><b>p</b></td>
227229
@ <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>
228232
@ <tr><td valign="top"><b>r</b></td>
229233
@ <td><i>Read-Tkt:</i> View tickets</td></tr>
230234
@ <tr><td valign="top"><b>s</b></td>
231235
@ <td><i>Setup/Super-user:</i> Setup and configure this website</td></tr>
232236
@ <tr><td valign="top"><b>t</b></td>
@@ -292,20 +296,19 @@
292296
/*
293297
** WEBPAGE: /setup_uedit
294298
*/
295299
void user_edit(void){
296300
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;
300301
const char *zGroup;
301302
const char *zOldLogin;
302
- char *inherit[128];
303303
int doWrite;
304
- int uid;
304
+ int uid, i;
305305
int higherUser = 0; /* True if user being edited is SETUP and the */
306306
/* user doing the editing is ADMIN. Disallow editing */
307
+ char *inherit[128];
308
+ int a[128];
309
+ char *oa[128];
307310
308311
/* Must have ADMIN privleges to access this page
309312
*/
310313
login_check_credentials();
311314
if( !g.perm.Admin ){ login_needed(); return; }
@@ -330,58 +333,19 @@
330333
** modified user record. After writing the user record, redirect
331334
** to the page that displays a list of users.
332335
*/
333336
doWrite = cgi_all("login","info","pw") && !higherUser;
334337
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
+ }
383347
384348
zCap[i] = 0;
385349
zPw = P("pw");
386350
zLogin = P("login");
387351
if( strlen(zLogin)==0 ){
@@ -457,40 +421,20 @@
457421
*/
458422
zLogin = "";
459423
zInfo = "";
460424
zCap = "";
461425
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] = "";
464427
if( uid ){
465428
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
466429
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
467430
zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
468431
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
+ }
492436
}
493437
494438
/* figure out inherited permissions */
495439
memset(inherit, 0, sizeof(inherit));
496440
if( fossil_strcmp(zLogin, "developer") ){
@@ -562,59 +506,63 @@
562506
@ <td class="usetupEditLabel">Capabilities:</td>
563507
@ <td>
564508
#define B(x) inherit[x]
565509
@ <table border=0><tr><td valign="top">
566510
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>
616564
@ </td></tr></table>
617565
@ </td>
618566
@ </tr>
619567
@ <tr>
620568
@ <td align="right">Password:</td>
621569
--- 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 @@
362362
@ %h(zFile)
363363
}
364364
@ added by %h(zUser) on
365365
hyperlink_to_date(zDate, ".");
366366
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>]
368371
}
369372
@ </li>
370373
}
371374
if( cnt ){
372375
@ </ul>
373376
--- 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

Keyboard Shortcuts

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